def __init__(self, widgets, position='top'): self.widgets = widgets self.position = position self.bounds = None self.window = None self.redraw = Event('bar.redraw') self.redraw.listen(self.expose)
def __init__(self): self.topbars = [] self.bottombars = [] self.leftslices = [] self.rightslices = [] self.updated = Event('screen.updated') self.bars_visible = True self.group_hooks = []
def __init__(self, max_lines=10): self.window = None self.max_lines = max_lines self.redraw = Event('menu.redraw') self.redraw.listen(self._redraw) self.submit_ev = Event('menu.submit') self.submit_ev.listen(self._submit) self.complete = Event('menu.complete') self.complete.listen(self._complete) self.close = Event('menu.close') self.close.listen(self._close)
def __init__(self, screen, width, groups, states): self.screen = screen self.width = width self._cairo = None self._img = None self.redraw = Event('leftbar.redraw') self.redraw.listen(self._redraw) self.repaint = Event('leftbar.repaint') self.repaint.listen(self._paint) self.screen.add_group_hook(self._group_hook) self.visible = False self.groups = groups self.states = states self._drawn_group = None
class Layout(metaclass=LayoutMeta): def __init__(self): self.visible = False self.relayout = Event('layout.relayout') self.relayout.listen(self.check_relayout) def check_relayout(self): if self.visible: self.layout() self.group.check_focus() @classmethod def get_defined_classes(cls, base): res = OrderedDict() for k in cls.fields: v = getattr(cls, k) if isinstance(v, type) and issubclass(v, base): res[k] = v return res def dirty(self): self.relayout.emit() def all_visible_windows(self): for i in getattr(self, 'visible_windows', ()): yield i sub = getattr(self, 'sublayouts', None) if sub: for s in sub(): for i in s.visible_windows: yield i def hide(self): self.visible = False for i in self.all_visible_windows(): i.hide() def show(self): self.visible = True for i in self.all_visible_windows(): i.show()
class Select(GadgetBase): commander = dependency(CommandDispatcher, 'commander') dispatcher = dependency(EventDispatcher, 'event-dispatcher') def __init__(self, max_lines=10): self.window = None self.max_lines = max_lines self.redraw = Event('menu.redraw') self.redraw.listen(self._redraw) self.submit_ev = Event('menu.submit') self.submit_ev.listen(self._submit) self.complete = Event('menu.complete') self.complete.listen(self._complete) self.close = Event('menu.close') self.close.listen(self._close) def __zorro_di_done__(self): self.line_height = self.theme.menu.line_height def cmd_show(self): if self.window: self.cmd_hide() self._current_items = self.items() show_lines = min(len(self._current_items) + 1, self.max_lines) h = self.theme.menu.line_height self.height = h * self.max_lines bounds = self.commander['screen'].bounds._replace(height=h) self._img = self.xcore.pixbuf(bounds.width, h) wid = self.xcore.create_toplevel( bounds, klass=self.xcore.WindowClass.InputOutput, params={ self.xcore.CW.BackPixel: self.theme.menu.background, self.xcore.CW.OverrideRedirect: True, self.xcore.CW.EventMask: self.xcore.EventMask.FocusChange | self.xcore.EventMask.EnterWindow | self.xcore.EventMask.LeaveWindow | self.xcore.EventMask.KeymapState | self.xcore.EventMask.KeyPress, }) self.window = di(self).inject( DisplayWindow(wid, self.draw, focus_out=self._close)) self.dispatcher.all_windows[wid] = self.window self.dispatcher.frames[wid] = self.window # dirty hack self.window.show() self.window.focus() self.text_field = di(self).inject( TextField(self.theme.menu, events={ 'draw': self.redraw, 'submit': self.submit_ev, 'complete': self.complete, 'close': self.close, })) self.dispatcher.active_field = self.text_field self._redraw() def cmd_hide(self): self._close() def draw(self, rect=None): self._img.draw(self.window) def match_lines(self, value): matched = set() for line, res in self._current_items: if line in matched: continue if line.startswith(value): matched.add(line) yield (line, [(1, line[:len(value)]), (0, line[len(value):])], res) ncval = value.lower() for line, res in self._current_items: if line in matched: continue if line.lower().startswith(value): matched.add(line) yield (line, [(1, line[:len(value)]), (0, line[len(value):])], res) for line, res in self._current_items: if line in matched: continue if ncval in line.lower(): matched.add(line) opcodes = [] for pt in re.compile('((?i)' + re.escape(value) + ')').split(line): opcodes.append((pt.lower() == ncval, pt)) yield (line, opcodes, res) def _redraw(self): if not self.window and not self.text_field: return lines = list( islice(self.match_lines(self.text_field.value), self.max_lines)) newh = (len(lines) + 1) * self.line_height if newh != self.height: # don't need to render, need resize self.height = newh bounds = self.commander['screen'].bounds._replace(height=newh) self._img = self.xcore.pixbuf(bounds.width, newh) self.window.set_bounds(bounds) ctx = self._img.context() ctx.set_source(self.theme.menu.background_pat) ctx.rectangle(0, 0, self._img.width, self._img.height) ctx.fill() sx, sy, _, _, ax, ay = ctx.text_extents(self.text_field.value) self.text_field.draw(ctx) th = self.theme.menu pad = th.padding y = self.line_height for text, opcodes, value in lines: ctx.move_to(pad.left, y + self.line_height - pad.bottom) for op, tx in opcodes: ctx.set_source(th.highlight_pat if op else th.text_pat) ctx.show_text(tx) y += self.line_height self.draw() def _submit(self): input = self.text_field.value matched = None value = None for matched, opcodes, value in self.match_lines(input): break self.submit(input, matched, value) self._close() def _close(self): if self.window: self.window.destroy() self.window = None if self.dispatcher.active_field == self.text_field: self.dispatcher.active_field = None self.text_field = None def _complete(self): text, _, val = next(iter(self.match_lines(self.text_field.value))) self.text_field.value = text self.text_field.sel_start = len(text) self.text_field.sel_width = 0 self.redraw.emit()
def __init__(self): self.visible = False self.relayout = Event('layout.relayout') self.relayout.listen(self.check_relayout)
class Screen(object): def __init__(self): self.topbars = [] self.bottombars = [] self.leftslices = [] self.rightslices = [] self.updated = Event('screen.updated') self.bars_visible = True self.group_hooks = [] def add_group_hook(self, fun): self.group_hooks.append(fun) def remove_group_hook(self, fun): self.group_hooks.remove(fun) def set_group(self, group): self.group = group for h in self.group_hooks: h() def cmd_focus(self): self.group.focus() def set_bounds(self, rect): self.bounds = rect x, y, w, h = rect if self.bars_visible: for bar in self.topbars: bar.set_bounds(Rectangle(x, y, w, bar.height)) y += bar.height h -= bar.height for bar in self.bottombars: h -= bar.height bar.set_bounds(Rectangle(x, y + h, w, bar.height)) for gadget in self.leftslices: gadget.set_bounds(Rectangle(x, y, gadget.width, h)) x += gadget.width w -= gadget.width for gadget in self.rightslices: w -= gadget.width gadget.set_bounds(Rectangle(x + w, y, gadget.width, h)) self.inner_bounds = Rectangle(x, y, w, h) self.updated.emit() def all_bars(self): for bar in self.topbars: yield bar for bar in self.bottombars: yield bar def add_top_bar(self, bar): if bar not in self.topbars: self.topbars.append(bar) self.set_bounds(self.bounds) def add_bottom_bar(self, bar): if bar not in self.bottombars: self.bottombars.append(bar) self.set_bounds(self.bounds) def slice_left(self, obj): if obj not in self.leftslices: self.leftslices.append(obj) self.set_bounds(self.bounds) def unslice_left(self, obj): if obj in self.leftslices: self.leftslices.remove(obj) self.set_bounds(self.bounds) def slice_right(self, obj): if obj not in self.rightslices: self.rightslices.append(obj) self.set_bounds(self.bounds) def unslice_right(self, obj): if obj in self.rightslices: self.leftslices.remove(obj) self.set_bounds(self.bounds) def cmd_toggle_bars(self): if self.bars_visible: self.cmd_hide_bars() else: self.cmd_show_bars() def cmd_hide_bars(self): for bar in self.all_bars(): bar.window.hide() self.bars_visible = False self.inner_bounds = self.bounds self.updated.emit() def cmd_show_bars(self): for bar in self.all_bars(): bar.window.show() self.bars_visible = True self.set_bounds(self.bounds)
class Select(GadgetBase): commander = dependency(CommandDispatcher, 'commander') dispatcher = dependency(EventDispatcher, 'event-dispatcher') def __init__(self, max_lines=10): self.window = None self.max_lines = max_lines self.redraw = Event('menu.redraw') self.redraw.listen(self._redraw) self.submit_ev = Event('menu.submit') self.submit_ev.listen(self._submit) self.complete = Event('menu.complete') self.complete.listen(self._complete) self.close = Event('menu.close') self.close.listen(self._close) def __zorro_di_done__(self): self.line_height = self.theme.menu.line_height def cmd_show(self): if self.window: self.cmd_hide() self._current_items = self.items() show_lines = min(len(self._current_items) + 1, self.max_lines) h = self.theme.menu.line_height self.height = h*self.max_lines bounds = self.commander['screen'].bounds._replace(height=h) self._img = self.xcore.pixbuf(bounds.width, h) wid = self.xcore.create_toplevel(bounds, klass=self.xcore.WindowClass.InputOutput, params={ self.xcore.CW.BackPixel: self.theme.menu.background, self.xcore.CW.OverrideRedirect: True, self.xcore.CW.EventMask: self.xcore.EventMask.FocusChange | self.xcore.EventMask.EnterWindow | self.xcore.EventMask.LeaveWindow | self.xcore.EventMask.KeymapState | self.xcore.EventMask.KeyPress, }) self.window = di(self).inject(DisplayWindow(wid, self.draw, focus_out=self._close)) self.dispatcher.all_windows[wid] = self.window self.dispatcher.frames[wid] = self.window # dirty hack self.window.show() self.window.focus() self.text_field = di(self).inject(TextField(self.theme.menu, events={ 'draw': self.redraw, 'submit': self.submit_ev, 'complete': self.complete, 'close': self.close, })) self.dispatcher.active_field = self.text_field self._redraw() def cmd_hide(self): self._close() def draw(self, rect=None): self._img.draw(self.window) def match_lines(self, value): matched = set() for line, res in self._current_items: if line in matched: continue if line.startswith(value): matched.add(line) yield (line, [(1, line[:len(value)]), (0, line[len(value):])], res) ncval = value.lower() for line, res in self._current_items: if line in matched: continue if line.lower().startswith(value): matched.add(line) yield (line, [(1, line[:len(value)]), (0, line[len(value):])], res) for line, res in self._current_items: if line in matched: continue if ncval in line.lower(): matched.add(line) opcodes = [] for pt in re.compile('((?i)'+re.escape(value)+')').split(line): opcodes.append((pt.lower() == ncval, pt)) yield (line, opcodes, res) def _redraw(self): if not self.window and not self.text_field: return lines = list(islice(self.match_lines(self.text_field.value), self.max_lines)) newh = (len(lines)+1)*self.line_height if newh != self.height: # don't need to render, need resize self.height = newh bounds = self.commander['screen'].bounds._replace(height=newh) self._img = self.xcore.pixbuf(bounds.width, newh) self.window.set_bounds(bounds) ctx = self._img.context() ctx.set_source(self.theme.menu.background_pat) ctx.rectangle(0, 0, self._img.width, self._img.height) ctx.fill() sx, sy, _, _, ax, ay = ctx.text_extents(self.text_field.value) self.text_field.draw(ctx) th = self.theme.menu pad = th.padding y = self.line_height for text, opcodes, value in lines: ctx.move_to(pad.left, y + self.line_height - pad.bottom) for op, tx in opcodes: ctx.set_source(th.highlight_pat if op else th.text_pat) ctx.show_text(tx) y += self.line_height self.draw() def _submit(self): input = self.text_field.value matched = None value = None for matched, opcodes, value in self.match_lines(input): break self.submit(input, matched, value) self._close() def _close(self): if self.window: self.window.destroy() self.window = None if self.dispatcher.active_field == self.text_field: self.dispatcher.active_field = None self.text_field = None def _complete(self): text, _, val = next(iter(self.match_lines(self.text_field.value))) self.text_field.value = text self.text_field.sel_start = len(text) self.text_field.sel_width = 0 self.redraw.emit()
class Bar(object): xcore = dependency(Core, 'xcore') dispatcher = dependency(EventDispatcher, 'event-dispatcher') theme = dependency(Theme, 'theme') def __init__(self, widgets, position='top'): self.widgets = widgets self.position = position self.bounds = None self.window = None self.redraw = Event('bar.redraw') self.redraw.listen(self.expose) def __zorro_di_done__(self): bar = self.theme.bar self.height = bar.height self.background = bar.background_pat inj = di(self).clone() inj['bar'] = self for w in self.widgets: w.height = self.height inj.inject(w) def set_bounds(self, rect): self.bounds = rect self.width = rect.width stride = self.xcore.bitmap_stride self.img = self.xcore.pixbuf(self.width, self.height) self.cairo = self.img.context() if self.window and not self.window.set_bounds(rect): self.redraw.emit() def create_window(self): EM = self.xcore.EventMask CW = self.xcore.CW self.window = DisplayWindow( self.xcore.create_toplevel( self.bounds, klass=self.xcore.WindowClass.InputOutput, params={ CW.EventMask: EM.Exposure | EM.SubstructureNotify, CW.OverrideRedirect: True, }), self.expose) self.window.want.size = self.bounds di(self).inject(self.window) self.dispatcher.register_window(self.window) self.window.show() def expose(self, rect=None): # TODO(tailhook) set clip region to specified rectangle self.cairo.set_source(self.background) self.cairo.rectangle(0, 0, self.width, self.height) self.cairo.fill() l = 0 r = self.width for i in self.widgets: self.cairo.save() self.cairo.rectangle(l, 0, r - l, self.height) self.cairo.clip() l, r = i.draw(self.cairo, l, r) self.cairo.restore() self.img.draw(self.window)
class Screen(object): def __init__(self): self.topbars = [] self.bottombars = [] self.leftslices = [] self.rightslices = [] self.updated = Event('screen.updated') self.bars_visible = True self.group_hooks = [] def add_group_hook(self, fun): self.group_hooks.append(fun) def remove_group_hook(self, fun): self.group_hooks.remove(fun) def set_group(self, group): self.group = group for h in self.group_hooks: h() def cmd_focus(self): self.group.focus() def set_bounds(self, rect): self.bounds = rect x, y, w, h = rect if self.bars_visible: for bar in self.topbars: bar.set_bounds(Rectangle(x, y, w, bar.height)) y += bar.height h -= bar.height for bar in self.bottombars: h -= bar.height bar.set_bounds(Rectangle(x, y+h, w, bar.height)) for gadget in self.leftslices: gadget.set_bounds(Rectangle(x, y, gadget.width, h)) x += gadget.width w -= gadget.width for gadget in self.rightslices: w -= gadget.width gadget.set_bounds(Rectangle(x+w, y, gadget.width, h)) self.inner_bounds = Rectangle(x, y, w, h) self.updated.emit() def all_bars(self): for bar in self.topbars: yield bar for bar in self.bottombars: yield bar def add_top_bar(self, bar): if bar not in self.topbars: self.topbars.append(bar) self.set_bounds(self.bounds) def add_bottom_bar(self, bar): if bar not in self.bottombars: self.bottombars.append(bar) self.set_bounds(self.bounds) def slice_left(self, obj): if obj not in self.leftslices: self.leftslices.append(obj) self.set_bounds(self.bounds) def unslice_left(self, obj): if obj in self.leftslices: self.leftslices.remove(obj) self.set_bounds(self.bounds) def slice_right(self, obj): if obj not in self.rightslices: self.rightslices.append(obj) self.set_bounds(self.bounds) def unslice_right(self, obj): if obj in self.rightslices: self.leftslices.remove(obj) self.set_bounds(self.bounds) def cmd_toggle_bars(self): if self.bars_visible: self.cmd_hide_bars() else: self.cmd_show_bars() def cmd_hide_bars(self): for bar in self.all_bars(): bar.window.hide() self.bars_visible = False self.inner_bounds = self.bounds self.updated.emit() def cmd_show_bars(self): for bar in self.all_bars(): bar.window.show() self.bars_visible = True self.set_bounds(self.bounds)
class Bar(object): xcore = dependency(Core, 'xcore') dispatcher = dependency(EventDispatcher, 'event-dispatcher') theme = dependency(Theme, 'theme') def __init__(self, widgets, position='top'): self.widgets = widgets self.position = position self.bounds = None self.window = None self.redraw = Event('bar.redraw') self.redraw.listen(self.expose) def __zorro_di_done__(self): bar = self.theme.bar self.height = bar.height self.background = bar.background_pat inj = di(self).clone() inj['bar'] = self for w in self.widgets: w.height = self.height inj.inject(w) def set_bounds(self, rect): self.bounds = rect self.width = rect.width stride = self.xcore.bitmap_stride self.img = self.xcore.pixbuf(self.width, self.height) self.cairo = self.img.context() if self.window and not self.window.set_bounds(rect): self.redraw.emit() def create_window(self): EM = self.xcore.EventMask CW = self.xcore.CW self.window = DisplayWindow(self.xcore.create_toplevel(self.bounds, klass=self.xcore.WindowClass.InputOutput, params={ CW.EventMask: EM.Exposure | EM.SubstructureNotify, CW.OverrideRedirect: True, }), self.expose) self.window.want.size = self.bounds di(self).inject(self.window) self.dispatcher.register_window(self.window) self.window.show() def expose(self, rect=None): # TODO(tailhook) set clip region to specified rectangle self.cairo.set_source(self.background) self.cairo.rectangle(0, 0, self.width, self.height) self.cairo.fill() l = 0 r = self.width for i in self.widgets: self.cairo.save() self.cairo.rectangle(l, 0, r-l, self.height) self.cairo.clip() l, r = i.draw(self.cairo, l, r) self.cairo.restore() self.img.draw(self.window)
class LeftBar(object): xcore = dependency(XCore, 'xcore') theme = dependency(Theme, 'theme') dispatcher = dependency(EventDispatcher, 'event-dispatcher') commander = dependency(CommandDispatcher, 'commander') def __init__(self, screen, width, groups, states): self.screen = screen self.width = width self._cairo = None self._img = None self.redraw = Event('leftbar.redraw') self.redraw.listen(self._redraw) self.repaint = Event('leftbar.repaint') self.repaint.listen(self._paint) self.screen.add_group_hook(self._group_hook) self.visible = False self.groups = groups self.states = states self._drawn_group = None def __zorro_di_done__(self): wid = self.xcore.create_toplevel(Rectangle(0, 0, 1, 1), klass=self.xcore.WindowClass.InputOutput, params={ self.xcore.CW.BackPixel: self.theme.menu.background, self.xcore.CW.OverrideRedirect: True, self.xcore.CW.EventMask: self.xcore.EventMask.Exposure, }) self.window = di(self).inject(DisplayWindow(wid, self.paint)) self.dispatcher.all_windows[wid] = self.window self.window.show() if self.screen.group: self._group_hook() self.commander.events['window'].listen(self._check_redraw) Window.any_window_changed.listen(self._check_redraw) def paint(self, rect): self.repaint.emit() def _check_redraw(self): st = self.states.get(self.screen.group) if st is None or st.dirty: self.redraw.emit() def set_bounds(self, rect): self.bounds = rect self.window.set_bounds(rect) self._cairo = None self._img = None def _draw_section(self, title, y): ctx = self._cairo theme = self.theme.tabs ctx.set_source(theme.section_color_pat) sx, sy, tw, th, ax, ay = ctx.text_extents(title) y += theme.section_padding.top + th x = self.width - theme.section_padding.right - tw ctx.move_to(x, y) ctx.show_text(title) y += theme.section_padding.bottom return y def _draw_win(self, win, y): ctx = self._cairo theme = self.theme.tabs sx, sy, tw, th, ax, ay = ctx.text_extents(win.title) fh = th + theme.padding.top + theme.padding.bottom # Background if win.active: ctx.set_source(theme.active_bg_pat) elif win.urgent: ctx.set_source(theme.urgent_bg_pat) else: ctx.set_source(theme.inactive_bg_pat) ctx.move_to(self.width, y) ctx.line_to(theme.margin.left + theme.border_radius, y) ctx.curve_to(theme.margin.left + theme.border_radius/3, y, theme.margin.left, y + theme.border_radius/3, theme.margin.left, y + theme.border_radius) ctx.line_to(theme.margin.left, y + fh - theme.border_radius) ctx.curve_to(theme.margin.left, y + fh - theme.border_radius/3, theme.margin.left + theme.border_radius/3, y + fh, theme.margin.left + theme.border_radius, y + fh) ctx.line_to(self.width, y + fh) ctx.close_path() ctx.fill() # Icon if hasattr(win.win, 'icons'): win.win.draw_icon(ctx, theme.margin.left + theme.padding.left, y + (th + theme.padding.top + theme.padding.bottom - theme.icon_size)//2, theme.icon_size) # Title if win.active: ctx.set_source(theme.active_title_pat) elif win.urgent: ctx.set_source(theme.urgent_title_pat) else: ctx.set_source(theme.inactive_title_pat) x = theme.margin.left + theme.padding.left x += theme.icon_size + theme.icon_spacing ctx.move_to(x, y + theme.padding.top + th) y += th + theme.spacing + theme.padding.top + theme.padding.bottom ctx.show_text(win.title) ctx.fill() return y def _paint(self): if self._img: self._img.draw(self.window) def _redraw(self): if not self.visible: return gr = self.screen.group st = self.states.get(gr) if st is None: st = self.states[gr] = di(self).inject(State(gr)) if not st.update() and self._img and self._drawn_group == gr: return if self._img is None: self._img = self.xcore.pixbuf(self.width, self.bounds.height) self._cairo = self._img.context() theme = self.theme.tabs ctx = self._cairo ctx.set_source(theme.background_pat) ctx.rectangle(0, 0, self._img.width, self._img.height) ctx.fill() theme.font.apply(ctx) y = theme.margin.top for sec in st.sections: for win in sec: if isinstance(win, str): if len(st.sections) > 1: y = self._draw_section(win, y) else: y = self._draw_win(win, y) self._drawn_group = gr self.repaint.emit() def _group_hook(self): ngr = self.screen.group if ngr.name in self.groups: self.show() else: self.hide() self.redraw.emit() def show(self): if not self.visible: self.visible = True self.window.show() self.screen.slice_left(self) def hide(self): if self.visible: self.visible = False self.screen.unslice_left(self) self.window.hide() self._cairo = None self._img = None
class LeftBar(object): xcore = dependency(XCore, 'xcore') theme = dependency(Theme, 'theme') dispatcher = dependency(EventDispatcher, 'event-dispatcher') commander = dependency(CommandDispatcher, 'commander') def __init__(self, screen, width, groups, states): self.screen = screen self.width = width self._cairo = None self._img = None self.redraw = Event('leftbar.redraw') self.redraw.listen(self._redraw) self.repaint = Event('leftbar.repaint') self.repaint.listen(self._paint) self.screen.add_group_hook(self._group_hook) self.visible = False self.groups = groups self.states = states self._drawn_group = None def __zorro_di_done__(self): wid = self.xcore.create_toplevel( Rectangle(0, 0, 1, 1), klass=self.xcore.WindowClass.InputOutput, params={ self.xcore.CW.BackPixel: self.theme.menu.background, self.xcore.CW.OverrideRedirect: True, self.xcore.CW.EventMask: self.xcore.EventMask.Exposure, }) self.window = di(self).inject(DisplayWindow(wid, self.paint)) self.dispatcher.all_windows[wid] = self.window self.window.show() if self.screen.group: self._group_hook() self.commander.events['window'].listen(self._check_redraw) Window.any_window_changed.listen(self._check_redraw) def paint(self, rect): self.repaint.emit() def _check_redraw(self): st = self.states.get(self.screen.group) if st is None or st.dirty: self.redraw.emit() def set_bounds(self, rect): self.bounds = rect self.window.set_bounds(rect) self._cairo = None self._img = None def _draw_section(self, title, y): ctx = self._cairo theme = self.theme.tabs ctx.set_source(theme.section_color_pat) sx, sy, tw, th, ax, ay = ctx.text_extents(title) y += theme.section_padding.top + th x = self.width - theme.section_padding.right - tw ctx.move_to(x, y) ctx.show_text(title) y += theme.section_padding.bottom return y def _draw_win(self, win, y): ctx = self._cairo theme = self.theme.tabs sx, sy, tw, th, ax, ay = ctx.text_extents(win.title) fh = th + theme.padding.top + theme.padding.bottom # Background if win.active: ctx.set_source(theme.active_bg_pat) elif win.urgent: ctx.set_source(theme.urgent_bg_pat) else: ctx.set_source(theme.inactive_bg_pat) ctx.move_to(self.width, y) ctx.line_to(theme.margin.left + theme.border_radius, y) ctx.curve_to(theme.margin.left + theme.border_radius / 3, y, theme.margin.left, y + theme.border_radius / 3, theme.margin.left, y + theme.border_radius) ctx.line_to(theme.margin.left, y + fh - theme.border_radius) ctx.curve_to(theme.margin.left, y + fh - theme.border_radius / 3, theme.margin.left + theme.border_radius / 3, y + fh, theme.margin.left + theme.border_radius, y + fh) ctx.line_to(self.width, y + fh) ctx.close_path() ctx.fill() # Icon if hasattr(win.win, 'icons'): win.win.draw_icon( ctx, theme.margin.left + theme.padding.left, y + (th + theme.padding.top + theme.padding.bottom - theme.icon_size) // 2, theme.icon_size) # Title if win.active: ctx.set_source(theme.active_title_pat) elif win.urgent: ctx.set_source(theme.urgent_title_pat) else: ctx.set_source(theme.inactive_title_pat) x = theme.margin.left + theme.padding.left x += theme.icon_size + theme.icon_spacing ctx.move_to(x, y + theme.padding.top + th) y += th + theme.spacing + theme.padding.top + theme.padding.bottom ctx.show_text(win.title) ctx.fill() return y def _paint(self): if self._img: self._img.draw(self.window) def _redraw(self): if not self.visible: return gr = self.screen.group st = self.states.get(gr) if st is None: st = self.states[gr] = di(self).inject(State(gr)) if not st.update() and self._img and self._drawn_group == gr: return if self._img is None: self._img = self.xcore.pixbuf(self.width, self.bounds.height) self._cairo = self._img.context() theme = self.theme.tabs ctx = self._cairo ctx.set_source(theme.background_pat) ctx.rectangle(0, 0, self._img.width, self._img.height) ctx.fill() theme.font.apply(ctx) y = theme.margin.top for sec in st.sections: for win in sec: if isinstance(win, str): if len(st.sections) > 1: y = self._draw_section(win, y) else: y = self._draw_win(win, y) self._drawn_group = gr self.repaint.emit() def _group_hook(self): ngr = self.screen.group if ngr.name in self.groups: self.show() else: self.hide() self.redraw.emit() def show(self): if not self.visible: self.visible = True self.window.show() self.screen.slice_left(self) def hide(self): if self.visible: self.visible = False self.screen.unslice_left(self) self.window.hide() self._cairo = None self._img = None