201
by Jason Conti
Using a bit of cairo to display icons with rounded edges and message items with rounded backgrounds. Still need to get the proper colors from the gtk theme and possibly add options for custom colors. |
1 |
"""
|
2 |
RoundedWidgets.py
|
|
3 |
April 22, 2011
|
|
4 |
||
5 |
Custom widgets that have rounded edges
|
|
6 |
"""
|
|
7 |
||
8 |
import logging |
|
9 |
import math |
|
10 |
||
11 |
from gi.repository import Gdk, GdkPixbuf, GObject, Gtk |
|
12 |
||
203
by Jason Conti
Adding Theme.py in preparation for adding color themes, and updated RoundedBox to use the new Color class. |
13 |
from Theme import Color |
14 |
||
201
by Jason Conti
Using a bit of cairo to display icons with rounded edges and message items with rounded backgrounds. Still need to get the proper colors from the gtk theme and possibly add options for custom colors. |
15 |
logger = logging.getLogger("RoundedWidgets") |
16 |
||
17 |
# Angles in pi/2 increments
|
|
18 |
A0 = 0 |
|
19 |
A1 = math.pi / 2.0 |
|
20 |
A2 = math.pi |
|
21 |
A3 = 3.0 * math.pi / 2.0 |
|
22 |
A4 = 2.0 * math.pi |
|
23 |
||
24 |
def draw_rounded_box(cr, x, y, width, height, radius): |
|
25 |
"""Draws a rounded box to a cairo context, but doesn't fill or stroke it
|
|
26 |
so it can be used as a clipping region as well."""
|
|
27 |
x1 = x |
|
28 |
y1 = y |
|
29 |
w = width |
|
30 |
h = height |
|
31 |
x2 = x1 + w |
|
32 |
y2 = y1 + h |
|
33 |
r = radius |
|
34 |
||
35 |
cr.move_to(x1 + r, y1) |
|
36 |
cr.line_to(x2 - r, y1) |
|
37 |
cr.arc(x2 - r, y1 + r, r, A3, A4) |
|
38 |
cr.line_to(x2, y2 - r) |
|
39 |
cr.arc(x2 - r, y2 - r, r, A0, A1) |
|
40 |
cr.line_to(x1 + r, y2) |
|
41 |
cr.arc(x1 + r, y2 - r, r, A1, A2) |
|
42 |
cr.line_to(x1, y1 + r) |
|
43 |
cr.arc(x1 + r, y1 + r, r, A2, A3) |
|
44 |
||
45 |
class RoundedBox(Gtk.Box): |
|
46 |
"""A simple container that draws a solid background with rounded edges"""
|
|
207
by Jason Conti
Setting most of the colors with a gtk rc style now, except RoundedBox, because I can't figure out how to get the background style color. Seems like it should be widget.style.bg[state] however that list is always empty. |
47 |
BACKGROUND_COLOR = Color("#fff") |
203
by Jason Conti
Adding Theme.py in preparation for adding color themes, and updated RoundedBox to use the new Color class. |
48 |
def __init__(self, radius = 10): |
201
by Jason Conti
Using a bit of cairo to display icons with rounded edges and message items with rounded backgrounds. Still need to get the proper colors from the gtk theme and possibly add options for custom colors. |
49 |
GObject.GObject.__init__(self) |
50 |
||
51 |
self._radius = radius |
|
52 |
||
53 |
self.set_double_buffered(False) |
|
54 |
self.set_redraw_on_allocate(True) |
|
55 |
||
56 |
def set_radius(self, radius): |
|
57 |
"""Sets the radius of the curved edges of this box"""
|
|
58 |
self._radius = radius |
|
59 |
self.queue_draw() |
|
60 |
||
61 |
def get_radius(self): |
|
62 |
"""Gets the radius of the curved edges of this box"""
|
|
63 |
return self._radius |
|
64 |
||
65 |
def do_expose_event(self, event): |
|
66 |
"""Draws the background and propagates the event to the children"""
|
|
67 |
window = self.get_window() |
|
68 |
alloc = self.get_allocation() |
|
69 |
cr = window.cairo_create() |
|
70 |
||
71 |
# Set the clipping region
|
|
72 |
cr.rectangle(event.area.x, event.area.y, event.area.width, |
|
73 |
event.area.height) |
|
74 |
cr.clip() |
|
75 |
||
207
by Jason Conti
Setting most of the colors with a gtk rc style now, except RoundedBox, because I can't figure out how to get the background style color. Seems like it should be widget.style.bg[state] however that list is always empty. |
76 |
#TODO: Fix this hack
|
77 |
color = RoundedBox.BACKGROUND_COLOR |
|
78 |
cr.set_source_rgb(color.red, color.green, color.blue) |
|
201
by Jason Conti
Using a bit of cairo to display icons with rounded edges and message items with rounded backgrounds. Still need to get the proper colors from the gtk theme and possibly add options for custom colors. |
79 |
|
80 |
# Draw the rounded box
|
|
81 |
draw_rounded_box(cr, alloc.x, alloc.y, alloc.width, alloc.height, |
|
82 |
self._radius) |
|
83 |
cr.fill() |
|
84 |
||
85 |
# Propagate the event to the children
|
|
86 |
for child in self.get_children(): |
|
87 |
self.propagate_expose(child, event) |
|
88 |
||
89 |
class RoundedIcon(Gtk.Widget): |
|
90 |
"""Draws a pixbuf with rounded edges"""
|
|
91 |
def __init__(self, pixbuf = None, radius = 10): |
|
92 |
GObject.GObject.__init__(self) |
|
93 |
||
94 |
self._pixbuf = pixbuf |
|
95 |
self._radius = radius |
|
96 |
||
97 |
self.set_has_window(False) |
|
98 |
self.set_double_buffered(False) |
|
99 |
self.set_redraw_on_allocate(True) |
|
100 |
||
101 |
def set_from_pixbuf(self, pixbuf): |
|
102 |
"""Sets the icon from a pixbuf"""
|
|
103 |
self._pixbuf = pixbuf |
|
104 |
||
105 |
def do_expose_event(self, event): |
|
106 |
"""Draws the pixbuf"""
|
|
107 |
pixbuf = self._pixbuf |
|
108 |
if pixbuf == None: |
|
109 |
return
|
|
110 |
||
111 |
window = self.get_window() |
|
112 |
alloc = self.get_allocation() |
|
113 |
cr = window.cairo_create() |
|
114 |
||
115 |
w = pixbuf.get_width() |
|
116 |
h = pixbuf.get_height() |
|
117 |
||
118 |
if alloc.width < w or alloc.height < h: |
|
119 |
self.set_size_request(w, h) |
|
120 |
return
|
|
121 |
||
122 |
cr.translate(alloc.x, alloc.y) |
|
123 |
||
124 |
draw_rounded_box(cr, 0, 0, |
|
125 |
w, h, |
|
126 |
self._radius) |
|
127 |
cr.clip() |
|
128 |
||
129 |
Gdk.cairo_set_source_pixbuf(cr, pixbuf, 0, 0) |
|
130 |
cr.paint() |
|
131 |