61
60
self._layout_ltr(cr, x, y, w, h, r, aw)
63
62
self._layout_rtl(cr, x, y, w, h, r, aw)
67
65
class ShapeRoundedRect(Shape):
79
77
def __init__(self, direction=Gtk.TextDirection.LTR):
80
78
Shape.__init__(self, direction)
81
79
self.name = 'RoundedRect'
84
81
def layout(self, cr, x, y, w, h, r, aw):
86
cr.arc(r+x, r+y, r, PI, 270*PI_OVER_180)
87
cr.arc(x+w-r, r+y, r, 270*PI_OVER_180, 0)
88
cr.arc(x+w-r, y+h-r, r, 0, 90*PI_OVER_180)
89
cr.arc(r+x, y+h-r, r, 90*PI_OVER_180, PI)
83
cr.arc(r + x, r + y, r, PI, 270 * PI_OVER_180)
84
cr.arc(x + w - r, r + y, r, 270 * PI_OVER_180, 0)
85
cr.arc(x + w - r, y + h - r, r, 0, 90 * PI_OVER_180)
86
cr.arc(r + x, y + h - r, r, 90 * PI_OVER_180, PI)
94
90
class ShapeStartArrow(Shape):
96
92
def __init__(self, direction=Gtk.TextDirection.LTR):
97
93
Shape.__init__(self, direction)
98
94
self.name = 'StartArrow'
101
96
def _layout_ltr(self, cr, x, y, w, h, r, aw):
105
cr.arc(r+x, r+y, r, PI, 270*PI_OVER_180)
100
cr.arc(r + x, r + y, r, PI, 270 * PI_OVER_180)
108
cr.line_to(x+w-haw, y)
109
cr.line_to(x+w+haw, y+(h/2))
110
cr.line_to(x+w-haw, y+h)
112
cr.arc(r+x, y+h-r, r, 90*PI_OVER_180, PI)
103
cr.line_to(x + w - haw, y)
104
cr.line_to(x + w + haw, y + (h / 2))
105
cr.line_to(x + w - haw, y + h)
107
cr.arc(r + x, y + h - r, r, 90 * PI_OVER_180, PI)
116
110
def _layout_rtl(self, cr, x, y, w, h, r, aw):
119
113
cr.new_sub_path()
120
cr.move_to(x-haw, (y+h)/2)
121
cr.line_to(x+aw-haw, y)
122
cr.arc(x+w-r, r+y, r, 270*PI_OVER_180, 0)
123
cr.arc(x+w-r, y+h-r, r, 0, 90*PI_OVER_180)
124
cr.line_to(x+aw-haw, y+h)
114
cr.move_to(x - haw, (y + h) / 2)
115
cr.line_to(x + aw - haw, y)
116
cr.arc(x + w - r, r + y, r, 270 * PI_OVER_180, 0)
117
cr.arc(x + w - r, y + h - r, r, 0, 90 * PI_OVER_180)
118
cr.line_to(x + aw - haw, y + h)
129
122
class ShapeMidArrow(Shape):
133
126
#~ self.draw_xoffset = -2
134
127
self._color = 0, 1, 0
135
128
self.name = 'MidArrow'
138
130
def _layout_ltr(self, cr, x, y, w, h, r, aw):
139
self.hadjustment = haw = aw/2
140
cr.move_to(x-haw-1, y)
131
self.hadjustment = haw = aw / 2
132
cr.move_to(x - haw - 1, y)
142
cr.line_to(x+w-haw, y)
143
cr.line_to(x+w+haw, y+(h/2))
144
cr.line_to(x+w-haw, y+h)
145
cr.line_to(x-haw-1, y+h)
134
cr.line_to(x + w - haw, y)
135
cr.line_to(x + w + haw, y + (h / 2))
136
cr.line_to(x + w - haw, y + h)
137
cr.line_to(x - haw - 1, y + h)
147
cr.line_to(x+haw-1, y+(h/2))
139
cr.line_to(x + haw - 1, y + (h / 2))
152
143
def _layout_rtl(self, cr, x, y, w, h, r, aw):
153
self.hadjustment = haw = -aw/2
144
self.hadjustment = haw = -aw / 2
155
cr.move_to(x+haw, (h+y)/2)
156
cr.line_to(x+aw+haw, y)
157
cr.line_to(x+w-haw+1, y)
158
cr.line_to(x+w-aw-haw+1, (y+h)/2)
159
cr.line_to(x+w-haw+1, y+h)
160
cr.line_to(x+aw+haw, y+h)
146
cr.move_to(x + haw, (h + y) / 2)
147
cr.line_to(x + aw + haw, y)
148
cr.line_to(x + w - haw + 1, y)
149
cr.line_to(x + w - aw - haw + 1, (y + h) / 2)
150
cr.line_to(x + w - haw + 1, y + h)
151
cr.line_to(x + aw + haw, y + h)
165
155
class ShapeEndCap(Shape):
169
159
#~ self.draw_xoffset = -2
170
160
self._color = 0, 0, 1
171
161
self.name = 'EndCap'
174
163
def _layout_ltr(self, cr, x, y, w, h, r, aw):
175
self.hadjustment = haw = aw/2
164
self.hadjustment = haw = aw / 2
177
cr.move_to(x-haw-1, y)
166
cr.move_to(x - haw - 1, y)
179
cr.arc(x+w-r, r+y, r, 270*PI_OVER_180, 0)
180
cr.arc(x+w-r, y+h-r, r, 0, 90*PI_OVER_180)
168
cr.arc(x + w - r, r + y, r, 270 * PI_OVER_180, 0)
169
cr.arc(x + w - r, y + h - r, r, 0, 90 * PI_OVER_180)
182
cr.line_to(x-haw-1, y+h)
183
cr.line_to(x+haw-1, y+(h/2))
171
cr.line_to(x - haw - 1, y + h)
172
cr.line_to(x + haw - 1, y + (h / 2))
187
175
def _layout_rtl(self, cr, x, y, w, h, r, aw):
188
self.hadjustment = haw = -aw/2
176
self.hadjustment = haw = -aw / 2
190
cr.arc(r+x, r+y, r, PI, 270*PI_OVER_180)
191
cr.line_to(x+w-haw+1, y)
192
cr.line_to(x+w-haw-aw+1, (y+h)/2)
193
cr.line_to(x+w-haw+1, y+h)
194
cr.arc(r+x, y+h-r, r, 90*PI_OVER_180, PI)
178
cr.arc(r + x, r + y, r, PI, 270 * PI_OVER_180)
179
cr.line_to(x + w - haw + 1, y)
180
cr.line_to(x + w - haw - aw + 1, (y + h) / 2)
181
cr.line_to(x + w - haw + 1, y + h)
182
cr.arc(r + x, y + h - r, r, 90 * PI_OVER_180, PI)
199
186
class AnimationClock(GObject.GObject):
205
"animation-frame" : (GObject.SignalFlags.RUN_LAST,
189
"animation-frame": (GObject.SignalFlags.RUN_LAST,
209
"animation-finished" : (GObject.SignalFlags.RUN_FIRST,
193
"animation-finished": (GObject.SignalFlags.RUN_FIRST,
215
198
def __init__(self, fps, duration):
216
199
GObject.GObject.__init__(self)
257
238
self._clock = None
258
239
self._progress = 0
259
240
self.in_progress = False
263
243
self.stop(who_called='start')
264
if not self.sequence: return
244
if not self.sequence:
266
247
self._clock = GObject.timeout_add(self._timestep,
267
248
self._schedule_animation_frame,
269
250
self.in_progress = True
273
253
class PathBarAnimator(AnimationClock):
275
254
# animation display constants
277
256
DURATION = 150 # spec says 150ms
279
258
# animation modes
280
NONE = 'animation-none'
281
OUT = 'animation-out'
259
NONE = 'animation-none'
260
OUT = 'animation-out'
283
262
WIDTH_CHANGE = 'animation-width-change'
285
264
def __init__(self, pathbar):
302
280
anim_alloc = Gdk.Rectangle()
303
anim_alloc.x = real_alloc.x-xo
281
anim_alloc.x = real_alloc.x - xo
304
282
anim_alloc.y = real_alloc.y
305
283
anim_alloc.width = real_alloc.width
306
284
anim_alloc.height = real_alloc.height
308
286
part.new_frame(anim_alloc)
311
288
def _animate_in(self, part, progress, kwargs):
312
289
real_alloc = part.get_allocation()
318
295
anim_alloc = Gdk.Rectangle()
319
anim_alloc.x = real_alloc.x-xo
296
anim_alloc.x = real_alloc.x - xo
320
297
anim_alloc.y = real_alloc.y
321
298
anim_alloc.width = real_alloc.width
322
299
anim_alloc.height = real_alloc.height
324
301
part.new_frame(anim_alloc)
327
303
def _animate_width_change(self, part, progress, kwargs):
328
304
start_w = kwargs['start_width']
331
307
width = int(round(start_w + (end_w - start_w) * progress))
332
308
part.set_size_request(width, part.get_height_request())
335
310
def _on_animation_frame(self, clock, progress):
336
if not self.sequence: return
311
if not self.sequence:
338
314
for actor, animation, kwargs in self.sequence:
340
if animation == PathBarAnimator.NONE: continue
315
if animation == PathBarAnimator.NONE:
342
318
if animation == PathBarAnimator.OUT:
343
319
self._animate_out(actor, progress, kwargs)
357
331
self.sequence = []
358
332
self.pathbar.psuedo_parts = []
359
333
self.pathbar.queue_draw()
362
335
def append_animation(self, actor, animation, **kwargs):
363
336
self.sequence.append((actor, animation, kwargs))
366
338
def reset(self, who_called='?'):
367
AnimationClock.stop(self, who_called=who_called+'.reset')
339
AnimationClock.stop(self, who_called=who_called + '.reset')
368
340
self.sequence = []
372
343
class PathBar(Gtk.HBox):
374
MIN_PART_WIDTH = 25 # pixels
345
MIN_PART_WIDTH = 25 # pixels
376
347
def __init__(self):
377
348
GObject.GObject.__init__(self)
468
437
# paint a frame around the entire pathbar
469
438
width = self.get_parts_width()
470
Gtk.render_background(context, cr, 1, 1, width-2, a.height-2)
439
Gtk.render_background(context, cr, 1, 1, width - 2, a.height - 2)
472
441
self._paint_widget_parts(cr, context, a.x, a.y)
554
522
print 'shrink parts by:', overhang
555
523
self._shrink_parts(overhang)
558
525
def _reclaim_space(self, part):
559
if not self.out_of_width: return
526
if not self.out_of_width:
561
529
claim = part.get_width_request()
562
530
self._grow_parts(claim)
565
532
def _append_compose_parts(self, new_part):
566
533
d = self.get_direction()
595
562
last = children[-1]
596
563
last.set_shape(ShapeEndCap(d))
597
564
self.queue_draw()
600
566
def _cleanup_revealer(self):
601
if not self._revealer: return
567
if not self._revealer:
602
569
GObject.source_remove(self._revealer)
603
570
self._revealer = None
606
572
def _theme(self, part):
607
573
#~ part.set_padding(self.theme['xpad'], self.theme['ypad'])
608
574
part.set_padding(12, 4)
613
578
def first_part(self):
614
579
children = self.get_children()
615
if not children: return None
619
584
def last_part(self):
620
585
children = self.get_children()
621
if not children: return None
624
589
def reveal_part(self, part, animate=True):
625
590
# do not do here:
787
751
self.animation_allocation = allocation
788
752
self.queue_draw()
791
754
def animation_finished(self):
792
755
self.animation_in_progress = False
793
756
self.animation_allocation = None
794
if self.get_parent(): self.get_parent().queue_draw()
757
if self.get_parent():
758
self.get_parent().queue_draw()
797
760
def paint(self, cr, a, context, xo, yo):
798
if self.is_nopaint: return
803
767
w, h = a.width, a.height
804
arrow_width = 12#theme['arrow-width']
768
arrow_width = 12 # theme['arrow-width']
806
770
if isinstance(self, PathPart):
807
771
_a = self.get_allocation()
808
772
self.shape.layout(cr,
773
_a.x - xo + 1, _a.y - yo,
810
774
w, h, 3, arrow_width)
813
777
Gtk.render_background(context, cr,
815
a.width+10, a.height)
778
a.x - xo - 10, a.y - yo,
779
a.width + 10, a.height)
817
cr.translate(a.x-xo, a.y-yo)
781
cr.translate(a.x - xo, a.y - yo)
819
783
if self.shape.name.find('Arrow') != -1:
820
784
# draw arrow head
821
cr.move_to(w-arrow_width/2, 2)
823
cr.line_to(w-arrow_width/2, h-2)
785
cr.move_to(w - arrow_width / 2, 2)
786
cr.line_to(w + 5, h / 2)
787
cr.line_to(w - arrow_width / 2, h - 2)
824
788
# fetch the line color and stroke
825
789
rgba = context.get_border_color(Gtk.StateFlags.NORMAL)
826
790
cr.set_source_rgb(rgba.red, rgba.green, rgba.blue)
831
795
e = self.layout.get_pixel_extents()[1]
832
796
lw, lh = e.width, e.height
833
797
pw, ph = a.width, a.height
835
x = min(self.xpadding, (pw-lw)/2)
799
x = min(self.xpadding, (pw - lw) / 2)
839
803
Gtk.render_layout(context,
845
809
# paint the focus frame if need be
846
810
if isinstance(self, PathPart) and self.has_focus():
848
x, w, h = x-2, lw+4, lh+1
812
x, w, h = x - 2, lw + 4, lh + 1
849
813
Gtk.render_focus(context, cr, x, y, w, h)
855
818
class PsuedoPathPart(PathPartCommon):
887
849
return self.size_request[1]
889
851
def animation_finished(self):
892
854
def queue_draw(self):
893
855
a = self.allocation
895
self.parent.queue_draw_area(a.x-aw/2, a.y,
896
a.width+aw, a.height)
857
self.parent.queue_draw_area(a.x - aw / 2, a.y,
858
a.width + aw, a.height)
900
861
class PathPart(Gtk.EventBox, PathPartCommon):
903
"clicked" : (GObject.SignalFlags.RUN_LAST,
864
"clicked": (GObject.SignalFlags.RUN_LAST,
941
901
part.set_state(Gtk.StateFlags.PRELIGHT)
942
902
self.queue_draw()
945
904
def _on_leave_notify(self, part, event):
946
905
self.pathbar.queue_reveal_part(self.pathbar.last_part)
947
906
part.set_state(Gtk.StateFlags.NORMAL)
948
907
self.queue_draw()
951
909
def _on_button_press(self, part, event):
952
if event.button != 1: return
910
if event.button != 1:
953
912
self.pathbar._press_origin = part
954
913
part.set_state(Gtk.StateFlags.ACTIVE)
955
914
self.queue_draw()
958
916
def _on_button_release(self, part, event):
959
if event.button != 1: return
917
if event.button != 1:
961
920
if self.pathbar._press_origin != part:
962
921
self.pathbar._press_origin = None
971
930
self.emit, 'clicked')
973
932
self.queue_draw()
976
934
def _on_key_press(self, part, event):
977
935
if event.keyval in (Gdk.KEY_space, Gdk.KEY_Return, Gdk.KEY_KP_Enter):
978
936
part.set_state(Gtk.StateFlags.ACTIVE)
979
937
self.queue_draw()
982
939
def _on_key_release(self, part, event):
983
940
if event.keyval in (Gdk.KEY_space, Gdk.KEY_Return, Gdk.KEY_KP_Enter):
985
942
GObject.timeout_add(self.pathbar._timeout_initial,
986
943
self.emit, 'clicked')
987
944
self.queue_draw()
990
946
def _on_focus_in(self, part, event):
991
947
self.pathbar.reveal_part(self)
994
949
def _on_focus_out(self, part, event):
995
950
self.queue_draw()
998
952
# private methods
999
953
def _init_event_handling(self):
1000
954
self.set_property("can-focus", True)
1001
self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK|
1002
Gdk.EventMask.BUTTON_RELEASE_MASK|
1003
Gdk.EventMask.KEY_RELEASE_MASK|
1004
Gdk.EventMask.KEY_PRESS_MASK|
1005
Gdk.EventMask.ENTER_NOTIFY_MASK|
955
self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK |
956
Gdk.EventMask.BUTTON_RELEASE_MASK |
957
Gdk.EventMask.KEY_RELEASE_MASK |
958
Gdk.EventMask.KEY_PRESS_MASK |
959
Gdk.EventMask.ENTER_NOTIFY_MASK |
1006
960
Gdk.EventMask.LEAVE_NOTIFY_MASK)
1008
962
self.connect("enter-notify-event", self._on_enter_notify)
1013
967
self.connect("key-release-event", self._on_key_release)
1014
968
self.connect("focus-in-event", self._on_focus_in)
1015
969
self.connect("focus-out-event", self._on_focus_out)
1018
971
def _calc_natural_size(self, who_called='?'):
1019
972
ne = self.natural_extents
1020
973
nw, nh = ne.width, ne.height
1022
nw += self.shape.hadjustment + 2*self.xpadding
1023
nh += 2*self.ypadding
975
nw += self.shape.hadjustment + 2 * self.xpadding
976
nh += 2 * self.ypadding
1025
978
self.natural_size = nw, nh
1026
979
self.set_size_request(nw, nh)
1029
981
# public methods
1035
987
self.xpadding = xpadding
1036
988
self.ypadding = ypadding
1037
989
self._calc_natural_size()
1040
991
def set_size_request(self, width, height):
1041
width = max(2*self.xpadding+1, width)
1042
height = max(2*self.ypadding+1, height)
1043
self.layout.set_width(Pango.SCALE * (width - 2*self.xpadding))
992
width = max(2 * self.xpadding + 1, width)
993
height = max(2 * self.ypadding + 1, height)
994
self.layout.set_width(Pango.SCALE * (width - 2 * self.xpadding))
1044
995
Gtk.Widget.set_size_request(self, width, height)
1047
997
def set_nopaint(self, is_nopaint):
1048
998
self.is_nopaint = is_nopaint
1049
999
self.queue_draw()
1052
1001
def set_shape(self, shape):
1053
if shape == self.shape: return
1002
if shape == self.shape:
1054
1004
self.shape = shape
1055
1005
self._calc_natural_size()
1056
1006
self.queue_draw()
1059
1008
def set_label(self, label):
1060
1009
self.label = label
1207
1153
if 'do_callback' in kwargs and kwargs['do_callback']:
1208
1154
part = self[-1]
1209
1155
part.callback(self, part)
1212
1157
def remove_all(self, **kwargs):
1213
if len(self) <= 1: return
1214
1160
ids = filter(lambda k: k != 'category',
1215
1161
self.id_to_part.keys())
1216
1162
self.remove_ids(*ids, **kwargs)
1219
1164
def has_id(self, id):
1220
return self.id_to_part.has_key(id)
1165
return id in self.id_to_part
1222
1167
def get_parts(self):
1223
1168
return self.get_children()
1225
1170
def get_active(self):
1226
1171
parts = self.get_parts()
1227
if not parts: return None
1230
1175
def get_button_from_id(self, id):
1232
1177
return the button for the given id (or None)
1234
if not id in self.id_to_part:
1236
return self.id_to_part[id]
1179
return self.id_to_part.get(id)
1238
1181
def set_active_no_callback(self, part):