Mercurial > fife-parpg
diff engine/extensions/pychan/widgets/layout.py @ 255:51cc05d862f2
Merged editor_rewrite branch to trunk.
This contains changes that may break compatibility against existing clients.
For a list of changes that may affect your client, see: http://wiki.fifengine.de/Changes_to_pychan_and_FIFE_in_editor_rewrite_branch
author | cheesesucker@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Mon, 08 Jun 2009 16:00:02 +0000 |
parents | 1cc51d145af9 |
children | d8bcff5f7222 |
line wrap: on
line diff
--- a/engine/extensions/pychan/widgets/layout.py Wed Jun 03 19:29:52 2009 +0000 +++ b/engine/extensions/pychan/widgets/layout.py Mon Jun 08 16:00:02 2009 +0000 @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- -from common import * +from pychan.attrs import IntAttr + +AlignTop, AlignBottom, AlignLeft, AlignRight, AlignCenter = range(5) +def isLayouted(widget): + return isinstance(widget,LayoutBase) class LayoutBase(object): """ @@ -12,7 +16,7 @@ ----------------- The layout is calculated in the L{Widget.show} method. Thus if you modify the layout, - by adding or removing child widgets for example, you have to call L{Widget.adaptLayout} + by adding or removing child widgets for example, you have to call L{widgets.Widget.adaptLayout} so that the changes ripple through the widget hierachy. Internals @@ -31,67 +35,118 @@ """ def __init__(self,align = (AlignLeft,AlignTop), **kwargs): self.align = align - self.spacer = None + self.spacer = [] super(LayoutBase,self).__init__(**kwargs) def addSpacer(self,spacer): - if self.spacer: - raise RuntimeException("Already a Spacer in %s!" % str(self)) - self.spacer = spacer + self.spacer.append(spacer) spacer.index = len(self.children) def xdelta(self,widget):return 0 def ydelta(self,widget):return 0 - def _adjustHeight(self): - if self.align[1] == AlignTop:return #dy = 0 - if self.align[1] == AlignBottom: - y = self.height - self.childarea[1] - self.border_size - self.margins[1] - else: - y = (self.height - self.childarea[1] - self.border_size - self.margins[1])/2 - for widget in self.children: - widget.y = y - y += self.ydelta(widget) + def _applyHeight(self, spacers = []): + y = self.border_size + self.margins[1] + ydelta = map(self.ydelta,self.children) + for index, child in enumerate(self.children): + while spacers and spacers[0].index == index: + y += spacers.pop(0).size + child.y = y + y += ydelta.pop(0) def _adjustHeightWithSpacer(self): pass - def _adjustWidth(self): - if self.align[0] == AlignLeft:return #dx = 0 - if self.align[0] == AlignRight: - x = self.width - self.childarea[0] - self.border_size - self.margins[0] - else: - x = (self.width - self.childarea[0] - self.border_size - self.margins[0])/2 - for widget in self.children: - widget.x = x - x += self.xdelta(widget) - - def _expandWidthSpacer(self): + def _applyWidth(self, spacers = []): x = self.border_size + self.margins[0] xdelta = map(self.xdelta,self.children) - - for widget in self.children[:self.spacer.index]: - widget.x = x + for index, child in enumerate(self.children): + while spacers and spacers[0].index == index: + x += spacers.pop(0).size + child.x = x x += xdelta.pop(0) - x = self.width - sum(xdelta) - self.border_size - self.margins[0] - for widget in self.children[self.spacer.index:]: - widget.x = x - x += xdelta.pop(0) + def _expandWidthSpacer(self): + xdelta = map(self.xdelta,self.children) + xdelta += [spacer.min_size for spacer in self.spacer] + + available_space = self.width - 2*self.margins[0] - 2*self.border_size - self._extra_border[0] + + used_space = sum(xdelta) + if self.children: + used_space -= self.padding + if used_space >= available_space: + return + + expandable_items = self._getExpanders(vertical=False) + #print "AS/US - before",self,[o.width for o in expandable_items] + #print "SPACERS",self.spacer + + index = 0 + while used_space < available_space and expandable_items: + index = index % len(expandable_items) + + expander = expandable_items[index] + old_width = expander.width + expander.width += 1 + if old_width == expander.width: + expandable_items.pop(index) + else: + used_space += 1 + index += 1 + + #print "AS/US - after",self,[o.width for o in expandable_items] + #print "SPACERS",self.spacer + self._applyWidth(spacers = self.spacer[:]) def _expandHeightSpacer(self): - y = self.border_size + self.margins[1] ydelta = map(self.ydelta,self.children) + ydelta += [spacer.min_size for spacer in self.spacer] + + available_space = self.height - 2*self.margins[1] - 2*self.border_size - self._extra_border[1] + + used_space = sum(ydelta) + if self.children: + used_space -= self.padding + + if used_space >= available_space: + return + + expandable_items = self._getExpanders(vertical=True) + #print "AS/US - before",self,[o.height for o in expandable_items] + + index = 0 + while used_space < available_space and expandable_items: + index = index % len(expandable_items) - for widget in self.children[:self.spacer.index]: - widget.y = y - y += ydelta.pop(0) + expander = expandable_items[index] + old_width = expander.height + expander.height += 1 + if old_width == expander.height: + expandable_items.pop(index) + else: + used_space += 1 + index += 1 + + #print "AS/US - after",self,[o.height for o in expandable_items] + self._applyHeight(spacers = self.spacer[:]) + - y = self.height - sum(ydelta) - self.border_size - self.margins[1] - for widget in self.children[self.spacer.index:]: - widget.y = y - y += ydelta.pop(0) + def _getExpanders(self,vertical=True): + expanders = [] + spacers = self.spacer[:] + for index, child in enumerate(self.children): + if spacers and spacers[0].index == index: + expanders.append( spacers.pop(0) ) + if child.vexpand and vertical: + expanders += [child]*child.vexpand + if child.hexpand and not vertical: + expanders += [child]*child.hexpand + return expanders + spacers + def _resetSpacers(self): + for spacer in self.spacer: + spacer.size = 0 class VBoxLayoutMixin(LayoutBase): """ @@ -101,29 +156,32 @@ super(VBoxLayoutMixin,self).__init__(**kwargs) def resizeToContent(self, recurse = True): + self._resetSpacers() + max_w = self.getMaxChildrenWidth() x = self.margins[0] + self.border_size y = self.margins[1] + self.border_size for widget in self.children: - widget.x = x - widget.y = y widget.width = max_w y += widget.height + self.padding - #Add the padding for the spacer. - if self.spacer: - y += self.padding + if self.children: + y -= self.padding + + y += sum([spacer.min_size for spacer in self.spacer]) - self.height = y + self.margins[1] - self.padding - self.width = max_w + 2*x - self.childarea = max_w, y - self.padding - self.margins[1] + self.height = y + self.margins[1] + self.border_size + self._extra_border[1] + self.width = max_w + 2*x + self._extra_border[0] - self._adjustHeight() - self._adjustWidth() + self._applyHeight(spacers = self.spacer[:]) + self._applyWidth() def expandContent(self): - if self.spacer: - self._expandHeightSpacer() + self._expandHeightSpacer() + if not self.hexpand and self.parent:return + for widget in self.children: + widget.width = self.width - 2*self.margins[0] - 2*self.border_size - self._extra_border[0] + def ydelta(self,widget):return widget.height + self.padding @@ -135,28 +193,85 @@ super(HBoxLayoutMixin,self).__init__(**kwargs) def resizeToContent(self, recurse = True): + self._resetSpacers() + max_h = self.getMaxChildrenHeight() x = self.margins[0] + self.border_size y = self.margins[1] + self.border_size for widget in self.children: - widget.x = x - widget.y = y widget.height = max_h x += widget.width + self.padding - - #Add the padding for the spacer. - if self.spacer: - x += self.padding + if self.children: + x -= self.padding + x += sum([spacer.min_size for spacer in self.spacer]) - self.width = x + self.margins[0] - self.padding - self.height = max_h + 2*y - self.childarea = x - self.margins[0] - self.padding, max_h + self.width = x + self.margins[0] + self._extra_border[0] + self.height = max_h + 2*y + self._extra_border[1] - self._adjustHeight() - self._adjustWidth() + self._applyHeight() + self._applyWidth(spacers = self.spacer[:]) def expandContent(self): - if self.spacer: - self._expandWidthSpacer() + self._expandWidthSpacer() + if not self.vexpand and self.parent:return + for widget in self.children: + widget.height = self.height - 2*self.margins[1] - 2*self.border_size - self._extra_border[1] def xdelta(self,widget):return widget.width + self.padding + +class Spacer(object): + """ A spacer represents expandable or fixed 'whitespace' in the GUI. + + In a XML file you can get this by adding a <Spacer /> inside a VBox or + HBox element (Windows implicitly are VBox elements). + + Attributes + ---------- + + As with widgets a number of attributes can be set on a spacer (inside the XML definition). + + - min_size: Int: The minimal size this Spacer is allowed to have. + - max_size: Int: The maximal size this Spacer is allowed to have. + - fixed_size: Int: Set min_size and max_size to the same vale - effectively a Fixed size spacer. + + """ + + ATTRIBUTES = [ + IntAttr('min_size'), IntAttr('size'), IntAttr('max_size'), + IntAttr('fixed_size'), + ] + + def __init__(self,parent=None,**kwargs): + self.parent = parent + self.min_size = 0 + self.max_size = 1000 + self.size = 0 + + def __str__(self): + return "Spacer(parent.name='%s')" % getattr(self.__parent,'name','None') + + def __repr__(self): + return "<Spacer(parent.name='%s') at %x>" % (getattr(self.__parent,'name','None'),id(self)) + + def _getSize(self): + self.size = self._size + return self._size + def _setSize(self,size): + self._size = max(self.min_size, min(self.max_size,size)) + size = property(_getSize,_setSize) + + # Alias for size + width = property(_getSize,_setSize) + height = property(_getSize,_setSize) + + def _setFixedSize(self,size): + self.min_size = self.max_size = size + self.size = size + fixed_size = property(fset=_setFixedSize) + + def _isExpanding(self): + if self.min_size < self.max_size: + return 1 + return 0 + vexpand = property(_isExpanding) + hexpand = property(_isExpanding)