[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.
This commit is contained in:
tobi-wan-kenobi 2020-04-05 13:58:59 +02:00
parent f5052473fb
commit 37cca1c3b9
2 changed files with 74 additions and 36 deletions

View file

@ -6,29 +6,76 @@ import core.theme
import core.event import core.event
def dump_json(obj): 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): class block(object):
__COMMON_THEME_FIELDS = [ __COMMON_THEME_FIELDS = [
'separator', 'separator_block_width', 'separator', 'separator-block-width', 'default-separators',
'border_top', 'border_left', 'border_right', 'border_bottom', 'border-top', 'border-left', 'border-right', 'border-bottom',
'pango', 'fg', 'bg' 'pango', 'fg', 'bg', 'padding', 'prefix', 'suffix'
] ]
def __init__(self, theme, module, widget): def __init__(self, theme, module, widget):
self.__attributes = {} self.__attributes = {}
for key in self.__COMMON_THEME_FIELDS: for key in self.__COMMON_THEME_FIELDS:
tmp = theme.get(key, widget) tmp = theme.get(key, widget)
if tmp: if tmp is not None:
self.__attributes[key] = tmp self.__attributes[key] = tmp
self.__attributes['name'] = module.id self.__attributes['name'] = module.id
self.__attributes['instance'] = widget.id self.__attributes['instance'] = widget.id
self.__attributes['prev-bg'] = theme.get('bg', 'previous')
def set(self, key, value): def set(self, key, value):
self.__attributes[key] = value self.__attributes[key] = value
def __dict__(self): def dict(self):
return {} 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): class i3(object):
def __init__(self, theme=core.theme.Theme(), config=core.config.Config([])): def __init__(self, theme=core.theme.Theme(), config=core.config.Config([])):
@ -69,29 +116,17 @@ class i3(object):
def stop(self): def stop(self):
return { 'suffix': '\n]' } 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): def __separator_block(self, module, widget):
if not self.__theme.get('separator'):
return []
blk = block(self.__theme, module, widget) blk = block(self.__theme, module, widget)
blk.set('_decorator', True) blk.set('_decorator', True)
return blk return [blk]
def __content_block(self, module, widget): def __content_block(self, module, widget):
text = self.__content[widget]
blk = block(self.__theme, module, widget) blk = block(self.__theme, module, widget)
blk.set('min_width', self.__decorate(module, widget, widget.get('theme.minwidth'))) blk.set('min_width', widget.get('theme.minwidth'))
blk.set('full_text', self.__decorate(module, widget, text)) blk.set('full_text', self.__content[widget])
if self.__config.debug(): if self.__config.debug():
blk.set('__state', ', '.join(module.state(widget))) blk.set('__state', ', '.join(module.state(widget)))
return blk return blk
@ -102,11 +137,12 @@ class i3(object):
if widget.module() and self.__config.autohide(widget.module().name()): if widget.module() and self.__config.autohide(widget.module().name()):
if not any(state in widget.state() for state in [ 'warning', 'critical']): if not any(state in widget.state() for state in [ 'warning', 'critical']):
continue continue
blocks.append(self.__separator_block(module, widget)) blocks.extend(self.__separator_block(module, widget))
blocks.append(self.__content_block(module, widget)) blocks.append(self.__content_block(module, widget))
core.event.trigger('next-widget') core.event.trigger('next-widget')
return blocks return blocks
# TODO: only updates full text, not the state!?
def update(self, affected_modules=None): def update(self, affected_modules=None):
now = time.time() now = time.time()
for module in self.__modules: for module in self.__modules:

View file

@ -20,14 +20,14 @@ class i3(unittest.TestCase):
}); });
self.separator = '***'; self.separator = '***';
self.separatorTheme = core.theme.Theme(raw_data = { self.separatorTheme = core.theme.Theme(raw_data = {
'defaults': { 'separator': self.separator } 'defaults': { 'separator': self.separator, 'fg': 'red', 'bg': 'blue' }
}); });
def test_start(self): def test_start(self):
core.event.clear() core.event.clear()
all_data = self.i3.start() all_data = self.i3.start()
data = all_data['data'] data = all_data['blocks']
self.assertEqual(1, data['version'], 'i3bar protocol version 1 expected') self.assertEqual(1, data['version'], 'i3bar protocol version 1 expected')
self.assertTrue(data['click_events'], 'click events should be enabled') self.assertTrue(data['click_events'], 'click events should be enabled')
self.assertEqual('\n[', all_data['suffix']) self.assertEqual('\n[', all_data['suffix'])
@ -48,37 +48,39 @@ class i3(unittest.TestCase):
def test_draw_existing_module(self): def test_draw_existing_module(self):
self.i3.test_draw = unittest.mock.MagicMock(return_value={ 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.draw('test_draw')
self.i3.test_draw.assert_called_once_with() self.i3.test_draw.assert_called_once_with()
def test_empty_status_line(self): def test_empty_status_line(self):
data = self.i3.statusline() 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') self.assertEqual(',', data['suffix'], 'expected "," as suffix')
def test_statusline(self): def test_statusline(self):
self.i3.modules([ self.someModule, self.someModule, self.someModule ]) self.i3.modules([ self.someModule, self.someModule, self.someModule ])
self.i3.update() self.i3.update()
data = self.i3.statusline() 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): def test_padding(self):
self.i3.theme(self.paddedTheme) 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) self.assertEqual(' abc ', result)
def test_no_separator(self): 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) self.assertEqual([], result)
def test_separator(self): def test_separator(self):
self.i3.theme(self.separatorTheme) 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(1, len(result))
self.assertEqual('***', result[0]['full_text']) self.assertEqual('***', result[0].dict()['full_text'])
self.assertTrue(result[0].get('_decorator', False)) self.assertTrue(result[0].dict().get('_decorator', False))
self.assertEqual(self.separatorTheme.bg(self.someModule.widget()), result[0]['color']) self.assertEqual(self.separatorTheme.bg(self.someModule.widget()), result[0].dict()['color'])
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4