Update VerticalLayout/HorizontalLayout. Add layout demo. Add Size, Borders to common. Update Coords, Rect.
authorRadek Brich <radek.brich@devl.cz>
Tue, 04 Oct 2011 22:51:12 +0200
changeset 16 8791a7da6835
parent 15 c55b4749e562
child 17 5be7cc43402e
Update VerticalLayout/HorizontalLayout. Add layout demo. Add Size, Borders to common. Update Coords, Rect.
demo_layout.py
tuikit/button.py
tuikit/common.py
tuikit/container.py
tuikit/layout.py
tuikit/widget.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demo_layout.py	Tue Oct 04 22:51:12 2011 +0200
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import locale
+locale.setlocale(locale.LC_ALL, '')
+
+from tuikit import *
+
+
+class MyApplication(Application):
+    def __init__(self):
+        Application.__init__(self)
+        self.top.connect('keypress', self.globalkeypress)
+
+        #self.top.borders = (1,1,1,1)
+
+        vert = VerticalLayout(homogeneous=False)
+        self.top.layout(vert)
+        
+        self.buildrow()
+        self.buildrow(expand=True)
+        self.buildrow(expand=True, fill=True)
+        self.buildrow(homogeneous=True)
+        self.buildrow(homogeneous=True, fill=True)
+        self.buildrow(homogeneous=True, fill=True, spacing=1)
+        self.buildrow(homogeneous=True, fill=True, spacing=2)
+        
+    def buildrow(self, homogeneous=False, spacing=0, expand=False, fill=False):
+        horz = HorizontalLayout(homogeneous=homogeneous, spacing=spacing)
+        hbox1 = Container()
+        hbox1.sizereq.h = 2
+        hbox1.layout(horz)
+        self.top.add(hbox1)
+        for i in range(5):
+            btn = Button('Btn' + str(i) * i * i)
+            hbox1.add(btn, expand=expand, fill=fill)
+        
+    def globalkeypress(self, keyname, char):
+        if keyname == 'escape' or char == 'q':
+            self.terminate()
+
+
+if __name__ == '__main__':
+    app = MyApplication()
+    app.start()
+
--- a/tuikit/button.py	Sun Oct 02 23:32:35 2011 +0200
+++ b/tuikit/button.py	Tue Oct 04 22:51:12 2011 +0200
@@ -9,18 +9,24 @@
     
     def __init__(self, label=''):
         '''Create button with given label, size according to label.'''
-        Widget.__init__(self, len(label) + 4, 1)
+        w = len(label) + 4
+        h = 1
+        Widget.__init__(self, w, h)
 
         #: Button label.
         self.label = label
         self.bg = 'button'
         self.bghi = 'button-active'
         self.highlight = False
+        
+        # size
+        self.sizereq.w = w
+        self.sizereq.h = h
 
         #: Left bracket graphic character.
-        self.lbracket = '<'
+        self.lbracket = '['
         #: Right bracket graphic character.
-        self.rbracket = '>'
+        self.rbracket = ']'
 
         self.connect('draw', self.on_draw)
         self.connect('mousedown', self.on_mousedown)
--- a/tuikit/common.py	Sun Oct 02 23:32:35 2011 +0200
+++ b/tuikit/common.py	Tue Oct 04 22:51:12 2011 +0200
@@ -6,9 +6,45 @@
         self.x = x
         self.y = y
 
+    def __getitem__(self, key):
+        try:
+            tupl = (self.x, self.y)
+            return tupl[key]
+        except TypeError:
+            pass
+        return self.__dict__[key]
+    
+    def __setitem__(self, key, value):
+        if key == 0:
+            self.x = value
+        if key == 1:
+            self.y = value
 
     def __repr__(self):
-        return 'Coords(%(x)s,%(y)s)' % self.__dict__
+        return 'Coords(x={0.x},y={0.y})'.format(self)
+
+
+class Size:
+    def __init__(self, w=None, h=None):
+        self.w = w
+        self.h = h
+    
+    def __getitem__(self, key):
+        try:
+            tupl = (self.w, self.h)
+            return tupl[key]
+        except TypeError:
+            pass
+        return self.__dict__[key]
+
+    def __setitem__(self, key, value):
+        if key == 0:
+            self.w = value
+        if key == 1:
+            self.h = value
+    
+    def __repr__(self):
+        return 'Size(w={0.w},h={0.h})'.format(self)
 
 
 class Rect:
@@ -18,7 +54,25 @@
         self.w = w
         self.h = h
 
-
     def __repr__(self):
-        return 'Rect(%(x)s,%(y)s,%(w)s,%(h)s)' % self.__dict__
+        return 'Rect(x={0.x},y={0.y},w={0.w},h={0.h})'.format(self)
+
+
+class Borders:
+    def __init__(self, l=0, t=0, r=0, b=0):
+        self.l = l # left
+        self.t = t # top
+        self.r = r # right
+        self.b = b # bottom
 
+    def __getitem__(self, key):
+        try:
+            tupl = (self.l, self.t, self.r, self.b)
+            return tupl[key]
+        except TypeError:
+            pass
+        return self.__dict__[key]
+    
+    def __repr__(self):
+        return 'Borders(l={0.l},t={0.t},r={0.r},b={0.b})'.format(self)
+
--- a/tuikit/container.py	Sun Oct 02 23:32:35 2011 +0200
+++ b/tuikit/container.py	Tue Oct 04 22:51:12 2011 +0200
@@ -1,7 +1,9 @@
 # -*- coding: utf-8 -*-
 
+import logging
+
 from .widget import Widget
-import logging
+from .common import Borders
 
 
 class Container(Widget):
@@ -19,7 +21,7 @@
 
         #: Width of borders (left, top, right, bottom).
         #: Child widgets are placed within borders.
-        self.borders = (0, 0, 0, 0)
+        self.borders = Borders()
         
         self._layout = None
         
--- a/tuikit/layout.py	Sun Oct 02 23:32:35 2011 +0200
+++ b/tuikit/layout.py	Tue Oct 04 22:51:12 2011 +0200
@@ -1,6 +1,14 @@
 # -*- coding: utf-8 -*-
+'''layout module
+
+VerticalLayout
+HorizontalLayout
+TableLayout
+
+'''
 
 import logging
+import math
 
 from .common import Rect
 
@@ -16,54 +24,72 @@
         return Rect(bl, bt, c.width - bl - br, c.height - bt - bb)
 
 
-class VerticalLayout(Layout):
-    def resize(self):
-        log = logging.getLogger('tuikit')
-        log.debug('VerticalLayout: resize %d children', len(self._getchildren()))
-
-        v = 0
-        c = self.container
-        bl, bt, br, bb = c.borders
+class LinearLayout(Layout):
+    def __init__(self, homogeneous=True, spacing=0):
+        self.homogeneous = homogeneous
+        self.spacing = spacing
 
-        last = None
-        for child in self._getchildren():
-            child.x = bl
-            child.width = c.width - bl - br
-            child.y = bt + v
-            v += child.height
+    def _resize(self, ax1, ax2):
+        children = self._getchildren()
+        c = self.container
+        b1 = c.borders[ax1]
+        b2 = c.borders[ax2]
+        # available space
+        space1 = c.size[ax1] - b1 - c.borders[ax1+2]
+        space2 = c.size[ax2] - b2 - c.borders[ax2+2]
+        
+        # all space minus spacing 
+        space_to_divide = space1 - (len(children) - 1) * self.spacing
+        if not self.homogeneous:
+            # reduce by space acquired by children
+            space_to_divide -= sum([child.sizereq[ax1] for child in children])
+            # number of children with expanded hint
+            expanded_num = len([ch for ch in children if ch.hint('expand')])
+        else:
+            # all children are implicitly expanded
+            expanded_num = len(children)
+        
+        if expanded_num:
+            # reserved space for each expanded child
+            space_child = space_to_divide / expanded_num
+
+        offset = 0.
+        for child in children:
+            child.position[ax1] = b1 + int(offset)
+            child.position[ax2] = b2
+            child.size[ax1] = child.sizereq[ax1]
+            child.size[ax2] = space2
+            
+            if child.hint('expand') or self.homogeneous:
+                maxsize = int(round(space_child + math.modf(offset)[0], 2))
+                offset += space_child + self.spacing
+                if not self.homogeneous:
+                    maxsize += child.sizereq[ax1]
+                    offset += child.sizereq[ax1]
+
+                if child.hint('fill'):
+                    child.size[ax1] = maxsize
+                else:
+                    child.position[ax1] += int((maxsize - child.size[ax1])/2)
+            else:
+                offset += child.size[ax1]
+            
             child.handle('resize')
-            last = child
-            
-            log.debug('VerticalLayout: child %r to y=%d', child, child.y)
-
-        if last and v < c.height - bt - bb:
-            last.height += c.height - bt - bb - v
-
         c.redraw()
 
 
-class HorizontalLayout(Layout):
+class VerticalLayout(LinearLayout):
     def resize(self):
-        log = logging.getLogger('tuikit')
-        log.debug('HorizontalLayout: resize')
-        
-        v = 0
-        c = self.container
-        bl, bt, br, bb = c.borders
+        ax1 = 1 # primary dimension - y
+        ax2 = 0 # secondary dimension - x
+        self._resize(ax1, ax2)        
 
-        last = None
-        for child in self._getchildren():
-            child.y = bt
-            child.height = c.height - bt - bb
-            child.x = bl + v
-            v += child.width
-            child.handle('resize')
-            last = child
-        
-        if last and v < c.width - bl - br:
-            last.width += c.width - bl - br - v
-        
-        c.redraw()
+
+class HorizontalLayout(LinearLayout):
+    def resize(self):
+        ax1 = 0 # primary dimension - x
+        ax2 = 1 # secondary dimension - y
+        self._resize(ax1, ax2)
 
 
 class GridLayout(Layout):
--- a/tuikit/widget.py	Sun Oct 02 23:32:35 2011 +0200
+++ b/tuikit/widget.py	Tue Oct 04 22:51:12 2011 +0200
@@ -3,6 +3,7 @@
 import logging
 
 from .eventsource import EventSource
+from .common import Coords, Size
 
 
 class Widget(EventSource):
@@ -20,25 +21,23 @@
         self.top = None
         
         # Position inside parent widget. Modified by layout manager.
-        self.x = 0
-        self.y = 0
+        self.position = Coords()
         
         # Actual size. Modified by layout manager.
-        self._width = width
-        self._height = height
+        self.size = Size(width, height)
         
         #: Minimal size of widget. Under normal circumstances
         #: widget will never be sized smaller than this.
         #: Tuple (w, h). Both must be integers >= 1.
-        self.sizemin = (1,1)
+        self.sizemin = Size(1,1)
         
         #: Maximum size of widget. Widget will never be sized bigger than this.
         #: Tuple (w, h). Integers >= 1 or None (meaning no maximum size or infinite).
-        self.sizemax = (None, None)
+        self.sizemax = Size(None, None)
         
         #: Size request. This is default size of the widget. Will be fulfilled if possible.
         #: Tuple (w, h). Integers >= 1 or None (meaning use minumal size).
-        self.sizereq = (10,10)
+        self.sizereq = Size(10,10)
         
         #: When false, the widget is not considered in layout.
         self.allowlayout = True
@@ -69,21 +68,40 @@
 
 
     @property
+    def x(self):
+        return self.position.x
+
+    @x.setter
+    def x(self, value):
+        self.position.x = value
+        #self.emit('resize')
+
+    @property
+    def y(self):
+        return self.position.y
+
+    @y.setter
+    def y(self, value):
+        self.position.y = value
+        #self.emit('resize')
+
+
+    @property
     def width(self):
-        return self._width
+        return self.size.w
 
     @width.setter
     def width(self, value):
-        self._width = value
+        self.size.w = value
         self.emit('resize')
 
     @property
     def height(self):
-        return self._height
+        return self.size.h
 
     @height.setter
     def height(self, value):
-        self._height = value
+        self.size.h = value
         self.emit('resize')
 
 
@@ -182,6 +200,9 @@
 
     ###
 
+    def hint(self, key):
+        if key in self.hints:
+            return self.hints[key]
 
     def enclose(self, x, y):
         if self.hidden: