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