# HG changeset patch # User Radek Brich # Date 1395185558 -3600 # Node ID 94f5baef19ac7c04ce508b46c80cbea3263ff47c # Parent 90d00354dc709f77308b8b4983e1141771b63424 Add Theme, Button. diff -r 90d00354dc70 -r 94f5baef19ac demos/03_application.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() diff -r 90d00354dc70 -r 94f5baef19ac tuikit/core/application.py --- 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 ===') diff -r 90d00354dc70 -r 94f5baef19ac tuikit/core/buffer.py --- 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) diff -r 90d00354dc70 -r 94f5baef19ac tuikit/core/container.py --- 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.""" diff -r 90d00354dc70 -r 94f5baef19ac tuikit/core/theme.py --- /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() diff -r 90d00354dc70 -r 94f5baef19ac tuikit/core/unigraph.py --- 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) diff -r 90d00354dc70 -r 94f5baef19ac tuikit/core/widget.py --- 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 diff -r 90d00354dc70 -r 94f5baef19ac tuikit/core/window.py --- 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) diff -r 90d00354dc70 -r 94f5baef19ac tuikit/widgets/button.py --- /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 + diff -r 90d00354dc70 -r 94f5baef19ac tuikit/widgets/label.py --- 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)