# HG changeset patch # User Radek Brich # Date 1356779766 -3600 # Node ID 37b7dfc3eae6509f99003ac983aaa7044b39ec49 # Parent 5faa38c10b67e68fe1b7ef8aea09190ac95d7f17 Update Emitter: All event handlers now have exactly one argument: object inherited from Event class, which carries any data. diff -r 5faa38c10b67 -r 37b7dfc3eae6 demo_checkbox.py --- a/demo_checkbox.py Wed Dec 26 01:00:31 2012 +0100 +++ b/demo_checkbox.py Sat Dec 29 12:16:06 2012 +0100 @@ -10,20 +10,20 @@ class MyApplication(Application): def __init__(self): Application.__init__(self) - self.top.connect('keypress', self.on_keypress) + self.top.connect('keypress', self.on_top_keypress) vert = VerticalLayout(homogeneous=False) self.top.layout(vert) - + combo = ComboBox(items=['abc', 'xyz']) self.top.add(combo) - + for i in range(10): cbox = Checkbox('checkbox ' + str(i)) self.top.add(cbox) - - def on_keypress(self, keyname, char): - if keyname == 'escape' or char == 'q': + + def on_top_keypress(self, ev): + if ev.keyname == 'escape' or ev.char == 'q': self.terminate() diff -r 5faa38c10b67 -r 37b7dfc3eae6 demo_editor.py --- a/demo_editor.py Wed Dec 26 01:00:31 2012 +0100 +++ b/demo_editor.py Sat Dec 29 12:16:06 2012 +0100 @@ -17,7 +17,7 @@ class MyApplication(Application): def __init__(self): Application.__init__(self) - self.top.connect('keypress', self.on_keypress) + self.top.connect('keypress', self.on_top_keypress) #edit = EditField(50, 'DlouhyTest12') #self.top.add(edit) @@ -48,13 +48,9 @@ self.button.label = 'YES' - def on_keypress(self, keyname, char): - if keyname == 'escape': + def on_top_keypress(self, ev): + if ev.keyname == 'escape': self.terminate() - if keyname == 'f1': - self.textedit.settext('%s' % self.top.focuschild) - self.textedit.redraw() - if __name__ == '__main__': diff -r 5faa38c10b67 -r 37b7dfc3eae6 demo_input.py --- a/demo_input.py Wed Dec 26 01:00:31 2012 +0100 +++ b/demo_input.py Sat Dec 29 12:16:06 2012 +0100 @@ -10,7 +10,7 @@ class MyApplication(Application): def __init__(self): Application.__init__(self) - self.top.connect('keypress', self.globalkeypress) + self.top.connect('keypress', self.on_top_keypress) self.text = '' textedit = TextEdit(100, 40, self.text) @@ -19,10 +19,10 @@ self.textedit = textedit - def globalkeypress(self, keyname, char): - if char == 'q': + def on_top_keypress(self, ev): + if ev.char == 'q': self.terminate() - self.text += 'keyname: %s char: %s\n' % (keyname, char) + self.text += 'keyname: %(keyname)s char: %(char)s\n' % ev self.textedit.settext(self.text) self.textedit.scrolltoend() diff -r 5faa38c10b67 -r 37b7dfc3eae6 demo_layout.py --- a/demo_layout.py Wed Dec 26 01:00:31 2012 +0100 +++ b/demo_layout.py Sat Dec 29 12:16:06 2012 +0100 @@ -10,13 +10,13 @@ class MyApplication(Application): def __init__(self): Application.__init__(self) - self.top.connect('keypress', self.globalkeypress) + self.top.connect('keypress', self.on_top_keypress) #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) @@ -24,7 +24,7 @@ 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() @@ -34,9 +34,9 @@ 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': + + def on_top_keypress(self, ev): + if ev.keyname == 'escape' or ev.char == 'q': self.terminate() diff -r 5faa38c10b67 -r 37b7dfc3eae6 demo_menu.py --- a/demo_menu.py Wed Dec 26 01:00:31 2012 +0100 +++ b/demo_menu.py Sat Dec 29 12:16:06 2012 +0100 @@ -10,7 +10,7 @@ class MyApplication(Application): def __init__(self): Application.__init__(self) - self.top.connect('keypress', self.globalkeypress) + self.top.connect('keypress', self.on_top_keypress) menubar = MenuBar() self.top.add(menubar) @@ -64,8 +64,8 @@ #win.add(subwin) - def globalkeypress(self, keyname, char): - if keyname == 'escape': + def on_top_keypress(self, ev): + if ev.keyname == 'escape': self.terminate() diff -r 5faa38c10b67 -r 37b7dfc3eae6 demo_tableview.py --- a/demo_tableview.py Wed Dec 26 01:00:31 2012 +0100 +++ b/demo_tableview.py Sat Dec 29 12:16:06 2012 +0100 @@ -12,7 +12,7 @@ class MyApplication(Application): def __init__(self): Application.__init__(self) - self.top.connect('keypress', self.globalkeypress) + self.top.connect('keypress', self.on_top_keypress) data = [] for y in range(100): @@ -21,19 +21,19 @@ row.append('r{}:c{}'.format(y, x)) data.append(row) model = TableModel(data) - + view = TableView(model) view.addcolumn(header=True, expand=False, sizereq=5) for x in range(10): view.addcolumn(title='head'+str(x)) - + self.top.add(view, expand=True, fill=True) vert = VerticalLayout() self.top.layout(vert) - def globalkeypress(self, keyname, char): - if keyname == 'escape': + def on_top_keypress(self, ev): + if ev.keyname == 'escape': self.terminate() diff -r 5faa38c10b67 -r 37b7dfc3eae6 demo_treeview.py --- a/demo_treeview.py Wed Dec 26 01:00:31 2012 +0100 +++ b/demo_treeview.py Sat Dec 29 12:16:06 2012 +0100 @@ -10,7 +10,7 @@ class MyApplication(Application): def __init__(self): Application.__init__(self) - self.top.connect('keypress', self.globalkeypress) + self.top.connect('keypress', self.on_top_keypress) model = TreeModel() model.add('/', ['a', 'b']) @@ -32,8 +32,8 @@ vert = VerticalLayout() self.top.layout(vert) - def globalkeypress(self, eo, keyname, char): - if keyname == 'escape': + def on_top_keypress(self, ev): + if ev.keyname == 'escape': self.terminate() diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/button.py --- a/tuikit/button.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/button.py Sat Dec 29 12:16:06 2012 +0100 @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from tuikit.widget import Widget +from tuikit.emitter import Event class Button(Widget): @@ -32,29 +33,29 @@ self.sizereq.w = w self.sizereq.h = h - self.add_events('click') + self.add_events('click', Event) - def _handle_draw(self, screen, x, y): - super()._handle_draw(screen, x, y) + def _handle_draw(self, ev): + super()._handle_draw(ev) pad = self.width - len(self.label) - len(self.prefix) - len(self.suffix) lpad, rpad = self._divide_padding(pad) - screen.pushcolor(self.getcolor()) + ev.driver.pushcolor(self.getcolor()) # prefix - screen.puts(x, y, self.prefix) + ev.driver.puts(ev.x, ev.y, self.prefix) pos = len(self.prefix) # left pad - screen.puts(x + pos, y, ' ' * lpad) + ev.driver.puts(ev.x + pos, ev.y, ' ' * lpad) pos += lpad # label - screen.puts(x + pos, y, self.label) + ev.driver.puts(ev.x + pos, ev.y, self.label) pos += len(self.label) # right pad - screen.puts(x + pos, y, ' ' * rpad) + ev.driver.puts(ev.x + pos, ev.y, ' ' * rpad) pos += rpad # suffix - screen.puts(x + pos, y, self.suffix) - screen.popcolor() + ev.driver.puts(ev.x + pos, ev.y, self.suffix) + ev.driver.popcolor() def _handle_mousedown(self, ev): super()._handle_mousedown(ev) @@ -69,9 +70,9 @@ if self.enclose(ev.px, ev.py): self.emit('click') - def _handle_keypress(self, keyname, char): - super()._handle_keypress(keyname, char) - if keyname == 'enter': + def _handle_keypress(self, ev): + super()._handle_keypress(ev) + if ev.keyname == 'enter': self.emit('click') def getcolor(self): diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/checkbox.py --- a/tuikit/checkbox.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/checkbox.py Sat Dec 29 12:16:06 2012 +0100 @@ -18,8 +18,8 @@ self.bg = 'normal' self.bghi = 'active' - def _handle_click(self): - super()._handle_click() + def _handle_click(self, ev): + super()._handle_click(ev) if self.checked: self.checked = False self.prefix = '[ ] ' diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/combobox.py --- a/tuikit/combobox.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/combobox.py Sat Dec 29 12:16:06 2012 +0100 @@ -28,8 +28,11 @@ self._menu = Menu(items) self._menu.hide() self._menu.allow_layout = False - #self.top.add(self._menu) - def _on_btn_click(self): - pass + def _set_top(self, value): + super()._set_top(value) + self.top.add(self._menu) + def _on_btn_click(self, ev): + self._menu.show() + diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/common.py --- a/tuikit/common.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/common.py Sat Dec 29 12:16:06 2012 +0100 @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from tuikit.emitter import Emitter +from tuikit.emitter import Event, Emitter class Coords: @@ -41,7 +41,7 @@ def __init__(self, w=None, h=None): self._w = w self._h = h - self.add_events('change') + self.add_events('change', Event) @property def w(self): @@ -197,28 +197,3 @@ LTEE = '├' RTEE = '┤' - -class MouseEvent: - def __init__(self, x=0, y=0, button=0): - self.x = x # global coordinates - self.y = y - self.wx = x # local widget coordinates - self.wy = y - self.px = 0 # parent coordinates - self.py = 0 - self.button = button - - - def childevent(self, child): - ev = MouseEvent(self.x, self.y, self.button) - # original local coordinates are new parent coordinates - ev.px = self.wx - ev.py = self.wy - # update local coordinates - ev.wx = self.wx - child.x - ev.wy = self.wy - child.y - return ev - - def __repr__(self): - return 'MouseEvent(x={0.x},y={0.y},button={0.button})'.format(self) - diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/container.py --- a/tuikit/container.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/container.py Sat Dec 29 12:16:06 2012 +0100 @@ -43,7 +43,7 @@ '''Add widget into this container.''' self.children.append(widget) widget.parent = self - widget.settop(self.top) + widget.top = self.top widget.hints.update(kwargs) if self.focuschild is None and widget.can_focus(): self.focuschild = widget @@ -55,11 +55,10 @@ layout.container = self - def settop(self, top): - '''Set top widget.''' - self.top = top + def _set_top(self, value): + self._top = value for child in self.children: - child.settop(top) + child.top = value def focus_next(self): @@ -92,17 +91,17 @@ return self.trap_focus or not cycled idx_new += 1 - def _handle_keypress(self, keyname, char): - super()._handle_keypress(keyname, char) + def _handle_keypress(self, ev): + super()._handle_keypress(ev) if self.focuschild is not None: - handled = self.focuschild.emit('keypress', keyname, char) + handled = self.focuschild.emit('keypress', ev) if handled: return True - if keyname == 'tab': + if ev.keyname == 'tab': return self.focus_next() - def _handle_resize(self): - super()._handle_resize() + def _handle_resize(self, ev): + super()._handle_resize(ev) for child in self.children: child.emit('resize') diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/driver.py --- a/tuikit/driver.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/driver.py Sat Dec 29 12:16:06 2012 +0100 @@ -5,23 +5,23 @@ class Driver: - + '''Abstract driver interface. Use as base for drivers.''' - + def __init__(self): '''Initialize instance attributes.''' + #: Screen size. self.size = Size() - '''Screen size.''' + #: Clipping region stack. self.clipstack = ClipStack() - '''Clipping region stack.''' + #: Unicode graphics characters. self.unigraph = UnicodeGraphics() - '''Unicode graphics characters.''' + #: Stack of color prefixes. self.colorprefix = [] - '''Stack of color prefixes.''' ## drawing ## - + def puts(self, x, y, s): '''Output string of characters.''' for c in s: @@ -59,7 +59,7 @@ ## colors ## - + def pushcolorprefix(self, name): self.colorprefix.append(name) diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/driver_curses.py --- a/tuikit/driver_curses.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/driver_curses.py Sat Dec 29 12:16:06 2012 +0100 @@ -6,7 +6,6 @@ import logging from tuikit.driver import Driver -from tuikit.common import MouseEvent class DriverCurses(Driver): @@ -282,28 +281,22 @@ except curses.error: return [] - ev = MouseEvent(x, y) - out = [] if bstate & curses.REPORT_MOUSE_POSITION: - out += [('mousemove', ev)] + out += [('mousemove', x, y)] if bstate & curses.BUTTON1_PRESSED: - ev.button = 1 - out += [('mousedown', ev)] + out += [('mousedown', x, y, 1)] if bstate & curses.BUTTON3_PRESSED: - ev.button = 3 - out += [('mousedown', ev)] + out += [('mousedown', x, y, 3)] if bstate & curses.BUTTON1_RELEASED: - ev.button = 1 - out += [('mouseup', ev)] + out += [('mouseup', x, y, 1)] if bstate & curses.BUTTON3_RELEASED: - ev.button = 3 - out += [('mouseup', ev)] + out += [('mouseup', x, y, 3)] return out @@ -333,7 +326,7 @@ consumed.append(c) while True: - # self.log.debug('c=%s len=%s', c, len(codes)) + #self.log.debug('c=%s len=%s', c, len(codes)) for code in codes: if c == code[len(consumed)-1]: if len(code) - 1 == len(consumed): @@ -341,7 +334,7 @@ else: matchingcodes += [code] - # self.log.debug('matching=%s', len(matchingcodes)) + #self.log.debug('matching=%s', len(matchingcodes)) # match found, or no matching code found -> stop if len(matchingcodes) == 0: @@ -385,25 +378,23 @@ x = self.inputqueue_get_wait() - 0x21 y = self.inputqueue_get_wait() - 0x21 - ev = MouseEvent(x, y) out = [] if t in (0x20, 0x21, 0x22): # button press btn = t - 0x1f - ev.button = btn if not btn in self.mbtnstack: self.mbtnstack.append(btn) - out += [('mousedown', ev)] + out += [('mousedown', x, y, btn)] else: - out += [('mousemove', ev)] + out += [('mousemove', x, y, btn)] elif t == 0x23: # button release - ev.button = self.mbtnstack.pop() - out += [('mouseup', ev)] + btn = self.mbtnstack.pop() + out += [('mouseup', x, y, btn)] elif t in (0x60, 0x61): # wheel up, down - ev.button = 4 + t - 0x60 - out += [('mousewheel', ev)] + btn = 4 + t - 0x60 + out += [('mousewheel', x, y, btn)] else: raise Exception('Unknown mouse event: %x' % t) diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/driver_pygame.py --- a/tuikit/driver_pygame.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/driver_pygame.py Sat Dec 29 12:16:06 2012 +0100 @@ -5,17 +5,17 @@ import logging from tuikit.driver import Driver -from tuikit.common import Coords, Size, MouseEvent +from tuikit.common import Coords, Size class TerminalScreen: - + '''Provide character-level output to screen SDL surface. - + This is performance bottleneck and should be optimized as much as possible. - + ''' - + def __init__(self): fontselect = 'dejavusansmono,liberationmono,freemono' self.font = pygame.font.SysFont(fontselect, 14) @@ -25,13 +25,13 @@ height = self.font.get_height() self.charsize = Size(advance, height) self.ascent = self.font.get_ascent() - + # choose self.render() implementation if hasattr(self.font, 'render_glyph'): self.render_char = self.render_glyph else: self.render_char = self.render_noglyph - + self.chars = None self.attrs = None self.default_attr = None @@ -39,10 +39,10 @@ def set_default_attr(self, fg, bg, flags): self.default_attr = (fg, bg, flags) - + def set_attr(self, fg, bg, flags): self.current_attr = (fg, bg, flags) - + def reset_attr(self): self.current_attr = self.default_attr @@ -51,18 +51,18 @@ numchars = w * h self.chars = [' '] * numchars self.attrs = [self.default_attr] * numchars - + def clear(self): numchars = self.w * self.h for pos in range(numchars): self.chars[pos] = ' ' self.attrs[pos] = self.default_attr - + def putch(self, x, y, c): pos = y * self.w + x self.chars[pos] = c self.attrs[pos] = self.current_attr - + def update(self, surface): pos = 0 for y in range(self.h): @@ -77,18 +77,18 @@ def render_glyph(self, screen, x, y, c, fgcolor, bgcolor, flags): '''Render using render_glyph and metrics. - + This is the correct way, but the output seems same as of render_noglyph and this implementation requires patching PyGame to work. - + This implements render() method. See render_noglyph for other implementation. - - ''' + + ''' # draw background dest = Coords(x * self.charsize.w, y * self.charsize.h) if bgcolor != self.default_attr[1]: screen.fill(bgcolor, pygame.Rect(dest.x, dest.y, self.charsize.w, self.charsize.h)) - + if not c: return @@ -103,7 +103,7 @@ metrics = font.metrics(c)[0] minx, maxx, miny, maxy, advance = metrics height, ascent = self.charsize.h, self.ascent - + # clips origin and area of rendered character according to metrics startx, starty = 0, 0 if minx < 0: @@ -116,24 +116,24 @@ miny = ascent - height if maxx > advance: maxx = advance - + # draw character dest.x += minx dest.y += ascent - maxy area = pygame.Rect(startx, starty, maxx - minx, maxy - miny) screen.blit(surface, tuple(dest), area) - + def render_noglyph(self, screen, x, y, c, fgcolor, bgcolor, attr): '''Render character using normal text rendering. - + This implements render() method. See render_glyph for other implementation. - + ''' if attr == 'bold': font = self.font_bold else: font = self.font - + # render character, get metrics surface = font.render(c, True, fgcolor, bgcolor) metrics = font.metrics(c)[0] @@ -141,20 +141,20 @@ startx = 0 if minx < 0: startx = abs(minx) - + # draw background dest = Coords(x * self.charsize.w, y * self.charsize.h) screen.fill(bgcolor, pygame.Rect(dest.x, dest.y, self.charsize.w, self.charsize.h)) - + # draw character area = pygame.Rect(startx, 0, self.charsize.w, self.charsize.h) screen.blit(surface, tuple(dest), area) class DriverPygame(Driver): - + '''PyGame driver class.''' - + keymap = { pygame.K_ESCAPE : 'escape', pygame.K_TAB : 'tab', @@ -186,7 +186,7 @@ pygame.K_SCROLLOCK : 'scrollock', pygame.K_PAUSE : 'pause', } - + colormap = { 'black' : (0,0,0), 'blue' : (23,23,178), @@ -205,7 +205,7 @@ 'intenseyellow' : (255,255,84), 'intensewhite' : (255,255,255), } - + def __init__(self): '''Initialize instance attributes''' Driver.__init__(self) @@ -234,21 +234,22 @@ self.term.reset(self.size.w, self.size.h) ## input ## - + def getevents(self, timeout=None): '''Process input, return list of events.''' events = [] for ev in pygame.event.get(): # mouse if ev.type == pygame.MOUSEMOTION: - evdata = MouseEvent(ev.pos[0] // self.charsize.w, ev.pos[1] // self.charsize.h) - events.append(('mousemove', evdata)) + mx = ev.pos[0] // self.charsize.w + my = ev.pos[1] // self.charsize.h + events.append(('mousemove', mx, my)) elif ev.type in (pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP): - evdata = MouseEvent(ev.pos[0] // self.charsize.w, ev.pos[1] // self.charsize.h) - evdata.button = ev.button + mx = ev.pos[0] // self.charsize.w + my = ev.pos[1] // self.charsize.h evname = {pygame.MOUSEBUTTONDOWN: 'mousedown', pygame.MOUSEBUTTONUP: 'mouseup'} - events.append((evname[ev.type], evdata)) - + events.append((evname[ev.type], mx, my, ev.button)) + # keyboard elif ev.type == pygame.KEYDOWN: keypress = self.keypress_from_pygame_event(ev) @@ -263,7 +264,7 @@ elif ev.type == pygame.KEYUP: if ev.key == self.last_key: pygame.time.set_timer(pygame.USEREVENT, 0) - + # window elif ev.type == pygame.VIDEORESIZE: neww, newh = ev.w // self.charsize.w, ev.h // self.charsize.h @@ -273,7 +274,7 @@ events.append(('resize',)) elif ev.type == pygame.QUIT: events.append(('quit',)) - + else: self.log.warning('Unknown PyGame event: %r', ev.type) return events @@ -289,12 +290,12 @@ return keypress ## drawing ## - + def erase(self): '''Clear screen.''' self.term.clear() self.screen.fill(self.term.default_attr[1]) - + def putch(self, x, y, c): if not self.clipstack.test(x, y): return @@ -307,13 +308,13 @@ ## colors ## - + def _parsecolor(self, name, attr=None): name = name.lower().strip() if attr == 'bold': name = 'intense' + name return self.colormap[name] - + def _parseattrs(self, attrs): res = '' for a in attrs: @@ -324,10 +325,10 @@ def setcolor(self, name, desc): '''Define color name. - + name - name of color (e.g. 'normal', 'active') desc - color description - foreground, background, attributes (e.g. 'black on white, bold') - + ''' parts = desc.split(',') fg, bg = parts[0].split(' on ') @@ -347,7 +348,7 @@ col = self.colors[name] self.current_color = col self.colorstack.append(col) - + def popcolor(self): '''Remove color from top of stack and use new top color for following output.''' self.colorstack.pop() diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/editbox.py --- a/tuikit/editbox.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/editbox.py Sat Dec 29 12:16:06 2012 +0100 @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from tuikit.widget import Widget +from tuikit.emitter import Event class EditBox(Widget): @@ -20,13 +21,15 @@ self.sline = 0 self.spos = 0 - self.add_events('scroll', 'areasize') + self.add_events( + 'scroll', Event, + 'areasize', Event) self.set_text(text) - def _handle_draw(self, screen, x, y): - super()._handle_draw(screen, x, y) + def _handle_draw(self, ev): + super()._handle_draw(ev) for j in range(self.height): if self.yofs + j >= len(self.lines): break @@ -35,52 +38,52 @@ #line += ' ' * (self.width - len(line)) #else: #line = line[:self.width] - screen.puts(x, y + j, line) + ev.driver.puts(ev.x, ev.y + j, line) self.cursor = (self.get_cpos() - self.xofs, self.cline - self.yofs) - def _handle_keypress(self, keyname, char): - super()._handle_keypress(keyname, char) - if keyname: - if keyname == 'left': + def _handle_keypress(self, ev): + super()._handle_keypress(ev) + if ev.keyname: + if ev.keyname == 'left': self.move_left() - if keyname == 'right': + if ev.keyname == 'right': self.move_right() - if keyname == 'home': + if ev.keyname == 'home': self.move_home() - if keyname == 'end': + if ev.keyname == 'end': self.move_end() - if keyname == 'up': + if ev.keyname == 'up': self.move_up() - if keyname == 'down': + if ev.keyname == 'down': self.move_down() - if keyname == 'pageup': + if ev.keyname == 'pageup': self.move_pageup() - if keyname == 'pagedown': + if ev.keyname == 'pagedown': self.move_pagedown() - if keyname == 'backspace': + if ev.keyname == 'backspace': if self.cline > 0 or self.cpos > 0: self.move_left() self.del_char() - if keyname == 'delete': + if ev.keyname == 'delete': self.del_char() - if keyname == 'enter': + if ev.keyname == 'enter': self.add_newline() self.move_right() - if char: - self.add_char(char) + if ev.char: + self.add_char(ev.char) self.move_right() self.redraw() @@ -121,8 +124,9 @@ yofs = len(self.lines) - self.height if yofs < 0: yofs = 0 - self.yofs = yofs - self.emit('scroll') + if self.yofs != yofs: + self.yofs = yofs + self.emit('scroll') def move_left(self): diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/editfield.py --- a/tuikit/editfield.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/editfield.py Sat Dec 29 12:16:06 2012 +0100 @@ -22,56 +22,56 @@ self.pos = len(value) # position of cursor in value self.ofs = 0 # position of value beginning on screen - def _handle_resize(self): - super()._handle_resize() + def _handle_resize(self, ev): + super()._handle_resize(ev) self.tw = self.width - 2 - def _handle_draw(self, screen, x, y): - super()._handle_draw(screen, x, y) - screen.pushcolor('normal') + def _handle_draw(self, ev): + super()._handle_draw(ev) + ev.driver.pushcolor('normal') # draw value val = self.value + ' ' * self.tw # add spaces to fill rest of field val = val[self.ofs : self.ofs + self.tw] # cut value - begin from ofs, limit to tw chars - screen.puts(x + 1, y, val.encode(self.code)) + ev.driver.puts(ev.x + 1, ev.y, val.encode(self.code)) # draw arrows if content overflows c = ' ' if self.ofs > 0: c = '<' - screen.putch(x, y, c) + ev.driver.putch(ev.x, ev.y, c) c = ' ' if len(self.value[self.ofs:]) > self.tw: c = '>' - screen.putch(x + self.width-1, y, c) + ev.driver.putch(ev.x + self.width-1, ev.y, c) self.cursor = (1 + self.pos - self.ofs, 0) - screen.popcolor() + ev.driver.popcolor() - def _handle_keypress(self, keyname, char): - super()._handle_keypress(keyname, char) + def _handle_keypress(self, ev): + super()._handle_keypress(ev) handled = False - if keyname: + if ev.keyname: handled = True - if keyname == 'left': + if ev.keyname == 'left': self.move_left() - elif keyname == 'right': + elif ev.keyname == 'right': self.move_right() - elif keyname == 'backspace': + elif ev.keyname == 'backspace': if self.pos > 0: self.move_left() self.del_char() - elif keyname == 'delete': + elif ev.keyname == 'delete': self.del_char() else: handled = False - if char: - self.add_char(char) + if ev.char: + self.add_char(ev.char) self.move_right() handled = True diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/emitter.py --- a/tuikit/emitter.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/emitter.py Sat Dec 29 12:16:06 2012 +0100 @@ -1,28 +1,116 @@ # -*- coding: utf-8 -*- +"""Event emitter. + +This is simple implementation of signals/slots paradigm. +We deliberately do not use signal or slot words, as they +could be misleading (there are __slots__ in Python and +signals in Unix OS). + +""" import logging +class Event: + def __init__(self): + self.originator = None + + def __getitem__(self, key): + return self.__dict__[key] + + +class DrawEvent(Event): + def __init__(self, driver, x, y): + super().__init__() + self.driver = driver + self.x = x + self.y = y + + def __repr__(self): + return 'DrawEvent(x={0.x},y={0.y})'.format(self) + + +class FocusEvent(Event): + def __init__(self, old=None, new=None): + super().__init__() + #: Former focused widget. + self.old = old + #: Current focused widget. + self.new = new + + def __repr__(self): + return 'FocusEvent(old={0.old},new={0.new})'.format(self) + + +class KeyboardEvent(Event): + def __init__(self, keyname, char): + super().__init__() + self.keyname = keyname + self.char = char + + def __repr__(self): + return 'KeyboardEvent(keyname={0.keyname},char={0.char})'.format(self) + + +class MouseEvent(Event): + def __init__(self, x=0, y=0, button=0): + super().__init__() + self.x = x # global coordinates + self.y = y + self.wx = x # local widget coordinates + self.wy = y + self.px = 0 # parent coordinates + self.py = 0 + self.button = button + + def childevent(self, child): + ev = MouseEvent(self.x, self.y, self.button) + # original local coordinates are new parent coordinates + ev.px = self.wx + ev.py = self.wy + # update local coordinates + ev.wx = self.wx - child.x + ev.wy = self.wy - child.y + return ev + + def __repr__(self): + return 'MouseEvent(x={0.x},y={0.y},button={0.button})'.format(self) + + +class GenericEvent(Event): + """Generic event which carries an object with additional data.""" + def __init__(self, data): + self.data = data + + class Emitter: """Event emitter mixin class.""" - def add_events(self, *event_names): - """Add event names which may be registered by user. + def add_events(self, *events): + """Add events which may be registered by user. This should be called only by subclasses. This serves also as initializer, other methods of Emitter will not work if add_events was not called. + *events -- Arguments must be given in pairs. + + Each pair consists of event_name, event_class: + event_name -- a string used in connect(), emit() + event_class -- class of event payload + """ if not hasattr(self, '_event_handlers'): self._event_handlers = dict() - for event_name in event_names: + self._event_class = dict() + for event_name, event_class in zip(events[::2], events[1::2]): self._event_handlers[event_name] = [] + self._event_class[event_name] = event_class # add default dummy handler if no handler exists for this event handler_name = '_handle_' + event_name if not hasattr(Emitter, handler_name): - setattr(Emitter, handler_name, lambda *args, **kwargs: False) + setattr(Emitter, handler_name, lambda self, ev: False) def connect(self, event_name, handler): """Connect event handler to event name. @@ -69,17 +157,33 @@ Return True when one of the handlers returns True, False otherwise. - + + This creates new instance of event_class given to + add_events() and passes all arguments after event_name + to its __init__ method. + + Unless first of these arguments is Event instance + in which case no object is created and the instance + is passed to handlers. + """ logging.getLogger('tuikit').debug('Emit "%s" on %s %s', event_name, self.__class__.__name__, getattr(self, 'name', None) or id(self)) - handled = getattr(self, '_handle_' + event_name)(*args, **kwargs) + # create event from specified event class, or use first argument + if len(args) and isinstance(args[0], Event): + event = args[0] + else: + event = self._event_class[event_name](*args, **kwargs) + event.originator = self + # try default handler, stop if satisfied + handled = getattr(self, '_handle_' + event_name)(event) if handled: return True + # try custom handlers, stop if satisfied for handler in self._event_handlers[event_name]: - handled = handler(self, *args, **kwargs) + handled = handler(event) if handled: return True diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/label.py --- a/tuikit/label.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/label.py Sat Dec 29 12:16:06 2012 +0100 @@ -9,9 +9,9 @@ self.label = label - def _handle_draw(self, screen, x, y): - super()._handle_draw(screen, x, y) - screen.pushcolor('normal') - screen.puts(x, y, self.label) - screen.popcolor() + def _handle_draw(self, ev): + super()._handle_draw(ev) + ev.driver.pushcolor('normal') + ev.driver.puts(ev.x, ev.y, self.label) + ev.driver.popcolor() diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/layout.py --- a/tuikit/layout.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/layout.py Sat Dec 29 12:16:06 2012 +0100 @@ -25,7 +25,7 @@ self._container = value self._container.connect('resize', self._on_container_resize) - def _on_container_resize(self, eo): + def _on_container_resize(self, ev): self.resize() def _getchildren(self): diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/menu.py --- a/tuikit/menu.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/menu.py Sat Dec 29 12:16:06 2012 +0100 @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from tuikit.widget import Widget +from tuikit.emitter import GenericEvent class Menu(Widget): @@ -17,34 +18,36 @@ self.selected = items[0] self.menubar = None - self.add_events('activate') + self.add_events('activate', GenericEvent) - def _handle_draw(self, screen, x, y): - super()._handle_draw(screen, x, y) - screen.pushcolor(self.bg) - screen.frame(x, y, self.width, self.height) + def _handle_draw(self, ev): + super()._handle_draw(ev) + ev.driver.pushcolor(self.bg) + ev.driver.frame(ev.x, ev.y, self.width, self.height) i = 1 for item in self.items: if item is None: - screen.puts(x, y + i, screen.unigraph.LTEE + \ - screen.unigraph.HLINE * (self.width - 2) + screen.unigraph.RTEE) + ev.driver.puts(ev.x, ev.y + i, ev.driver.unigraph.LTEE + \ + ev.driver.unigraph.HLINE * (self.width - 2) + ev.driver.unigraph.RTEE) else: if self.selected == item: - screen.pushcolor(self.highlight) - screen.puts(x + 1, y + i, ' ' + item[0] + ' ' * (self.width - 3 - len(item[0]))) - screen.popcolor() + ev.driver.pushcolor(self.highlight) + ev.driver.puts(ev.x + 1, ev.y + i, + ' ' + item[0] + ' ' * (self.width - 3 - len(item[0]))) + ev.driver.popcolor() else: - screen.puts(x + 1, y + i, ' ' + item[0] + ' ' * (self.width - 3 - len(item[0]))) + ev.driver.puts(ev.x + 1, ev.y + i, + ' ' + item[0] + ' ' * (self.width - 3 - len(item[0]))) i += 1 - screen.popcolor() + ev.driver.popcolor() - def _handle_keypress(self, keyname, char): - super()._handle_keypress(keyname, char) - if keyname == 'up': + def _handle_keypress(self, ev): + super()._handle_keypress(ev) + if ev.keyname == 'up': self.move_selected(-1) - if keyname == 'down': + if ev.keyname == 'down': self.move_selected(+1) - if keyname == 'enter': + if ev.keyname == 'enter': self.run_selected() self.redraw() diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/menubar.py --- a/tuikit/menubar.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/menubar.py Sat Dec 29 12:16:06 2012 +0100 @@ -29,33 +29,33 @@ item[1].menubar = self i += len(item[0]) + 4 - def _handle_draw(self, screen, x, y): - super()._handle_draw(screen, x, y) - screen.pushcolor(self.bg) + def _handle_draw(self, ev): + super()._handle_draw(ev) + ev.driver.pushcolor(self.bg) i = 0 for item in self.items: if self.selected == item: - screen.pushcolor(self.highlight) - screen.puts(x + i, y, ' ' + item[0] + ' ') - screen.popcolor() + ev.driver.pushcolor(self.highlight) + ev.driver.puts(ev.x + i, ev.y, ' ' + item[0] + ' ') + ev.driver.popcolor() else: - screen.puts(x + i, y, ' ' + item[0] + ' ') + ev.driver.puts(ev.x + i, ev.y, ' ' + item[0] + ' ') i += len(item[0]) + 4 if i < self.width: - screen.puts(x + i, y, ' ' * (self.width - i)) - screen.popcolor() + ev.driver.puts(ev.x + i, ev.y, ' ' * (self.width - i)) + ev.driver.popcolor() - def _handle_keypress(self, keyname, char): - super()._handle_draw(keyname, char) - if keyname == 'left': + def _handle_keypress(self, ev): + super()._handle_draw(ev) + if ev.keyname == 'left': self.move_selected(-1) - elif keyname == 'right': + elif ev.keyname == 'right': self.move_selected(+1) else: if self.selected: if isinstance(self.selected[1], Widget): - self.selected[1].emit('keypress', keyname, char) + self.selected[1].emit('keypress', ev.keyname, ev.char) def move_selected(self, offset): @@ -68,25 +68,28 @@ def _handle_mousedown(self, ev): super()._handle_mousedown(ev) + self._select_xy(ev.wx, ev.wy) + + def _handle_mousemove(self, ev): + super()._handle_mousemove(ev) + self._select_xy(ev.wx, ev.wy) + + def _select_xy(self, wx, wy): i = 0 self.unselect() for item in self.items: w = len(item[0]) + 4 - if ev.wx >= i and ev.wx < i + w: + if wx >= i and wx < i + w: self.select(item) i += w - def _handle_mousemove(self, ev): - super()._handle_mousemove(ev) - self.on_mousedown(ev) - - def _handle_unfocus(self, newfocus): - super()._handle_unfocus() - if self.selected and newfocus == self.selected[1]: + def _handle_unfocus(self, ev): + super()._handle_unfocus(ev) + if self.selected and ev.new == self.selected[1]: return self.unselect() - def on_submenu_focus(self): + def on_submenu_focus(self, ev): self.set_focus() def select(self, item): diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/scrollbar.py --- a/tuikit/scrollbar.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/scrollbar.py Sat Dec 29 12:16:06 2012 +0100 @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from tuikit.widget import Widget +from tuikit.emitter import Event class VScrollbar(Widget): @@ -16,7 +17,7 @@ self.dragging = False self.move = None - self.add_events('change') + self.add_events('change', Event) @property def max(self): @@ -39,9 +40,10 @@ @pos.setter def pos(self, value): - self._pos = value - self._update_thumbpos() - self.emit('change') + if self._pos != value: + self._pos = value + self._update_thumbpos() + self.emit('change') def _update_thumbpos(self): self._thumbpos = 0 @@ -49,13 +51,13 @@ self._thumbpos = int(round(self._pos / self._max * (self.height - 3))) self.redraw() - def _handle_draw(self, screen, x, y): - super()._handle_draw(screen, x, y) - screen.putch(x, y, screen.unigraph.UP_ARROW) - for i in range(y + 1, y + self.height - 1): - screen.putch(x, i, screen.unigraph.LIGHT_SHADE) - screen.putch(x, y + 1 + self._thumbpos, screen.unigraph.BLOCK) - screen.putch(x, y + self.height - 1, screen.unigraph.DOWN_ARROW) + def _handle_draw(self, ev): + super()._handle_draw(ev) + ev.driver.putch(ev.x, ev.y, ev.driver.unigraph.UP_ARROW) + for i in range(ev.y + 1, ev.y + self.height - 1): + ev.driver.putch(ev.x, i, ev.driver.unigraph.LIGHT_SHADE) + ev.driver.putch(ev.x, ev.y + 1 + self._thumbpos, ev.driver.unigraph.BLOCK) + ev.driver.putch(ev.x, ev.y + self.height - 1, ev.driver.unigraph.DOWN_ARROW) def _handle_mousedown(self, ev): super()._handle_mousedown(ev) diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/scrollview.py --- a/tuikit/scrollview.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/scrollview.py Sat Dec 29 12:16:06 2012 +0100 @@ -26,16 +26,16 @@ if widget != self.vscroll: widget.connect('sizereq', self._on_child_sizereq) - def _handle_resize(self): - super()._handle_resize() + def _handle_resize(self, ev): + super()._handle_resize(ev) self.vscroll.x = self.size.w - 1 self.vscroll.height = self.height self._update_vscroll_max() - def _on_vscroll_change(self, eo): + def _on_vscroll_change(self, ev): self.offset.y = - self.vscroll.pos - def _on_child_sizereq(self, eo): + def _on_child_sizereq(self, ev): self._update_vscroll_max() def _update_vscroll_max(self): diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/tableview.py --- a/tuikit/tableview.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/tableview.py Sat Dec 29 12:16:06 2012 +0100 @@ -3,14 +3,14 @@ import math import logging -from tuikit.emitter import Emitter +from tuikit.emitter import Event, Emitter from tuikit.widget import Widget from tuikit.common import Coords class TableModel(Emitter): def __init__(self, list_of_lists): - self.add_events('change') + self.add_events('change', Event) self.data = list_of_lists def getcount(self): @@ -82,7 +82,9 @@ self.acell = Coords() '''Active cell (cursor).''' - self.add_events('scroll', 'areasize') + self.add_events( + 'scroll', Event, + 'areasize', Event) def getmodel(self): return self._model @@ -156,32 +158,32 @@ screen.popcolor() x += col.size + self.spacing - def _handle_draw(self, screen, x, y): - super()._handle_draw(screen, x, y) - screen.pushcolor('normal') + def _handle_draw(self, ev): + super()._handle_draw(ev) + ev.driver.pushcolor('normal') self.rowcount = self.model.getcount() numrows = min(self.rowcount - self.offset.y, self.size.h - self.headsize) rows = self.model.getrows(self.offset.y, self.offset.y + numrows) self.compute_column_sizes() - self.draw_head(screen, x, y) - y += self.headsize + self.draw_head(ev.driver, ev.x, ev.y) + y = ev.y + self.headsize for row in rows: highlight = [] if self.offset.y + rows.index(row) == self.acell.y: highlight.append(self.acell.x) - self.draw_row(screen, x, y, row, highlight) + self.draw_row(ev.driver, ev.x, y, row, highlight) y += 1 - screen.popcolor() + ev.driver.popcolor() - def _handle_keypress(self, keyname, char): - super()._handle_keypress(keyname, char) - if keyname: - if keyname == 'up': self.move_up() - if keyname == 'down': self.move_down() - if keyname == 'left': self.move_left() - if keyname == 'right': self.move_right() - if keyname == 'pageup': self.move_pageup() - if keyname == 'pagedown': self.move_pagedown() + def _handle_keypress(self, ev): + super()._handle_keypress(ev) + if ev.keyname: + if ev.keyname == 'up': self.move_up() + if ev.keyname == 'down': self.move_down() + if ev.keyname == 'left': self.move_left() + if ev.keyname == 'right': self.move_right() + if ev.keyname == 'pageup': self.move_pageup() + if ev.keyname == 'pagedown': self.move_pagedown() self.redraw() def set_yofs(self, yofs): diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/textedit.py --- a/tuikit/textedit.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/textedit.py Sat Dec 29 12:16:06 2012 +0100 @@ -22,33 +22,28 @@ self.vscroll.y = 1 self.vscroll.connect('change', self.on_vscroll_change) - self.on_editbox_areasize() + self.on_editbox_areasize(None) def settext(self, text): self.editbox.set_text(text) - def scrolltoend(self): self.editbox.move_pagelast() - - def _handle_draw(self, screen, x, y): - super()._handle_draw(screen, x, y) - screen.frame(x, y, self.width, self.height) - + def _handle_draw(self, ev): + super()._handle_draw(ev) + ev.driver.frame(ev.x, ev.y, self.width, self.height) - def on_editbox_scroll(self): - self.vscroll.setpos(self.editbox.yofs) + def on_editbox_scroll(self, ev): + self.vscroll.pos = self.editbox.yofs - - def on_editbox_areasize(self): + def on_editbox_areasize(self, ev): smax = len(self.editbox.lines) - self.editbox.height if smax < 0: smax = 0 self.vscroll.max = smax - - def on_vscroll_change(self): + def on_vscroll_change(self, ev): self.editbox.set_yofs(self.vscroll.pos) self.editbox.redraw() diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/treeview.py --- a/tuikit/treeview.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/treeview.py Sat Dec 29 12:16:06 2012 +0100 @@ -1,9 +1,13 @@ # -*- coding: utf-8 -*- -from tuikit.emitter import Emitter +from tuikit.emitter import Event, Emitter from tuikit.widget import Widget -import logging + +class TreeEvent(Event): + def __init__(self, node): + Event.__init__(self) + self.node = node class TreeIter: @@ -99,7 +103,7 @@ """ def __init__(self): - self.add_events('node_added') # node added, arg is the node + self.add_events('node_added', TreeEvent) self.root = TreeNode('', model=self) def __iter__(self): @@ -185,8 +189,8 @@ self.collapsed = [] self.add_events( - 'expand', # node expanded, the affected node is given in args - 'collapse') # node collapsed, the affected node is given in args + 'expand', TreeEvent, # node expanded, event carries the affected node + 'collapse', TreeEvent) # node collapsed, event carries the affected node if model: self.model = model @@ -212,9 +216,9 @@ pass self._update_sizereq() - def on_model_node_added(self, node): + def on_model_node_added(self, ev): if self.cnode is None: - self.cnode = node + self.cnode = ev.node self._update_sizereq() self.redraw() @@ -235,25 +239,26 @@ pass self._update_sizereq() - def _handle_draw(self, driver, x, y): - super()._handle_draw(driver, x, y) - driver.pushcolor('normal') + def _handle_draw(self, ev): + super()._handle_draw(ev) + ev.driver.pushcolor('normal') lines = 0 # bit array, bit 0 - draw vertical line on first column, etc. + y = ev.y for level, index, count, node in self: # prepare string with vertical lines where they should be head = [] for l in range(level-1): if lines & (1 << l): - head.append(driver.unigraph.VLINE + ' ') + head.append(ev.driver.unigraph.VLINE + ' ') else: head.append(' ') # add vertical line if needed if index < count: - head.append(driver.unigraph.LTEE) + head.append(ev.driver.unigraph.LTEE) lines |= 1 << level-1 else: - head.append(driver.unigraph.LLCORNER) + head.append(ev.driver.unigraph.LLCORNER) lines &= ~(1 << level-1) # draw lines and titles head = ''.join(head) @@ -261,23 +266,22 @@ sep = '+' else: sep = ' ' - driver.puts(x, y, head + sep + str(node)) + ev.driver.puts(ev.x, y, head + sep + str(node)) if node is self.cnode: - driver.pushcolor('active') - driver.puts(x + len(head), y, sep + str(node) + ' ') - driver.popcolor() - + ev.driver.pushcolor('active') + ev.driver.puts(ev.x + len(head), y, sep + str(node) + ' ') + ev.driver.popcolor() y += 1 - driver.popcolor() + ev.driver.popcolor() - def _handle_keypress(self, keyname, char): - super()._handle_keypress(keyname, char) - if keyname: - if keyname == 'up': self.move_up() - if keyname == 'down': self.move_down() - if keyname == 'left': self.move_left() - if keyname == 'right': self.move_right() + def _handle_keypress(self, ev): + super()._handle_keypress(ev) + if ev.keyname: + if ev.keyname == 'up': self.move_up() + if ev.keyname == 'down': self.move_down() + if ev.keyname == 'left': self.move_left() + if ev.keyname == 'right': self.move_right() self.redraw() diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/widget.py --- a/tuikit/widget.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/widget.py Sat Dec 29 12:16:06 2012 +0100 @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from tuikit.emitter import Emitter +from tuikit.emitter import Emitter, Event, DrawEvent, FocusEvent, KeyboardEvent, MouseEvent from tuikit.common import Coords, Size @@ -16,7 +16,7 @@ self.parent = None #: Top widget (same for every widget in one application). - self.top = None + self._top = None # Position inside parent widget. Modified by layout manager. self.position = Coords() @@ -34,9 +34,9 @@ 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). + #: Size(w, h). Integers >= 1 or None (meaning use minumal size). self._sizereq = Size(10, 10) - self._sizereq.connect('change', lambda eo: self.emit('sizereq')) + self._sizereq.connect('change', lambda ev: self.emit('sizereq')) #: When false, the widget is not considered in layout. self.allow_layout = True @@ -58,16 +58,16 @@ # event handlers self.add_events( - 'resize', - 'sizereq', - 'draw', - 'focus', - 'unfocus', - 'keypress', - 'mousedown', - 'mouseup', - 'mousemove', - 'mousewheel') + 'resize', Event, + 'sizereq', Event, + 'draw', DrawEvent, + 'focus', FocusEvent, + 'unfocus', FocusEvent, + 'keypress', KeyboardEvent, + 'mousedown', MouseEvent, + 'mouseup', MouseEvent, + 'mousemove', MouseEvent, + 'mousewheel', MouseEvent) @property @@ -107,10 +107,26 @@ @property def sizereq(self): + """Size request. + + This is default size of the widget. Will be fulfilled if possible. + Size(w, h). Integers >= 1 or None (meaning use minumal size). + + """ return self._sizereq - def settop(self, top): - self.top = top + @property + def top(self): + """Top widget (same for every widget in one application).""" + return self._top + + @top.setter + def top(self, value): + self._set_top(value) + + def _set_top(self, value): + """Real setter for top. Allows override.""" + self._top = value ### events @@ -124,7 +140,7 @@ """Draw the widget. This method should not be overriden by subclasses, - use on_draw instead. + use _handle_draw instead. """ if self.hidden: @@ -171,10 +187,11 @@ """ if self.has_focus() or not self.can_focus(): return - if self.parent.focuschild: - self.parent.focuschild.emit('unfocus', self) + oldfocuschild = self.parent.focuschild self.parent.focuschild = self - self.emit('focus') + if oldfocuschild: + oldfocuschild.emit('unfocus', new=self) + self.emit('focus', old=oldfocuschild) def grab_focus(self): diff -r 5faa38c10b67 -r 37b7dfc3eae6 tuikit/window.py --- a/tuikit/window.py Wed Dec 26 01:00:31 2012 +0100 +++ b/tuikit/window.py Sat Dec 29 12:16:06 2012 +0100 @@ -55,24 +55,24 @@ self.closebtn.hidden = not value - def _handle_draw(self, screen, x, y): - super()._handle_draw(screen, x, y) - screen.pushcolor('normal') - screen.frame(x, y, self.width, self.height) + def _handle_draw(self, ev): + super()._handle_draw(ev) + ev.driver.pushcolor('normal') + ev.driver.frame(ev.x, ev.y, self.width, self.height) if self.resizable: if self.resizing: - screen.pushcolor('controls-active') + ev.driver.pushcolor('controls-active') else: - screen.pushcolor('controls') - screen.puts(x + self.width - 2, y + self.height - 1, '─┘') # '━┛' - screen.popcolor() + ev.driver.pushcolor('controls') + ev.driver.puts(ev.x + self.width - 2, ev.y + self.height - 1, '─┘') # '━┛' + ev.driver.popcolor() if self.title: - screen.puts(x + (self.width - len(self.title))//2, y, self.title) + ev.driver.puts(ev.x + (self.width - len(self.title))//2, ev.y, self.title) - screen.fill(x+1, y+1, self.width-2, self.height-2) - screen.popcolor() + ev.driver.fill(ev.x+1, ev.y+1, self.width-2, self.height-2) + ev.driver.popcolor() def _handle_mousedown(self, ev): @@ -131,12 +131,11 @@ self.redraw(True) - def _handle_resize(self): - super()._handle_resize() + def _handle_resize(self, ev): + super()._handle_resize(ev) self.closebtn.x = self.width - 5 - def on_closebtn_click(self): + def on_closebtn_click(self, ev): self.hide() -