Automatically set the height according to the text height.
[pysize.git] / pysize / ui / gtk / pysize_widget_draw.py
blobf912c8e83de8f89352a234f8946ca6d4af2f408f
1 import pygtk
2 pygtk.require('2.0')
3 import gtk
4 assert gtk.pygtk_version >= (2, 8)
5 import pango
6 import gobject
7 import math
8 import cairo
9 import pangocairo
11 from pysize.ui.utils import human_unit, min_size_to_consider
13 RADIUS = 20.0
14 LINE_WIDTH = 4.0
15 CAIRO_IS_FAST = True
17 class PysizeWidget_Draw(object):
18 def __init__(self, options, args):
19 self.connect('expose-event', type(self)._expose_event)
20 self.modify_font(pango.FontDescription('Monospace 12'))
21 self.max_text_height = self.measure_font_height()
23 def measure_font_height(self):
24 (w, h) = self.create_pango_layout('a').get_pixel_size()
25 return h
27 def _get_requested_height(self):
28 return self.max_text_height * self.tree.root.size / \
29 min_size_to_consider(self.options.min_size)
31 def queue_node_redraw(self, node):
32 if node:
33 (x0, x1, y0, y1) = map(int, node.rectangle)
34 self.queue_draw_area(x0, y0, x1 - x0, y1 - y0)
36 def _draw_text(self, context, text, x0, x1, y0, y1):
37 pl = self.create_pango_layout(text)
38 pl.set_alignment(pango.ALIGN_CENTER)
39 w = x1 - x0
40 h = y1 - y0
41 pl.set_width(int(w*pango.SCALE))
42 pl.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
43 (real_w, real_h) = pl.get_pixel_size()
44 if real_h > self.max_text_height:
45 self.max_text_height = real_h
46 if real_h > h:
47 return False
48 context.move_to(x0, y0+(h-real_h)/2.0)
49 context.set_source_rgb(0, 0, 0)
50 context.show_layout(pl)
51 return True
53 def _get_node_colors(self, node, colors):
54 if node == self.cursor_node:
55 colors = map(lambda c: c+0.1, colors)
56 if node in self.selected_nodes + [self.button_press_node]:
57 colors = map(lambda c: c-0.3, colors)
58 return colors
60 def _draw_box(self, context, x0, x1, y0, y1, node):
61 if x0 == 0.0:
62 x0 += LINE_WIDTH/2.0
63 else:
64 x0 += LINE_WIDTH/4.0
65 x1 -= LINE_WIDTH/4.0
66 if y0 == 0.0:
67 y0 += LINE_WIDTH/2.0
68 else:
69 y0 += LINE_WIDTH/4.0
70 y1 -= LINE_WIDTH/4.0
71 node.rectangle = (x0, x1, y0, y1)
73 if CAIRO_IS_FAST:
74 context.new_path()
75 context.arc(x0 + RADIUS, y0 + RADIUS, RADIUS,
76 - math.pi, - math.pi / 2.0)
77 context.rel_line_to(x1 - x0 - 2*RADIUS, 0)
78 context.arc(x1 - RADIUS, y0 + RADIUS, RADIUS,
79 - math.pi / 2.0, 0)
80 context.rel_line_to(0, y1 - y0 - 2*RADIUS)
81 context.arc(x1 - RADIUS, y1 - RADIUS, RADIUS,
82 0, math.pi / 2.0)
83 context.rel_line_to(- x1 + x0 + 2*RADIUS, 0)
84 context.arc(x0 + RADIUS, y1 - RADIUS, RADIUS,
85 math.pi / 2.0, math.pi)
86 context.close_path()
88 context.set_source_rgb(0, 0, 0)
89 context.stroke_preserve()
91 gradient = cairo.LinearGradient(0, y0, 0, y1)
92 colors = self._get_node_colors(node, [0.5, 0.5, 1.0, 0.0, 0.5, 1.0])
93 gradient.add_color_stop_rgb(0.0, *colors[:3])
94 gradient.add_color_stop_rgb(1.0, *colors[3:])
95 context.set_source(gradient)
96 context.fill()
97 else:
98 context.rectangle(x0, y0, x1 - x0, y1 - y0)
99 context.set_source_rgb(0, 0, 0)
100 context.stroke_preserve()
102 colors = self._get_node_colors(node, [0, 0.5, 1.0])
103 context.set_source_rgb(*colors)
104 context.fill()
106 name = node.get_name()
107 size = human_unit(node.size)
108 self._draw_text(context, name + '\n' + size, x0, x1, y0, y1) or \
109 self._draw_text(context, name, x0, x1, y0, y1) or \
110 self._draw_text(context, size, x0, x1, y0, y1)
112 def _draw_boxes(self, context, node, depth, offset):
113 w = self.allocation.width
114 h = self.allocation.height
115 x0 = depth * (w - 1.0) / self.tree.height
116 x1 = (depth + 1.0) * (w - 1.0) / self.tree.height
117 y0 = (h - 1.0) * offset / self.tree.root.size
118 y1 = (h - 1.0) * (offset + node.size) / self.tree.root.size
120 self._draw_box(context, x0, x1, y0, y1, node)
121 depth += 1
122 for child in node.children:
123 self._draw_boxes(context, child, depth, offset)
124 offset += child.size
126 def _draw(self, context):
127 max_text_height_before = self.max_text_height
128 context.set_line_width(LINE_WIDTH)
129 offset = 0
130 for child in self.tree.root.children:
131 self._draw_boxes(context, child, 0, offset)
132 offset += child.size
133 if self.max_text_height != max_text_height_before:
134 self.schedule_new_tree()
136 def _expose_event(self, event):
137 context = self.window.cairo_create()
139 # set a clip region for the expose event
140 context.rectangle(event.area.x, event.area.y,
141 event.area.width, event.area.height)
142 context.clip()
144 self._draw(context)
145 return False
147 def max_number_of_items(self):
148 return max(2, self.allocation.height / self.max_text_height)
150 def _get_actual_min_size(self):
151 min_size = self.options.min_size
152 if min_size == 'auto':
153 min_size = self.tree.root.size * self.min_size_requested()
154 return int(min_size)
156 def _zoom(self, func):
157 min_size = self._get_actual_min_size()
158 self.options.min_size = func(min_size)
159 self.schedule_new_tree()
161 def zoom_fit(self):
162 self._zoom(lambda min_size: 'auto')
164 def zoom_in(self):
165 self._zoom(lambda min_size: str(int(min_size / 1.5)))
167 def zoom_out(self):
168 self._zoom(lambda min_size: str(int(min_size * 1.5)))