1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 from box import EventBox
24 from button import Button
25 from constant import DEFAULT_FONT_SIZE
26 from dialog import DialogBox, DIALOG_MASK_TAB_PAGE
27 from draw import draw_text
28 from scrolled_window import ScrolledWindow
29 from skin_config import skin_config
30 from theme import ui_theme
31 from locales import _
32 import gobject
33 import gtk
34 from utils import (container_remove_all, get_content_size,
35 color_hex_to_cairo, alpha_color_hex_to_cairo,
36 cairo_disable_antialias, is_in_rect, cairo_state,
37 get_window_shadow_size)
38
40 '''
41 Tab box.
42
43 @undocumented: press_tab_title_box
44 @undocumented: expose_tab_title_box
45 @undocumented: expose_tab_content_align
46 @undocumented: expose_tab_content_box
47 '''
48
50 '''
51 Initialize TabBox class.
52 '''
53
54 gtk.VBox.__init__(self)
55 self.tab_height = 29
56 self.tab_padding_x = 19
57 self.tab_padding_y = 9
58 self.tab_select_bg_color = ui_theme.get_color("tab_select_bg")
59 self.tab_select_frame_color = ui_theme.get_color("tab_select_frame")
60 self.tab_unselect_bg_color = ui_theme.get_color("tab_unselect_bg")
61 self.tab_unselect_frame_color = ui_theme.get_color("tab_unselect_bg")
62
63 self.tab_title_box = EventBox()
64 self.tab_title_box.set_size_request(-1, self.tab_height)
65 self.tab_title_align = gtk.Alignment()
66 self.tab_title_align.set(0.0, 0.0, 1.0, 1.0)
67 self.tab_title_align.set_padding(0, 0, 0, 0)
68 self.tab_title_align.add(self.tab_title_box)
69 self.tab_content_align = gtk.Alignment()
70 self.tab_content_align.set(0.0, 0.0, 1.0, 1.0)
71 self.tab_content_align.set_padding(0, 1, 0, 0)
72 self.tab_content_scrolled_window = ScrolledWindow()
73 self.tab_content_align.add(self.tab_content_scrolled_window)
74 self.tab_content_box = gtk.VBox()
75 self.tab_content_scrolled_window.add_child(self.tab_content_box)
76
77 self.tab_items = []
78 self.tab_title_widths = []
79 self.tab_index = -1
80
81 self.pack_start(self.tab_title_align, False, False)
82 self.pack_start(self.tab_content_align, True, True)
83
84 self.tab_title_box.connect("button-press-event", self.press_tab_title_box)
85 self.tab_title_box.connect("expose-event", self.expose_tab_title_box)
86 self.tab_content_align.connect("expose-event", self.expose_tab_content_align)
87 self.tab_content_box.connect("expose-event", self.expose_tab_content_box)
88
90 '''
91 Add items.
92
93 @param items: A list of tab item, tab item format: (tab_name, tab_widget)
94 @param default_index: Initialize index, default is 0.
95 '''
96 self.tab_items += items
97
98 for item in items:
99 self.tab_title_widths.append(get_content_size(item[0], DEFAULT_FONT_SIZE)[0] + self.tab_padding_x * 2)
100
101 self.switch_content(default_index)
102
103 - def switch_content(self, index):
104 '''
105 Switch content with given index.
106
107 @param index: Tab index.
108 '''
109 if self.tab_index != index:
110 self.tab_index = index
111 widget = self.tab_items[index][1]
112
113 container_remove_all(self.tab_content_box)
114 self.tab_content_box.add(widget)
115 self.tab_title_box.queue_draw()
116 self.tab_content_box.queue_draw()
117
118 self.show_all()
119
121 '''
122 Internal callback for `button-press-event` signal.
123 '''
124 for (index, item) in enumerate(self.tab_items):
125 if is_in_rect((event.x, event.y),
126 (sum(self.tab_title_widths[0:index]),
127 0,
128 self.tab_title_widths[index],
129 self.tab_height)):
130 self.switch_content(index)
131 break
132
134 '''
135 Internal callback for `expose-event` signal.
136 '''
137 cr = widget.window.cairo_create()
138 rect = widget.allocation
139
140
141 tab_title_width = sum(self.tab_title_widths)
142
143 with cairo_state(cr):
144 with cairo_disable_antialias(cr):
145 cr.rectangle(rect.x,
146 rect.y,
147 sum(self.tab_title_widths[0:self.tab_index]),
148 self.tab_height)
149 cr.rectangle(rect.x + sum(self.tab_title_widths[0:min(self.tab_index + 1, len(self.tab_items))]) + 1,
150 rect.y,
151 sum(self.tab_title_widths) - sum(self.tab_title_widths[0:min(self.tab_index + 1, len(self.tab_items))]),
152 self.tab_height)
153 cr.clip()
154
155 cr.set_source_rgba(*alpha_color_hex_to_cairo((self.tab_unselect_bg_color.get_color(), 0.7)))
156 cr.rectangle(rect.x + 1, rect.y + 1, tab_title_width, self.tab_height)
157 cr.fill()
158
159 cr.set_line_width(1)
160 cr.set_source_rgba(*alpha_color_hex_to_cairo((self.tab_unselect_frame_color.get_color(), 1.0)))
161 cr.rectangle(rect.x + 1, rect.y + 1, tab_title_width, self.tab_height)
162 cr.stroke()
163
164 for (index, width) in enumerate(self.tab_title_widths[:-1]):
165 cr.set_source_rgba(*alpha_color_hex_to_cairo((self.tab_unselect_frame_color.get_color(), 1.0)))
166 cr.rectangle(rect.x + 1 + sum(self.tab_title_widths[0:index]) + width,
167 rect.y + 1,
168 1,
169 self.tab_height)
170 cr.fill()
171
172 cr.set_source_rgb(*color_hex_to_cairo(self.tab_select_frame_color.get_color()))
173 cr.rectangle(rect.x,
174 rect.y + rect.height - 1,
175 sum(self.tab_title_widths[0:self.tab_index]),
176 1)
177 cr.fill()
178
179 cr.set_source_rgb(*color_hex_to_cairo(self.tab_select_frame_color.get_color()))
180 cr.rectangle(rect.x + 1 + sum(self.tab_title_widths[0:self.tab_index]),
181 rect.y + rect.height - 1,
182 rect.width - sum(self.tab_title_widths[0:self.tab_index]),
183 1)
184 cr.fill()
185
186 for (index, item) in enumerate(self.tab_items):
187
188 title = item[0]
189
190
191 with cairo_disable_antialias(cr):
192 if index == self.tab_index:
193
194 cr.set_source_rgba(*alpha_color_hex_to_cairo((self.tab_select_bg_color.get_color(), 0.93)))
195 if index == 0:
196 cr.rectangle(rect.x + sum(self.tab_title_widths[0:index]),
197 rect.y + 1,
198 self.tab_title_widths[index] + 1,
199 self.tab_height)
200 else:
201 cr.rectangle(rect.x + 1 + sum(self.tab_title_widths[0:index]),
202 rect.y + 1,
203 self.tab_title_widths[index],
204 self.tab_height)
205 cr.fill()
206
207 if index == 0:
208 cr.rectangle(rect.x,
209 rect.y,
210 rect.width,
211 self.tab_height)
212 cr.clip()
213
214 cr.set_line_width(1)
215 cr.set_source_rgb(*color_hex_to_cairo(self.tab_select_frame_color.get_color()))
216 if index == 0:
217 cr.rectangle(rect.x + sum(self.tab_title_widths[0:index]),
218 rect.y + 1,
219 self.tab_title_widths[index] + 2,
220 self.tab_height)
221 else:
222 cr.rectangle(rect.x + 1 + sum(self.tab_title_widths[0:index]),
223 rect.y + 1,
224 self.tab_title_widths[index] + 1,
225 self.tab_height)
226 cr.stroke()
227
228 draw_text(cr, title,
229 rect.x + sum(self.tab_title_widths[0:index]) + self.tab_padding_x,
230 rect.y + self.tab_padding_y,
231 self.tab_title_widths[index] - self.tab_padding_x * 2,
232 self.tab_height - self.tab_padding_y * 2,
233 )
234
235 - def expose_tab_content_align(self, widget, event):
236 '''
237 Internal function to `expose-event` signal.
238 '''
239 cr = widget.window.cairo_create()
240 rect = widget.allocation
241
242 with cairo_disable_antialias(cr):
243 cr.set_source_rgb(*color_hex_to_cairo(self.tab_select_frame_color.get_color()))
244 cr.rectangle(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2)
245 cr.stroke()
246
247 - def expose_tab_content_box(self, widget, event):
248 '''
249 Internal function to `expose-event` signal.
250 '''
251 cr = widget.window.cairo_create()
252 rect = widget.allocation
253
254
255 toplevel = widget.get_toplevel()
256 coordinate = widget.translate_coordinates(toplevel, rect.x, rect.y)
257 (offset_x, offset_y) = coordinate
258
259 with cairo_state(cr):
260 cr.translate(-offset_x, -offset_y)
261 cr.rectangle(offset_x, offset_y, rect.width, rect.height)
262 cr.clip()
263
264 (shadow_x, shadow_y) = get_window_shadow_size(self.get_toplevel())
265 skin_config.render_background(cr, self, rect.x + shadow_x, rect.y + shadow_y)
266
267
268 cr.set_source_rgba(*alpha_color_hex_to_cairo((self.tab_select_bg_color.get_color(), 0.93)))
269 cr.rectangle(rect.x, rect.y, rect.width, rect.height)
270 cr.fill()
271
272 gobject.type_register(TabBox)
273
275 '''
276 Tab window.
277
278 @undocumented: click_confirm_button
279 @undocumented: click_cancel_button
280 '''
281
282 - def __init__(self, title, items,
283 confirm_callback=None,
284 cancel_callback=None,
285 window_width=458,
286 window_height=472):
287 '''
288 Initialize TabWindow clas.
289
290 @param title: Tab window title.
291 @param items: A list of tab item, tab item format: (tab_name, tab_widget)
292 @param confirm_callback: Callback when user click ok button.
293 @param cancel_callback: Callback when user click cancel button.
294 @param window_width: Default window width.
295 @param window_height: Default window height.
296 '''
297 DialogBox.__init__(self,
298 title,
299 window_width,
300 window_height,
301 mask_type=DIALOG_MASK_TAB_PAGE)
302 self.confirm_callback = confirm_callback
303 self.cancel_callback = cancel_callback
304
305 self.window_box = gtk.VBox()
306
307 self.tab_window_width = window_width
308 self.tab_window_height = window_height
309 self.tab_box = TabBox()
310 self.tab_box.add_items(items)
311 self.tab_align = gtk.Alignment()
312 self.tab_align.set(0.5, 0.5, 1.0, 1.0)
313 self.tab_align.set_padding(8, 0, 0, 0)
314 self.tab_align.add(self.tab_box)
315
316 self.confirm_button = Button(_("OK"))
317 self.cancel_button = Button(_("Cancel"))
318
319 self.window_box.pack_start(self.tab_align, True, True)
320
321 self.confirm_button.connect("clicked", lambda w: self.click_confirm_button())
322 self.cancel_button.connect("clicked", lambda w: self.click_cancel_button())
323 self.connect("destroy", lambda w: self.destroy())
324
325 self.body_box.pack_start(self.window_box, True, True)
326 self.right_button_box.set_buttons([self.confirm_button, self.cancel_button])
327
336
345
346 gobject.type_register(TabWindow)
347