1 from ui.Widget import Widget
2 from utils.MiniXML import MiniXML
12 class Arrangement(Widget):
14 Layouter for complex arrangements of widgets.
18 EVENT_RESIZED = "event-resized"
23 # table: name -> widget
26 # table: widget -> (x1, y1, x2, y2)
27 self.__constraints = {}
29 # cache of arrangements, table: widget -> (x1, y1, x2, y2)
35 def connect_resized(self, cb, *args):
37 self._connect(self.EVENT_RESIZED, cb, *args)
55 return (_LIKE_X1, child)
61 return (_LIKE_Y1, child)
67 return (_LIKE_X2, child)
73 return (_LIKE_Y2, child)
77 def __resolve_child(self, child):
79 if (not child in self.__constraints):
82 x1, y1, x2, y2 = self.__constraints[child]
83 w, h = self.get_size()
85 if (child in self.__cache):
86 real_x1, real_y1, real_x2, real_y2 = self.__cache[child]
88 real_x1 = self.__resolve(x1, w)
89 real_y1 = self.__resolve(y1, h)
90 real_x2 = self.__resolve(x2, w)
91 real_y2 = self.__resolve(y2, h)
92 self.__cache[child] = (real_x1, real_y1, real_x2, real_y2)
94 return (real_x1, real_y1, real_x2, real_y2)
97 def __resolve(self, v, full_width):
104 return full_width + value
106 elif (vtype == _PCT):
108 return int(full_width * (value / 100.0))
110 return full_width + int(full_width * (value / 100.0))
112 elif (vtype == _LIKE_X1):
113 x1, y1, x2, y2 = self.__resolve_child(value)
116 elif (vtype == _LIKE_Y1):
117 x1, y1, x2, y2 = self.__resolve_child(value)
120 elif (vtype == _LIKE_X2):
121 x1, y1, x2, y2 = self.__resolve_child(value)
124 elif (vtype == _LIKE_Y2):
125 x1, y1, x2, y2 = self.__resolve_child(value)
129 def set_size(self, w, h):
131 prev_w, prev_h = self.get_size()
132 Widget.set_size(self, w, h)
133 if ((w, h) != (prev_w, prev_h)):
135 self.emit_event(self.EVENT_RESIZED)
138 def add(self, child, name = ""):
140 Adds a child widget. A unique name must be given when using arrangement
141 definitions in XML format.
143 @param child: child widget
144 @param name: name string
148 self.__names[name] = child
149 Widget.add(self, child)
152 def place(self, child, x1, y1, x2, y2):
154 Changes the placement of the given child widget.
156 @param child: child widget
157 @param x1: x-coordinate of top-left corner
158 @param y1: y-coordinate of top-left corner
159 @param x2: x-coordinate of bottom-right corner
160 @param y2: y-coordinate of botton-right corner
163 self.__constraints[child] = (x1, y1, x2, y2)
165 if (child in self.__cache):
166 del self.__cache[child]
169 def set_xml(self, xml):
171 Loads an arrangement from an XML arrangement definition.
173 @param xml: XML string containing the arrangement
176 dom = MiniXML(xml).get_dom()
177 self.__parse_node(dom, [], [])
180 def __parse_node(self, node, required_visibles, required_invisibles):
182 name = node.get_name()
183 if (name == "if-visible"):
184 child = self.__names[node.get_attr("name")]
185 required_visibles.append(child)
186 elif (name == "if-invisible"):
187 child = self.__names[node.get_attr("name")]
188 required_invisibles.append(child)
189 elif (name == "widget"):
190 child = self.__names[node.get_attr("name")]
191 x1 = self.__parse_value(node.get_attr("x1"))
192 y1 = self.__parse_value(node.get_attr("y1"))
193 x2 = self.__parse_value(node.get_attr("x2"))
194 y2 = self.__parse_value(node.get_attr("y2"))
196 if (self.__check_visibles(required_visibles, required_invisibles)):
197 self.place(child, x1, y1, x2, y2)
200 for c in node.get_children():
201 self.__parse_node(c, required_visibles[:], required_invisibles[:])
205 def __parse_value(self, v):
208 return self.pct(int(v[:-1]))
210 return self.px(int(v))
213 def __check_visibles(self, required_visibles, required_invisibles):
215 for v in required_visibles:
216 if (not v.is_visible()):
220 for v in required_invisibles:
228 def render_this(self):
230 for child in self.get_children():
231 x1, y1, x2, y2 = self.__resolve_child(child)
232 child.set_geometry(x1, y1, x2 - x1, y2 - y1)