Mercurial > fife-parpg
comparison engine/extensions/pychan/widgets/layout.py @ 249:1cc51d145af9
Further split up the containers.py; bugfix.
author | phoku@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Thu, 26 Mar 2009 16:36:21 +0000 |
parents | |
children | 51cc05d862f2 |
comparison
equal
deleted
inserted
replaced
248:a2d5e2721489 | 249:1cc51d145af9 |
---|---|
1 # -*- coding: utf-8 -*- | |
2 | |
3 from common import * | |
4 | |
5 class LayoutBase(object): | |
6 """ | |
7 This class is at the core of the layout engine. The two MixIn classes L{VBoxLayoutMixin} | |
8 and L{HBoxLayoutMixin} specialise on this by reimplementing the C{resizeToContent} and | |
9 the C{expandContent} methods. | |
10 | |
11 Dynamic Layouting | |
12 ----------------- | |
13 | |
14 The layout is calculated in the L{Widget.show} method. Thus if you modify the layout, | |
15 by adding or removing child widgets for example, you have to call L{Widget.adaptLayout} | |
16 so that the changes ripple through the widget hierachy. | |
17 | |
18 Internals | |
19 --------- | |
20 | |
21 At the core the layout engine works in two passes: | |
22 | |
23 Before a root widget loaded by the XML code is shown, its resizeToContent method | |
24 is called recursively (walking the widget containment relation in post order). | |
25 This shrinks all HBoxes and VBoxes to their minimum heigt and width. | |
26 After that the expandContent method is called recursively in the same order, | |
27 which will re-align the widgets if there is space left AND if a Spacer is contained. | |
28 | |
29 Inside bare Container instances (without a Layout MixIn) absolute positioning | |
30 can be used. | |
31 """ | |
32 def __init__(self,align = (AlignLeft,AlignTop), **kwargs): | |
33 self.align = align | |
34 self.spacer = None | |
35 super(LayoutBase,self).__init__(**kwargs) | |
36 | |
37 def addSpacer(self,spacer): | |
38 if self.spacer: | |
39 raise RuntimeException("Already a Spacer in %s!" % str(self)) | |
40 self.spacer = spacer | |
41 spacer.index = len(self.children) | |
42 | |
43 def xdelta(self,widget):return 0 | |
44 def ydelta(self,widget):return 0 | |
45 | |
46 def _adjustHeight(self): | |
47 if self.align[1] == AlignTop:return #dy = 0 | |
48 if self.align[1] == AlignBottom: | |
49 y = self.height - self.childarea[1] - self.border_size - self.margins[1] | |
50 else: | |
51 y = (self.height - self.childarea[1] - self.border_size - self.margins[1])/2 | |
52 for widget in self.children: | |
53 widget.y = y | |
54 y += self.ydelta(widget) | |
55 | |
56 def _adjustHeightWithSpacer(self): | |
57 pass | |
58 | |
59 def _adjustWidth(self): | |
60 if self.align[0] == AlignLeft:return #dx = 0 | |
61 if self.align[0] == AlignRight: | |
62 x = self.width - self.childarea[0] - self.border_size - self.margins[0] | |
63 else: | |
64 x = (self.width - self.childarea[0] - self.border_size - self.margins[0])/2 | |
65 for widget in self.children: | |
66 widget.x = x | |
67 x += self.xdelta(widget) | |
68 | |
69 def _expandWidthSpacer(self): | |
70 x = self.border_size + self.margins[0] | |
71 xdelta = map(self.xdelta,self.children) | |
72 | |
73 for widget in self.children[:self.spacer.index]: | |
74 widget.x = x | |
75 x += xdelta.pop(0) | |
76 | |
77 x = self.width - sum(xdelta) - self.border_size - self.margins[0] | |
78 for widget in self.children[self.spacer.index:]: | |
79 widget.x = x | |
80 x += xdelta.pop(0) | |
81 | |
82 def _expandHeightSpacer(self): | |
83 y = self.border_size + self.margins[1] | |
84 ydelta = map(self.ydelta,self.children) | |
85 | |
86 for widget in self.children[:self.spacer.index]: | |
87 widget.y = y | |
88 y += ydelta.pop(0) | |
89 | |
90 y = self.height - sum(ydelta) - self.border_size - self.margins[1] | |
91 for widget in self.children[self.spacer.index:]: | |
92 widget.y = y | |
93 y += ydelta.pop(0) | |
94 | |
95 | |
96 class VBoxLayoutMixin(LayoutBase): | |
97 """ | |
98 A mixin class for a vertical layout. Do not use directly. | |
99 """ | |
100 def __init__(self,**kwargs): | |
101 super(VBoxLayoutMixin,self).__init__(**kwargs) | |
102 | |
103 def resizeToContent(self, recurse = True): | |
104 max_w = self.getMaxChildrenWidth() | |
105 x = self.margins[0] + self.border_size | |
106 y = self.margins[1] + self.border_size | |
107 for widget in self.children: | |
108 widget.x = x | |
109 widget.y = y | |
110 widget.width = max_w | |
111 y += widget.height + self.padding | |
112 | |
113 #Add the padding for the spacer. | |
114 if self.spacer: | |
115 y += self.padding | |
116 | |
117 self.height = y + self.margins[1] - self.padding | |
118 self.width = max_w + 2*x | |
119 self.childarea = max_w, y - self.padding - self.margins[1] | |
120 | |
121 self._adjustHeight() | |
122 self._adjustWidth() | |
123 | |
124 def expandContent(self): | |
125 if self.spacer: | |
126 self._expandHeightSpacer() | |
127 | |
128 def ydelta(self,widget):return widget.height + self.padding | |
129 | |
130 class HBoxLayoutMixin(LayoutBase): | |
131 """ | |
132 A mixin class for a horizontal layout. Do not use directly. | |
133 """ | |
134 def __init__(self,**kwargs): | |
135 super(HBoxLayoutMixin,self).__init__(**kwargs) | |
136 | |
137 def resizeToContent(self, recurse = True): | |
138 max_h = self.getMaxChildrenHeight() | |
139 x = self.margins[0] + self.border_size | |
140 y = self.margins[1] + self.border_size | |
141 for widget in self.children: | |
142 widget.x = x | |
143 widget.y = y | |
144 widget.height = max_h | |
145 x += widget.width + self.padding | |
146 | |
147 #Add the padding for the spacer. | |
148 if self.spacer: | |
149 x += self.padding | |
150 | |
151 self.width = x + self.margins[0] - self.padding | |
152 self.height = max_h + 2*y | |
153 self.childarea = x - self.margins[0] - self.padding, max_h | |
154 | |
155 self._adjustHeight() | |
156 self._adjustWidth() | |
157 | |
158 def expandContent(self): | |
159 if self.spacer: | |
160 self._expandWidthSpacer() | |
161 | |
162 def xdelta(self,widget):return widget.width + self.padding |