Add Theme, Button.
authorRadek Brich <radek.brich@devl.cz>
Wed, 19 Mar 2014 00:32:38 +0100
changeset 89 94f5baef19ac
parent 88 90d00354dc70
child 90 781774a8d568
Add Theme, Button.
demos/03_application.py
tuikit/core/application.py
tuikit/core/buffer.py
tuikit/core/container.py
tuikit/core/theme.py
tuikit/core/unigraph.py
tuikit/core/widget.py
tuikit/core/window.py
tuikit/widgets/button.py
tuikit/widgets/label.py
--- a/demos/03_application.py	Tue Mar 18 22:39:21 2014 +0100
+++ b/demos/03_application.py	Wed Mar 19 00:32:38 2014 +0100
@@ -5,10 +5,15 @@
 
 from tuikit.core.application import Application
 from tuikit.widgets.label import Label
+from tuikit.widgets.button import Button
 
 label = Label('Hello there!')
 label.pos.update(20, 10)
 
+button = Button()
+button.pos.update(20, 20)
+
 app = Application()
 app.root_window.add(label)
+app.root_window.add(button)
 app.start()
--- a/tuikit/core/application.py	Tue Mar 18 22:39:21 2014 +0100
+++ b/tuikit/core/application.py	Wed Mar 19 00:32:38 2014 +0100
@@ -1,4 +1,5 @@
 from tuikit.core.window import WindowManager, Window
+from tuikit.core.theme import default_theme
 
 import logging
 
@@ -24,10 +25,11 @@
         self._started = False
         self._quit = False
         # find and initialize driver
-        self.use_driver(driver)
+        self.set_driver(driver)
+        self.set_theme(default_theme)
         self.window_manager.add(self.root_window)
 
-    def use_driver(self, driver_name):
+    def set_driver(self, driver_name):
         """Select driver to be used for rendering and input.
 
         `driver_name` should be one of: 'base', 'curses', 'sdl'
@@ -38,6 +40,9 @@
         module = __import__('tuikit.driver.' + driver_name, fromlist=['driver_class'])
         self.driver = module.driver_class()
 
+    def set_theme(self, theme):
+        self.window_manager.set_theme(theme)
+
     def start(self):
         """Start application. Runs main loop."""
         self.log.info('=== start ===')
--- a/tuikit/core/buffer.py	Tue Mar 18 22:39:21 2014 +0100
+++ b/tuikit/core/buffer.py	Wed Mar 19 00:32:38 2014 +0100
@@ -1,6 +1,8 @@
 from tuikit.core.signal import Signal
 from tuikit.core.coords import Size
-from tuikit.core.unigraph import unigraph_default
+from tuikit.core.theme import default_theme
+
+from contextlib import contextmanager
 
 
 class Cell:
@@ -58,7 +60,7 @@
         for i in range(h):
             self.hline(x, y + i, w, c)
 
-    def frame(self, x=0, y=0, w=0, h=0, style=unigraph_default):
+    def frame(self, x=0, y=0, w=0, h=0, theme=default_theme):
         """Draw rectangular frame.
 
         Frame whole buffer if width or height is not specified (zero).
@@ -67,14 +69,22 @@
         """
         w = self.size.w if not w else w
         h = self.size.h if not h else h
-        self.putch(x,      y,      style.frame_ulcorner)
-        self.putch(x+w-1,  y,      style.frame_urcorner)
-        self.putch(x,      y+h-1,  style.frame_llcorner)
-        self.putch(x+w-1,  y+h-1,  style.frame_lrcorner)
-        self.hline(x+1,    y,      w-2,  style.frame_hline)
-        self.hline(x+1,    y+h-1,  w-2,  style.frame_hline)
-        self.vline(x,      y+1,    h-2,  style.frame_vline)
-        self.vline(x+w-1,  y+1,    h-2,  style.frame_vline)
+        self.putch(x,      y,      theme.frame_ulcorner)
+        self.putch(x+w-1,  y,      theme.frame_urcorner)
+        self.putch(x,      y+h-1,  theme.frame_llcorner)
+        self.putch(x+w-1,  y+h-1,  theme.frame_lrcorner)
+        self.hline(x+1,    y,      w-2,  theme.frame_hline)
+        self.hline(x+1,    y+h-1,  w-2,  theme.frame_hline)
+        self.vline(x,      y+1,    h-2,  theme.frame_vline)
+        self.vline(x+w-1,  y+1,    h-2,  theme.frame_vline)
+
+    @contextmanager
+    def attr(self, attr_desc):
+        """Set attribute for block of commands, then restore previous attribute."""
+        original_attr = self.getattr()
+        self.setattr(attr_desc)
+        yield
+        self.setattr(original_attr)
 
 
 class Buffer(BufferOperationsMixin):
@@ -118,6 +128,10 @@
         cell = self._data[y * self._size.w + x]
         return cell.char, self._attr_descs[cell.attr]
 
+    def getattr(self):
+        """Get current attribute."""
+        return self._attr_descs[self._current_attr]
+
     def setattr(self, attr_desc):
         """Set attribute to be used for subsequent draw operations."""
         if attr_desc in self._attr_map:
@@ -159,6 +173,10 @@
         """Get cell data at `x`, `y` coords."""
         return self.target.get(x, y)
 
+    def getattr(self):
+        """Get current attribute."""
+        return self.target.getattr()
+
     def setattr(self, attr_desc):
         """Set attribute to be used for subsequent draw operations."""
         self.target.setattr(attr_desc)
--- a/tuikit/core/container.py	Tue Mar 18 22:39:21 2014 +0100
+++ b/tuikit/core/container.py	Wed Mar 19 00:32:38 2014 +0100
@@ -1,3 +1,6 @@
+from tuikit.core.theme import default_theme
+
+
 class Container:
 
     """Container for widgets."""
@@ -5,12 +8,19 @@
     def __init__(self):
         #: List of child widgets.
         self.children = []
+        self.theme = default_theme
 
     def add(self, widget, **kwargs):
         """Add widget into container."""
         self.children.append(widget)
         widget.parent = self
         widget.window = self.window if hasattr(self, 'window') else self
+        widget.set_theme(self.theme)
+
+    def set_theme(self, theme):
+        self.theme = theme
+        for child in self.children:
+            child.set_theme(theme)
 
     def draw(self, buffer, x=0, y=0):
         """Draw child widgets."""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tuikit/core/theme.py	Wed Mar 19 00:32:38 2014 +0100
@@ -0,0 +1,43 @@
+from tuikit.core.unigraph import unigraph_default as unigraph
+
+
+class ColorTheme:
+
+    """Default color style"""
+
+    normal = 'lightgray'
+    button = 'black on lightgray'
+    button_active = 'black on cyan'
+
+
+class GraphicalTheme:
+
+    """Default graphical symbols"""
+
+    # Frame
+    frame_hline = unigraph.HLINE
+    frame_vline = unigraph.VLINE
+    frame_ulcorner = unigraph.ULCORNER
+    frame_urcorner = unigraph.URCORNER
+    frame_llcorner = unigraph.LLCORNER
+    frame_lrcorner = unigraph.LRCORNER
+    frame_ltee = unigraph.LTEE
+    frame_rtee = unigraph.RTEE
+
+    # Scrollbar
+    sb_thumb = unigraph.CIRCLE
+    sb_htrack = unigraph.DOTTED_HLINE
+    sb_vtrack = unigraph.DOTTED_VLINE
+    sb_left = unigraph.LEFT_ARROW
+    sb_right = unigraph.RIGHT_ARROW
+    sb_up = unigraph.UP_ARROW
+    sb_down = unigraph.DOWN_ARROW
+
+
+class Theme(ColorTheme, GraphicalTheme):
+
+    def __getitem__(self, name):
+        return getattr(self, name)
+
+
+default_theme = Theme()
--- a/tuikit/core/unigraph.py	Tue Mar 18 22:39:21 2014 +0100
+++ b/tuikit/core/unigraph.py	Wed Mar 19 00:32:38 2014 +0100
@@ -47,25 +47,6 @@
     ROUND_LLCORNER = '╰'
     ROUND_LRCORNER = '╯'
 
-    ### Default style
-    # frame
-    frame_hline = HLINE
-    frame_vline = VLINE
-    frame_ulcorner = ULCORNER
-    frame_urcorner = URCORNER
-    frame_llcorner = LLCORNER
-    frame_lrcorner = LRCORNER
-    frame_ltee = LTEE
-    frame_rtee = RTEE
-    # scrollbar
-    sb_thumb = CIRCLE
-    sb_htrack = DOTTED_HLINE
-    sb_vtrack = DOTTED_VLINE
-    sb_left = LEFT_ARROW
-    sb_right = RIGHT_ARROW
-    sb_up = UP_ARROW
-    sb_down = DOWN_ARROW
-
     def __getitem__(self, name):
         return getattr(self, name)
 
--- a/tuikit/core/widget.py	Tue Mar 18 22:39:21 2014 +0100
+++ b/tuikit/core/widget.py	Wed Mar 19 00:32:38 2014 +0100
@@ -1,4 +1,5 @@
 from tuikit.core.coords import Point, Size
+from tuikit.core.theme import default_theme
 
 
 class Widget:
@@ -18,6 +19,8 @@
         self.parent = None
         #: Window owning this Widget
         self.window = None
+        #: Theme
+        self.theme = default_theme
 
         ### placing and size
         #: Position inside parent widget. Modified by layout manager.
@@ -54,3 +57,6 @@
 
     def draw(self, buffer, x, y):
         pass
+
+    def set_theme(self, theme):
+        self.theme = theme
--- a/tuikit/core/window.py	Tue Mar 18 22:39:21 2014 +0100
+++ b/tuikit/core/window.py	Wed Mar 19 00:32:38 2014 +0100
@@ -2,6 +2,7 @@
 from tuikit.core.signal import Signal
 from tuikit.core.container import Container
 from tuikit.core.coords import Point
+from tuikit.core.theme import default_theme
 
 
 class Window(Container):
@@ -64,11 +65,18 @@
 
 class WindowManager:
 
-    def __init__(self):
+    def __init__(self, theme=default_theme):
         self.windows = []
+        self.theme = theme
 
     def add(self, window):
         self.windows.append(window)
+        window.set_theme(self.theme)
+
+    def set_theme(self, theme):
+        self.theme = theme
+        for window in self.windows:
+            window.set_theme(theme)
 
     def resize(self, w, h):
         self.windows[0].resize(w, h)
@@ -79,6 +87,8 @@
                         x + window.x,
                         y + window.y)
 
+#    def keypress(self, keyname, char, mod=0):
+
     def handle_event(self, event_name, *args):
         """Handle input event to managed windows."""
         handler = getattr(self, event_name, None)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tuikit/widgets/button.py	Wed Mar 19 00:32:38 2014 +0100
@@ -0,0 +1,97 @@
+from tuikit.core.widget import Widget
+from tuikit.core.signal import Signal
+
+
+class Button(Widget):
+
+    """Clickable button."""
+
+    def __init__(self, label='btn'):
+        """Create button with given label, size according to label."""
+        Widget.__init__(self)
+
+        #: Button label.
+        self._label = ''
+        #: Text or graphics to be added before label
+        self.prefix = '['
+        #: Text or graphics to be added after label
+        self.suffix = ']'
+        #: How should label be aligned if button has excess space - center | left | right
+        self.align = 'center'
+        #: Padding between prefix/suffix and label
+        self.padding = 1
+
+        self.allow_focus = True
+
+        self.color = 'default'
+        self.color_highlighted = 'default on red'
+        self.highlight = False
+
+        self.sig_clicked = Signal()
+
+        self.label = label
+
+    @property
+    def label(self):
+        """Button label."""
+        return self._label
+
+    @label.setter
+    def label(self, value):
+        self._label = value
+        w = len(value) + len(self.prefix) + len(self.suffix) + 2 * self.padding
+        self.sizereq.update(w, 1)
+
+    def set_theme(self, theme):
+        self.color = theme.button
+        self.color_highlighted = theme.button_active
+
+    def _get_color(self):
+        if self.highlight: # or self.has_focus():
+            return self.color_highlighted
+        return self.color
+
+    def draw(self, buffer, x, y):
+        pad = self.width - len(self.label) - len(self.prefix) - len(self.suffix)
+        lpad, rpad = self._divide_padding(pad)
+        with buffer.attr(self._get_color()):
+            # prefix
+            buffer.puts(x, y, self.prefix)
+            pos = len(self.prefix)
+            # left pad
+            buffer.puts(x + pos, y, ' ' * lpad)
+            pos += lpad
+            # label
+            buffer.puts(x + pos, y, self.label)
+            pos += len(self.label)
+            # right pad
+            buffer.puts(x + pos, y, ' ' * rpad)
+            pos += rpad
+            # suffix
+            buffer.puts(x + pos, y, self.suffix)
+
+    def on_mousedown(self, ev):
+        self.highlight = True
+        self.redraw()
+
+    def on_mouseup(self, ev):
+        self.highlight = False
+        self.redraw()
+
+        if self.enclose(ev.px, ev.py):
+            self.emit('click')
+
+    def on_keypress(self, ev):
+        if ev.keyname == 'enter':
+            self.emit('click')
+
+    def _divide_padding(self, pad):
+        # default is 'left'
+        lpad, rpad = 0, pad
+        if self.align == 'center':
+            lpad = pad // 2
+            rpad = pad - lpad
+        elif self.align == 'right':
+            lpad, rpad = pad, 0
+        return lpad, rpad
+
--- a/tuikit/widgets/label.py	Tue Mar 18 22:39:21 2014 +0100
+++ b/tuikit/widgets/label.py	Wed Mar 19 00:32:38 2014 +0100
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
 from tuikit.core.widget import Widget
 
 
@@ -9,8 +7,11 @@
         Widget.__init__(self)
         self.sizereq.update(len(label), 1)
         self.label = label
-        #self.color = 'normal'
+        self.color = 'default'
+
+    def set_theme(self, theme):
+        self.color = self.theme.normal
 
     def draw(self, buffer, x, y):
-        #ev.driver.pushcolor(self.color)
-        buffer.puts(x, y, self.label)
+        with buffer.attr(self.color):
+            buffer.puts(x, y, self.label)