1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 from animation import Animation, LinerInterpolator
24 from gtk import gdk
25 from label import Label
26 from theme import ui_theme
27 from utils import propagate_expose, color_hex_to_cairo, cairo_disable_antialias
28 import cairo
29 import gobject
30 import gtk
31
32
33 __all__ = ["text", "custom", "show_delay", "hide_delay", "hide_duration",
34 "background", "padding", "has_shadow", "disable", "always_update",
35 "disable_all"]
39 self.x = 0
40 self.y = 0
41 self.child = None
42 self.container = None
43
45 if widget.get_has_window() and widget.parent:
46 (wx, wy) = widget.window.get_position()
47 x += wx - widget.allocation.x
48 y += wy - widget.allocation.y
49 else:
50 x -= widget.allocation.x
51 y -= widget.allocation.y
52 return (x, y)
53
55 if not widget.is_drawable():
56 return
57 if not cl.child :
58 (x, y) = cl.container.translate_coordinates(widget, int(cl.x), int(cl.y))
59 if x >= 0 and x < widget.allocation.width and \
60 y >=0 and y < widget.allocation.height:
61 if isinstance(widget, gtk.Container):
62 tmp = ChildLocation()
63 (tmp.x, tmp.y, tmp.container) = (x, y, widget)
64 widget.forall(child_location_foreach, tmp)
65 if tmp.child:
66 cl.child = tmp.child
67 else:
68 cl.child = widget
69 else:
70 cl.child = widget
71
73 if window.get_window_type() == gdk.WINDOW_OFFSCREEN:
74 (px, py) = (-1, -1)
75 window.emit("to-embedder", window, x, y, px, py)
76 return (px, py)
77 else:
78 p = window.get_position()
79 return (x + p[0], y + p[1])
80
82 cl = ChildLocation()
83
84 widget = gdkwindow.get_user_data()
85 if widget == None:
86 return (None, cl.x, cl.y)
87
88 cl.x = window_x
89 cl.y = window_y
90
91 while gdkwindow and gdkwindow != widget.window:
92 (cl.x, cl.y) = coords_to_parent(gdkwindow, cl.x, cl.y)
93 gdkwindow = gdkwindow.get_effective_parent()
94
95 if not gdkwindow:
96 return (None, cl.x, cl.y)
97
98 (cl.x, cl.y) = window_to_alloc(widget, cl.x, cl.y)
99
100
101 if isinstance(widget, gtk.Container):
102 cl.container = widget
103 cl.child = None
104 tmp_widget = widget
105
106 widget.forall(child_location_foreach, cl)
107
108 if cl.child and WidgetInfo.get_info(cl.child):
109 widget = cl.child
110 elif cl.container and WidgetInfo.get_info(cl.container):
111 widget = cl.container
112
113 (cl.x, cl.y) = tmp_widget.translate_coordinates(widget, int(cl.x), int(cl.y))
114
115
116 if WidgetInfo.get_info(widget):
117 return (widget, cl.x, cl.y)
118
119
120 p = widget.get_parent()
121 while p:
122 if WidgetInfo.get_info(p):
123 return (p, cl.x, cl.y)
124 else:
125 p = p.get_parent()
126
127 return (None, cl.x, cl.y)
128
170
192
194 """ generate child widget and update the TooltipInfo"""
195 if TooltipInfo.widget == TooltipInfo.prewidget and TooltipInfo.alignment.child and not TooltipInfo.need_update:
196 return
197
198 TooltipInfo.widget = TooltipInfo.tmpwidget
199
200 TooltipInfo.winfo = WidgetInfo.get_info(TooltipInfo.widget)
201 winfo = TooltipInfo.winfo
202
203 pre_child = TooltipInfo.alignment.child
204
205 if pre_child and winfo == WidgetInfo.get_info(pre_child) and not TooltipInfo.need_update:
206 return
207
208 if winfo.custom:
209 child = winfo.custom(*winfo.custom_args, **winfo.custom_kargs)
210 elif winfo.text:
211 child = Label(winfo.text, *winfo.text_args, **winfo.text_kargs)
212 else:
213 raise Warning, "tooltip enable's widget must has text or custom property"
214
215 if pre_child:
216 TooltipInfo.alignment.remove(pre_child)
217 pre_child.destroy()
218
219 TooltipInfo.alignment.set_padding(winfo.padding_t, winfo.padding_l, winfo.padding_b, winfo.padding_r)
220 TooltipInfo.alignment.add(child)
221 TooltipInfo.alignment.show_all()
222
223 allocation = gtk.gdk.Rectangle(0, 0, *TooltipInfo.alignment.child.size_request())
224 allocation.width += winfo.padding_l + winfo.padding_r
225 allocation.height += winfo.padding_t + winfo.padding_b
226 TooltipInfo.window.size_allocate(allocation)
227 TooltipInfo.window.modify_bg(gtk.STATE_NORMAL, winfo.background)
228 if winfo.always_update:
229 TooltipInfo.need_update = True
230 else:
231 TooltipInfo.need_update = False
232
235 def disable_q():
236 TooltipInfo.in_quickshow = False
237 if TooltipInfo.quickshow_id != 0:
238 gobject.source_remove(TooltipInfo.quickshow_id)
239 TooltipInfo.in_quickshow = True
240 if TooltipInfo.quickshow_id == 0:
241 TooltipInfo.quickshow_id = gobject.timeout_add(TooltipInfo.quickshow_delay, disable_q)
242 else:
243 gobject.source_remove(TooltipInfo.quickshow_id)
244 TooltipInfo.quickshow_id = gobject.timeout_add(TooltipInfo.quickshow_delay, disable_q)
245
256
301
306 def on_realize(win):
307 win.swindow = gtk.gdk.Window(win.get_parent_window(),
308 width=0, height=0,
309 window_type=gtk.gdk.WINDOW_TEMP,
310 wclass=gtk.gdk.INPUT_OUTPUT,
311 event_mask=(win.get_events() | gdk.EXPOSURE_MASK),
312 visual=win.get_visual(),
313 colormap=win.get_colormap(),
314 )
315 win.swindow.set_user_data(win)
316
317
318 win.animation = Animation([win.window, win.swindow], gdk.Window.set_opacity, 1000, [0, 1],
319 lambda *args: 1 - LinerInterpolator(*args))
320
321 def on_map(win):
322 winfo = TooltipInfo.winfo
323 win.animation.init(1)
324 win.animation.start_after(winfo.hide_delay)
325 geo = win.window.get_geometry()
326 win.swindow.move_resize(geo[0]+TooltipInfo.offset_x, geo[1]+TooltipInfo.offset_y,
327 win.allocation.width, win.allocation.height)
328
329 win.swindow.show()
330
331 def on_expose_event(win, e):
332 cr = win.swindow.cairo_create()
333 cr.set_source_rgba(1, 1, 1, 0)
334 cr.set_operator(cairo.OPERATOR_SOURCE)
335 cr.paint()
336 winfo = TooltipInfo.winfo
337 if winfo.has_shadow:
338
339 (x, y, width, height) = (0, 0, win.allocation.width, win.allocation.height)
340 (o_x, o_y) = (5, 5)
341
342
343
344 radial = cairo.RadialGradient(width - o_x, height-o_y, 1, width -o_x, height-o_y, o_x)
345 radial.add_color_stop_rgba(0.0, 0,0,0, 0.3)
346 radial.add_color_stop_rgba(0.6, 0,0,0, 0.1)
347 radial.add_color_stop_rgba(1, 0,0,0, 0)
348 cr.set_source(radial)
349 cr.rectangle(width-o_x, height-o_y, o_x, o_y)
350 cr.fill()
351
352
353 radial = cairo.RadialGradient(o_x, height-o_y, 1, o_x, height-o_y, o_x)
354 radial.add_color_stop_rgba(0.0, 0,0,0, 0.3)
355 radial.add_color_stop_rgba(0.6, 0,0,0, 0.1)
356 radial.add_color_stop_rgba(1, 0,0,0, 0)
357 cr.set_source(radial)
358 cr.rectangle(0, height-o_y, o_x, o_y)
359 cr.fill()
360
361
362 radial = cairo.RadialGradient(width-o_x, o_y, 1, width-o_x, o_y, o_x)
363 radial.add_color_stop_rgba(0.0, 0,0,0, 0.3)
364 radial.add_color_stop_rgba(0.6, 0,0,0, 0.1)
365 radial.add_color_stop_rgba(1, 0,0,0, 0)
366 cr.set_source(radial)
367 cr.rectangle(width-o_x, 0, o_x, o_y)
368 cr.fill()
369
370
371 vradial = cairo.LinearGradient(0, height-o_y, 0, height)
372 vradial.add_color_stop_rgba(0.0, 0,0,0, .5)
373 vradial.add_color_stop_rgba(0.4, 0,0,0, 0.25)
374 vradial.add_color_stop_rgba(1, 0,0,0, 0.0)
375 cr.set_source(vradial)
376 cr.rectangle(o_x, height-o_x, width-2*o_x, height)
377 cr.fill()
378
379 hradial = cairo.LinearGradient(width-o_x, 0, width, 0)
380 hradial.add_color_stop_rgba(0.0, 0,0,0, .5)
381 hradial.add_color_stop_rgba(0.4, 0,0,0, 0.25)
382 hradial.add_color_stop_rgba(1, 0,0,0, 0.0)
383 cr.set_source(hradial)
384 cr.rectangle(width-o_x, o_y, width, height-2*o_y)
385 cr.fill()
386
387 gtk.Alignment.do_expose_event(TooltipInfo.alignment, e)
388 propagate_expose(win, e)
389 return True
390
391 def on_unmap(win):
392 win.swindow.hide()
393
394 def on_expose_alignment(widget, event):
395 '''Expose tooltip label.'''
396 rect = widget.allocation
397 cr = widget.window.cairo_create()
398
399 with cairo_disable_antialias(cr):
400 cr.set_line_width(1)
401 cr.set_source_rgba(*color_hex_to_cairo(ui_theme.get_color("tooltip_frame").get_color()))
402 cr.rectangle(rect.x + 1, rect.y + 1, rect.width - 1, rect.height - 1)
403 cr.stroke()
404 return True
405
406 TooltipInfo.window = gtk.Window(gtk.WINDOW_POPUP)
407 TooltipInfo.window.set_colormap(gtk.gdk.Screen().get_rgba_colormap())
408 TooltipInfo.alignment = gtk.Alignment()
409 TooltipInfo.window.add(TooltipInfo.alignment)
410 TooltipInfo.window.connect('realize', on_realize)
411 TooltipInfo.window.connect('map', on_map)
412 TooltipInfo.window.connect('unmap', on_unmap)
413 TooltipInfo.window.connect('expose-event', on_expose_event)
414 TooltipInfo.alignment.connect('expose-event', on_expose_alignment)
415 __init_window()
416
417
418
419
420 display = None
440
484
485 all_method = {}
487 all_method[func.__name__] = func
488 def wrap(*args, **kargs):
489 return func(*args, **kargs)
490 wrap.__dict__ = all_method
491 return wrap
492
493
494
495
496 @chainmethod
497 -def set_value(widgets, kv):
498 if not isinstance(widgets, list):
499 widgets = [widgets]
500 for w in widgets:
501 w_info = WidgetInfo.get_info(w)
502 if not w_info:
503 w_info = init_widget(w)
504 for k in kv:
505 setattr(w_info, k, kv[k])
506 return set_value
507
508
509 @chainmethod
510 -def text(widget, content, *args, **kargs):
511 '''
512 set the tooltip's text content.
513 the "content", "*args" and "**kargs" are pass to the dtk.ui.Label,
514 so you can change the text's color and some other property.
515
516 @param widget: the widget of you want to change.
517 @param content: the text which you want show.
518 @param args: pass to the dtk.ui.Label
519 @param kargs: pass to the dtk.ui.Label
520 '''
521 set_value(widget, {
522 "text": content,
523 "text_args":args,
524 "text_kargs":kargs
525 })
526 return text
527
528 @chainmethod
529 -def custom(widget, cb, *args, **kargs):
530 '''
531 Set the custom tooltip content.
532
533 @param widget: the widget of you want to change.
534 @param cb: the function used to generate the content widget. this function should return an gtk.Widget. Be careful: if this function generate it's content affected by other runtime factor, you alsow should use "always_update"
535 to disable the internal cache mechanism
536 @param args: pass to the cb
537 @param kargs: pass to the cb
538 '''
539 set_value(widget, {
540 "custom" : cb,
541 "custom_args" : args,
542 "custom_kargs" : kargs
543 })
544 return custom
545 @chainmethod
547 '''
548 set the time of the tooltip's begin show after pointer stay on the widget.
549
550 @param widget: the widget of you want to change.
551 @param delay: the time of start begin show.
552 '''
553 delay = max(250, delay)
554 set_value(widget, {"show_delay": delay})
555 return show_delay
556
559 '''
560 set the time of the tooltip's start to hide.
561
562 @param widget: the widget of you want to change.
563 @param delay: the time of start begin hide.
564 '''
565 set_value(widget, {"hide_delay": delay})
566 return hide_delay
567
570 '''
571 set the duration of the tooltip's hide effect duration.
572
573 @param widget: the widget of you want to change.
574 @param delay: the time of the effect duration.
575 '''
576 set_value(widget, {"hide_duration": delay})
577 return hide_duration
578
581 '''
582 set the background of the tooltip's content.
583
584 @param widget: the widget of you want to change.
585 @param color: the gdk.Color of background.
586 '''
587 set_value(widget, {"background": color})
588 return background
589
590 @chainmethod
591 -def padding(widget, t, l, b, r):
592 '''
593 set the padding of the tooltip's content.
594
595 @param widget: the widget of you want to change.
596
597 @param t: the top space
598 @param l: the left space
599 @param b: the bottom space
600 @param r: the right space
601 '''
602 kv = {}
603 if t >= 0:
604 kv["padding_t"] = int(t)
605 if b >= 0:
606 kv["padding_b"] = int(b)
607 if l >= 0:
608 kv["padding_l"] = int(l)
609 if r >= 0:
610 kv["padding_r"] = int(r)
611
612 set_value(widget, kv)
613 return padding
614
615
616 @chainmethod
617 -def has_shadow(widget, need):
618 '''
619 whether this widget's tooltip need shadow.
620
621 @param widget: the widget of you want disable tooltip.
622 @param need : wheter need shadow .
623 '''
624 set_value(widget, {"has_shadow": need})
625 return has_shadow
626
627
628 @chainmethod
629 -def disable(widget, is_disable):
630 '''
631 disable this widget's tooltip
632
633 @param widget: the widget of you want disable tooltip.
634 @param is_disable: wheter disable tooltip.
635 '''
636 winfo = WidgetInfo.get_info(widget)
637 if is_disable:
638 if winfo.enable :
639 winfo.enable = False
640 TooltipInfo.enable_count -= 1
641 else:
642 if not winfo.enable :
643 winfo.enable = True
644 TooltipInfo.enable_count += 1
645 return disable
646
649 '''
650 Always create the new tooltip's content, used to show the
651
652 curstom tooltip content generate by function and the function's
653
654 return widget is different every time be invoked.
655
656 @param widget: Gtk.Widget instance.
657 @param need: whether alwasy update.
658 '''
659 set_value(widget, {"always_update" : need})
660 return always_update
661
664 '''
665 '''
666 count = TooltipInfo.enable_count
667 if is_disable:
668 if count > 0:
669 TooltipInfo.enable_count = -count
670 else:
671 if count < 0:
672 TooltipInfo.enable_count = -count
673
683 gdk.event_handler_set(tooltip_handler)
684