From 37cca1c3b92e16dc77c37df5ddf3df379202f2aa Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sun, 5 Apr 2020 13:58:59 +0200 Subject: [PATCH] [core/output] Fully switch to i3 block abstraction According to the unit tests, at least, the old functionality is back again - with the additional i3 block abstraction in output in place. Also, pango support is temporarily removed again and will be re-implemented based on the new architecture. --- core/output.py | 86 +++++++++++++++++++++++++++------------ tests/core/test_output.py | 24 ++++++----- 2 files changed, 74 insertions(+), 36 deletions(-) diff --git a/core/output.py b/core/output.py index 89fd51a..3e3135e 100644 --- a/core/output.py +++ b/core/output.py @@ -6,29 +6,76 @@ import core.theme import core.event def dump_json(obj): - return obj.__dict__ + return obj.dict() + +def assign(src, dst, key, src_key=None, default=None): + if not src_key: + src_key = key.replace('_', '-') # automagically replace - with _ + + for k in src_key if isinstance(src_key, list) else [src_key]: + if k in src: + dst[key] = src[k] + return + if default is not None: + dst[key] = default class block(object): __COMMON_THEME_FIELDS = [ - 'separator', 'separator_block_width', - 'border_top', 'border_left', 'border_right', 'border_bottom', - 'pango', 'fg', 'bg' + 'separator', 'separator-block-width', 'default-separators', + 'border-top', 'border-left', 'border-right', 'border-bottom', + 'pango', 'fg', 'bg', 'padding', 'prefix', 'suffix' ] def __init__(self, theme, module, widget): self.__attributes = {} for key in self.__COMMON_THEME_FIELDS: tmp = theme.get(key, widget) - if tmp: + if tmp is not None: self.__attributes[key] = tmp self.__attributes['name'] = module.id self.__attributes['instance'] = widget.id + self.__attributes['prev-bg'] = theme.get('bg', 'previous') def set(self, key, value): self.__attributes[key] = value - def __dict__(self): - return {} + def dict(self): + result = {} + + assign(self.__attributes, result, 'full_text', ['full_text', 'separator']) + assign(self.__attributes, result, 'separator', 'default-separators') + + if '_decorator' in self.__attributes: + assign(self.__attributes, result, 'color', 'bg') + assign(self.__attributes, result, 'background', 'prev-bg') + result['_decorator'] = True + else: + assign(self.__attributes, result, 'color', 'fg') + assign(self.__attributes, result, 'background', 'bg') + + if 'full_text' in self.__attributes: + result['full_text'] = self.__format(self.__attributes['full_text']) + + for k in [ + 'name', 'instance', 'separator_block_width', 'border', 'border_top', + 'border_bottom', 'border_left', 'border_right' + ]: + assign(self.__attributes, result, k) + + return result + + def __pad(self, text): + padding = self.__attributes.get('padding', '') + if not text: return padding + return '{}{}{}'.format(padding, text, padding) + + def __format(self, text): + if text is None: return None + return '{}{}{}'.format( + self.__pad(self.__attributes.get('prefix')), + text, + self.__pad(self.__attributes.get('suffix')) + ) class i3(object): def __init__(self, theme=core.theme.Theme(), config=core.config.Config([])): @@ -69,29 +116,17 @@ class i3(object): def stop(self): return { 'suffix': '\n]' } - def __pad(self, module, widget, full_text): - padding = self.__theme.padding() - if not full_text: return padding - return '{}{}{}'.format(padding, full_text, padding) - - def __decorate(self, module, widget, full_text): - if full_text is None: return None - return '{}{}{}'.format( - self.__pad(module, widget, self.__theme.prefix(widget)), - full_text, - self.__pad(module, widget, self.__theme.suffix(widget)) - ) - def __separator_block(self, module, widget): + if not self.__theme.get('separator'): + return [] blk = block(self.__theme, module, widget) blk.set('_decorator', True) - return blk + return [blk] def __content_block(self, module, widget): - text = self.__content[widget] blk = block(self.__theme, module, widget) - blk.set('min_width', self.__decorate(module, widget, widget.get('theme.minwidth'))) - blk.set('full_text', self.__decorate(module, widget, text)) + blk.set('min_width', widget.get('theme.minwidth')) + blk.set('full_text', self.__content[widget]) if self.__config.debug(): blk.set('__state', ', '.join(module.state(widget))) return blk @@ -102,11 +137,12 @@ class i3(object): if widget.module() and self.__config.autohide(widget.module().name()): if not any(state in widget.state() for state in [ 'warning', 'critical']): continue - blocks.append(self.__separator_block(module, widget)) + blocks.extend(self.__separator_block(module, widget)) blocks.append(self.__content_block(module, widget)) core.event.trigger('next-widget') return blocks + # TODO: only updates full text, not the state!? def update(self, affected_modules=None): now = time.time() for module in self.__modules: diff --git a/tests/core/test_output.py b/tests/core/test_output.py index 862b338..c400315 100644 --- a/tests/core/test_output.py +++ b/tests/core/test_output.py @@ -20,14 +20,14 @@ class i3(unittest.TestCase): }); self.separator = '***'; self.separatorTheme = core.theme.Theme(raw_data = { - 'defaults': { 'separator': self.separator } + 'defaults': { 'separator': self.separator, 'fg': 'red', 'bg': 'blue' } }); def test_start(self): core.event.clear() all_data = self.i3.start() - data = all_data['data'] + data = all_data['blocks'] self.assertEqual(1, data['version'], 'i3bar protocol version 1 expected') self.assertTrue(data['click_events'], 'click events should be enabled') self.assertEqual('\n[', all_data['suffix']) @@ -48,37 +48,39 @@ class i3(unittest.TestCase): def test_draw_existing_module(self): self.i3.test_draw = unittest.mock.MagicMock(return_value={ - 'data': { 'test': True }, 'suffix': 'end' + 'blocks': { 'test': True }, 'suffix': 'end' }) self.i3.draw('test_draw') self.i3.test_draw.assert_called_once_with() def test_empty_status_line(self): data = self.i3.statusline() - self.assertEqual([], data['data'], 'expected empty list of status line entries') + self.assertEqual([], data['blocks'], 'expected empty list of status line entries') self.assertEqual(',', data['suffix'], 'expected "," as suffix') def test_statusline(self): self.i3.modules([ self.someModule, self.someModule, self.someModule ]) self.i3.update() data = self.i3.statusline() - self.assertEqual(len(self.someModule.widgets())*3, len(data['data']), 'wrong number of widgets') + self.assertEqual(len(self.someModule.widgets())*3, len(data['blocks']), 'wrong number of widgets') def test_padding(self): self.i3.theme(self.paddedTheme) - result = self.i3.__pad(self.someModule, self.someModule.widget(), 'abc') + blk = core.output.block(self.i3.theme(), self.someModule, self.someModule.widget()) + blk.set('full_text', 'abc') + result = blk.dict()['full_text'] self.assertEqual(' abc ', result) def test_no_separator(self): - result = self.i3.__separator(self.someModule, self.someModule.widget()) + result = self.i3.__separator_block(self.someModule, self.someModule.widget()) self.assertEqual([], result) def test_separator(self): self.i3.theme(self.separatorTheme) - result = self.i3.__separator(self.someModule, self.someModule.widget()) + result = self.i3.__separator_block(self.someModule, self.someModule.widget()) self.assertEqual(1, len(result)) - self.assertEqual('***', result[0]['full_text']) - self.assertTrue(result[0].get('_decorator', False)) - self.assertEqual(self.separatorTheme.bg(self.someModule.widget()), result[0]['color']) + self.assertEqual('***', result[0].dict()['full_text']) + self.assertTrue(result[0].dict().get('_decorator', False)) + self.assertEqual(self.separatorTheme.bg(self.someModule.widget()), result[0].dict()['color']) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4