Propagate "quit" event, do not just terminate application. Resize: flag widgets to be resized, do resizes only once before draw. Draw: flag widgets to be redrawn, do not draw everything on any event.
authorRadek Brich <radek.brich@devl.cz>
Sun, 03 Feb 2013 16:38:41 +0100
changeset 77 fc1989059e19
parent 76 fa5301e58eca
child 78 6031e99c8ad3
child 98 dcfb185ac866
Propagate "quit" event, do not just terminate application. Resize: flag widgets to be resized, do resizes only once before draw. Draw: flag widgets to be redrawn, do not draw everything on any event.
demo_anchorlayout.py
demo_input.py
demo_menu.py
tuikit/application.py
tuikit/checkbox.py
tuikit/container.py
tuikit/driver.py
tuikit/driver_curses.py
tuikit/editbox.py
tuikit/layout.py
tuikit/scrollview.py
tuikit/tableview.py
tuikit/treeview.py
tuikit/widget.py
tuikit/window.py
--- a/demo_anchorlayout.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/demo_anchorlayout.py	Sun Feb 03 16:38:41 2013 +0100
@@ -51,7 +51,7 @@
         align = self.win.get_hint(align_type)
         align.select_next()
         ev.originator.label = '%s: %s' % (align_type, align.selected)
-        self.top.emit('resize')
+        self.top.need_resize()
         return True
 
     def on_label_margin_draw(self, ev):
--- a/demo_input.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/demo_input.py	Sun Feb 03 16:38:41 2013 +0100
@@ -19,12 +19,12 @@
         scroll.add(self.editbox)
         self.top.add(scroll, halign='fill', valign='fill')
 
-        self.editbox.add_handler('keypress', self.on_any_input)
-        self.editbox.add_handler('mousedown', self.on_any_input)
-        self.editbox.add_handler('mouseup', self.on_any_input)
-        self.editbox.add_handler('mousewheel', self.on_any_input)
-        self.editbox.add_handler('mousemove', self.on_any_input)
-        self.editbox.add_handler('mousehover', self.on_any_input)
+        scroll.add_handler('keypress', self.on_any_input)
+        scroll.add_handler('mousedown', self.on_any_input)
+        scroll.add_handler('mouseup', self.on_any_input)
+        scroll.add_handler('mousewheel', self.on_any_input)
+        scroll.add_handler('mousemove', self.on_any_input)
+        scroll.add_handler('mousehover', self.on_any_input)
 
     def on_any_input(self, ev):
         if ev.event_name == 'keypress' and ev.keyname == 'escape':
--- a/demo_menu.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/demo_menu.py	Sun Feb 03 16:38:41 2013 +0100
@@ -11,6 +11,7 @@
     def __init__(self):
         Application.__init__(self)
         self.top.add_handler('keypress', self.on_top_keypress, last=True)
+        self.top.add_handler('quit', lambda ev: self.terminate())
 
         helpwin = Window()
         helpwin.title = 'About'
--- a/tuikit/application.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/application.py	Sun Feb 03 16:38:41 2013 +0100
@@ -64,15 +64,15 @@
         self._load_conf('tuikit.conf')
         self._setup_logging()
 
+        #: Driver class instance (render + input), e.g. DriverCurses.
+        self.driver = self.get_driver_instance(self.cfg['driver'])
+
         # Top widget
         self._top = None
         self._timer = Timer()
         self.top = top_layout()
 
-        self.quit = False
-
-        #: Driver class instance (render + input), e.g. DriverCurses.
-        self.driver = self.get_driver_instance(self.cfg['driver'])
+        self._quit = False
 
     def _load_conf(self, file_name):
         try:
@@ -111,16 +111,16 @@
 
     def terminate(self):
         '''Terminate application.'''
-        self.quit = True
+        self._quit = True
 
     def main_loop(self):
         '''The main loop.'''
         self.startup()
         self._top._size = self.driver.size  # link top widget size to screen size
-        self._top.emit('resize')
         timer = self._timer
 
-        while True:
+        while not self._quit:
+            self._top.complete_requests()
             self._top.draw(self.driver, 0, 0)
             self.driver.commit()
 
@@ -129,13 +129,7 @@
             timer.process_timeouts()
 
             for event in events:
-                if event[0] == 'quit':
-                    self.quit = True
-                else:
-                    self._top.emit(event[0], *event[1:])
-
-            if self.quit:
-                break
+                self._top.emit(event[0], *event[1:])
         self.log.info('=== quit ===')
 
     def startup(self):
@@ -153,7 +147,7 @@
         drv.defcolor('window:normal',           'lightgray on blue')
         drv.defcolor('window:controls',         'white on blue, bold')
         drv.defcolor('window:controls-active',  'cyan on blue, bold')
-        drv.defcolor('button',                  'black on white')
+        drv.defcolor('button',                  'black on lightgray')
         drv.defcolor('button-active',           'black on cyan')
         drv.defcolor('menu',                    'black on cyan')
         drv.defcolor('menu-active',             'white on cyan, bold')
--- a/tuikit/checkbox.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/checkbox.py	Sun Feb 03 16:38:41 2013 +0100
@@ -25,4 +25,5 @@
         else:
             self.checked = True
             self.prefix = '[x] '
+        self.redraw()
 
--- a/tuikit/container.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/container.py	Sun Feb 03 16:38:41 2013 +0100
@@ -26,8 +26,6 @@
         #: Child widgets are placed within borders.
         self.borders = Borders()
 
-        self._layout = None
-
         self.widthrequest = (None, None)
         self.heightrequest = (None, None)
 
@@ -60,17 +58,6 @@
             self.children.remove(child)
             self.children.append(child)
 
-    @property
-    def layout(self):
-        return self._layout
-
-    @layout.setter
-    def layout(self, value):
-        """Layout manager for placing child widgets."""
-        self._layout = value
-        self._layout.container = self
-
-
     def move_child(self, child, x, y):
         pass
 
@@ -131,6 +118,11 @@
         if self.focuschild:
             self.focuschild.emit('unfocus', ev)
 
+    def redraw(self):
+        Widget.redraw(self)
+        for child in self.children:
+            child.redraw()
+
     def draw(self, driver, x, y):
         """Draw the container and its children.
 
@@ -251,3 +243,8 @@
                     self.mousechild = child
                 return True
 
+    def complete_requests(self):
+        Widget.complete_requests(self)
+        for child in self.children:
+            child.complete_requests()
+
--- a/tuikit/driver.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/driver.py	Sun Feb 03 16:38:41 2013 +0100
@@ -41,6 +41,11 @@
         for i in range(h):
             self.putch(x, y+i, c)
 
+    def fill_clip(self, c=' '):
+        """Fill current clip region."""
+        rect = self.clipstack.top()
+        self.fill(rect.x, rect.y, rect.w, rect.h, c)
+
     def fill(self, x, y, w, h, c=' '):
         '''Fill rectangular area.'''
         for i in range(h):
--- a/tuikit/driver_curses.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/driver_curses.py	Sun Feb 03 16:38:41 2013 +0100
@@ -195,9 +195,10 @@
     ## cursor ##
 
     def showcursor(self, x, y):
-        if not self.clipstack.test(x, y):
-            return
-        self.cursor = (y, x)
+        if self.clipstack.test(x, y):
+            self.cursor = (y, x)
+        else:
+            self.cursor = None
 
     def hidecursor(self):
         curses.curs_set(False)
--- a/tuikit/editbox.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/editbox.py	Sun Feb 03 16:38:41 2013 +0100
@@ -49,6 +49,7 @@
 
     def on_draw(self, ev):
         ev.driver.pushcolor('normal')
+        ev.driver.fill_clip()
         end_y = min(len(self.lines), ev.exposed.y + ev.exposed.h)
         for j in range(ev.exposed.y, end_y):
             line = self.lines[j]
@@ -83,6 +84,7 @@
         y = ev.wy
         x = min(ev.wx, len(self.lines[y]))
         self._spot.update(x=x, y=y)
+        self.redraw()
 
     def on_mousewheel(self, ev):
         if ev.button == 4:
--- a/tuikit/layout.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/layout.py	Sun Feb 03 16:38:41 2013 +0100
@@ -21,8 +21,11 @@
         widget.add_handler('show', self._on_child_size_change)
         widget.add_handler('hide', self._on_child_size_change)
 
+    def on_resize(self, ev):
+        self.redraw()
+
     def _on_child_size_change(self, ev):
-        self.emit('resize')
+        self.need_resize()
 
     def _get_children(self):
         return [child for child in self.children
@@ -225,6 +228,7 @@
         for child in self.children:
             x, y = child.hint_value('position')
             child._pos.update(x=x+ox, y=y+oy)
+        self.redraw()
 
 
 class GridLayout(Layout):
--- a/tuikit/scrollview.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/scrollview.py	Sun Feb 03 16:38:41 2013 +0100
@@ -15,6 +15,9 @@
     def add(self, widget, **kwargs):
         self._inner.add(widget, **kwargs)
 
+    def on_draw(self, ev):
+        ev.driver.fill_clip()
+
 
 class Scrolling:
     def __init__(self):
@@ -94,7 +97,7 @@
             self.vscroll.hide()
         else:
             self.vscroll.show()
-        self._inner.need_resize()
+        # self._inner.need_resize()
 
 
 class ScrollView(OffsetView, Scrolling):
--- a/tuikit/tableview.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/tableview.py	Sun Feb 03 16:38:41 2013 +0100
@@ -161,6 +161,7 @@
 
     def on_draw(self, ev):
         ev.driver.pushcolor('normal')
+        ev.driver.fill_clip()
         self.rowcount = self.model.getcount()
         numrows = min(self.rowcount - self.offset.y, self.height - self.headsize)
         rows = self.model.getrows(self.offset.y, self.offset.y + numrows)
@@ -185,8 +186,8 @@
             'pagedown':  self.move_pagedown}
         if ev.keyname in key_map:
             key_map[ev.keyname]()
+            self.redraw()
             return True
-        self.redraw()
 
     def set_yofs(self, yofs):
         if yofs > self.rowcount - (self.height - self.headsize):
--- a/tuikit/treeview.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/treeview.py	Sun Feb 03 16:38:41 2013 +0100
@@ -251,6 +251,7 @@
 
     def on_draw(self, ev):
         ev.driver.pushcolor('normal')
+        ev.driver.fill_clip()
 
         lines = 0  # bit array, bit 0 - draw vertical line on first column, etc.
         y = ev.y
@@ -292,8 +293,8 @@
             'right': self.move_right}
         if ev.keyname in key_map:
             key_map[ev.keyname]()
+            self.redraw()
             return True
-        self.redraw()
 
     def prev_node(self, node):
         # previous sibling
--- a/tuikit/widget.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/widget.py	Sun Feb 03 16:38:41 2013 +0100
@@ -28,7 +28,6 @@
         self._pos = Coords()
         #: Actual size. Modified only by layout manager.
         self._size = Size(10, 10)
-        self._size.add_handler('change', lambda ev: self.emit('resize'))
         #: Size of visible part of widget. Used in OffsetLayout. Modified only by layout manager.
         self._view_size = Size(10, 10)
         #: Default (natural) size of Widget.
@@ -73,6 +72,7 @@
         self.add_events(
             'resize', Event,
             'draw', DrawEvent,
+            'quit', Event,
             'keypress', KeyboardEvent,
             'mousedown', MouseEvent,
             'mouseup', MouseEvent,
@@ -111,6 +111,14 @@
     def height(self):
         return self._size.h
 
+    @property
+    def view_width(self):
+        return self._view_size.w
+
+    @property
+    def view_height(self):
+        return self._view_size.h
+
     def resize(self, w=None, h=None):
         """Set size request.
 
@@ -120,14 +128,6 @@
         self._sizereq.update(w, h)
 
     @property
-    def view_width(self):
-        return self._view_size.w
-
-    @property
-    def view_height(self):
-        return self._view_size.h
-
-    @property
     def sizereq(self):
         """Size request.
 
@@ -144,6 +144,16 @@
             logging.getLogger('tuikit').info('xy')
             self._size.update(self._sizereq)
 
+    def need_resize(self):
+        self._need_resize = True
+
+    def on_resize(self, ev):
+        self._need_resize = False
+
+    def complete_requests(self):
+        if self._need_resize:
+            self.emit('resize')
+
     ### misc
 
     @property
@@ -170,6 +180,8 @@
         """Real setter for top. Allows override."""
         self._top = value
 
+    ### hints
+
     def reset_hints(self):
         """Reset all hints to their initial value.
 
@@ -205,13 +217,8 @@
 
     ### events
 
-    def need_resize(self):
-        self._need_resize = True
-
-    def redraw(self, parent=False):
+    def redraw(self):
         self._need_draw = True
-        if parent and self.parent:
-            self.parent._redraw = True
 
     def draw(self, driver, x, y):
         """Draw the widget.
@@ -220,7 +227,7 @@
         use on_draw method instead.
 
         """
-        if self.hidden:
+        if self.hidden or not self._need_draw:
             return True
 
         driver.clipstack.push(x, y, self.width, self.height)
@@ -233,6 +240,7 @@
                 driver.showcursor(x + cx, y + cy)
             else:
                 driver.hidecursor()
+        self._need_draw = False
 
     def on_mousedown(self, ev):
         self.grab_focus()
@@ -240,11 +248,9 @@
 
     ### focus
 
-
     def can_focus(self):
         return not self.hidden and self.allow_focus
 
-
     def has_focus(self):
         if self.parent is None:
             return True
@@ -271,7 +277,9 @@
         self.parent.focuschild = self
         if oldfocuschild:
             oldfocuschild.emit('unfocus', new=self)
+            oldfocuschild.redraw()
         self.emit('focus', old=oldfocuschild)
+        self.redraw()
 
     def grab_focus(self):
         """Focus the widget and its parents."""
@@ -314,6 +322,8 @@
             self._hidden = True
             self.emit('hide')
             self.redraw()
+            if self.floater and self.top:
+                self.top.redraw()
 
     def show(self):
         '''Show widget. Convenience method.'''
--- a/tuikit/window.py	Sat Feb 02 12:54:27 2013 +0100
+++ b/tuikit/window.py	Sun Feb 03 16:38:41 2013 +0100
@@ -40,6 +40,7 @@
         self.colorprefix = 'window:'
 
         self._inner = inner_layout()
+        self._inner.add_handler('draw', self._on_inner_draw)
         Container.add(self, self._inner, halign='fill', valign='fill', margin=Borders(1,1,1,1))
 
     def add(self, widget, **kwargs):
@@ -55,7 +56,6 @@
         self._closebutton = value
         self.closebtn.hidden = not value
 
-
     def on_draw(self, ev):
         ev.driver.pushcolor('normal')
         ev.driver.frame(ev.x, ev.y, self.width, self.height)
@@ -84,23 +84,30 @@
         ev.driver.fill(ev.x+1, ev.y+1, self.width-2, self.height-2)
         ev.driver.popcolor()
 
+    def _on_inner_draw(self, ev):
+        ev.driver.pushcolor('normal')
+        ev.driver.fill_clip()
+        ev.driver.popcolor()
+
+    def on_mousedown(self, ev):
+        self.bring_up()
 
     def after_mousedown(self, ev):
         if self.resizable and ev.wx >= self.width - 1 and ev.wy >= self.height - 1:
             self._resizing = True
         elif self.movable:
             self._moving = True
-        self.redraw(True)
+        self.parent.redraw()
 
     def after_mouseup(self, ev):
         self._resize_or_move(ev)
         self._resizing = False
         self._moving = False
-        self.redraw(True)
+        self.parent.redraw()
 
     def after_mousemove(self, ev):
         self._resize_or_move(ev)
-        self.redraw(True)
+        self.parent.redraw()
 
     def _resize_or_move(self, ev):
         if self._resizing: