[core/themes] Add module-specific themes

Allow module-specific theme information to overload "default"
configuration. I.e. it is now possible to have specific prefix or
postfix configurations for different modules. The module name is derived
for each widget from the module (__module__) from which it was
instantiated.

see #23
This commit is contained in:
Tobi-wan Kenobi 2016-12-08 12:44:52 +01:00
parent 562fd85ca2
commit 2fa8d7b778
6 changed files with 56 additions and 20 deletions

View file

@ -13,6 +13,7 @@ class Module(object):
this base class.
"""
def __init__(self, engine, widgets):
self.name = self.__module__.split(".")[-1]
self._widgets = []
if widgets:
self._widgets = widgets if isinstance(widgets, list) else [widgets]
@ -59,10 +60,14 @@ class Engine(object):
"""Start the event loop"""
self._output.start()
while self.running():
self._output.begin()
for module in self._modules:
module.update(module.widgets())
self._output.draw(widgets=module.widgets(), engine=self)
for widget in module.widgets():
widget.set_module(module)
self._output.draw(widget=widget, engine=self)
self._output.flush()
self._output.end()
if self.running():
time.sleep(1)

View file

@ -9,6 +9,13 @@ class Widget(object):
"""Represents a single visible block in the status bar"""
def __init__(self, full_text):
self._full_text = full_text
self._module = None
def set_module(self, module):
self._module = module.name
def module(self):
return self._module
def full_text(self):
"""Retrieve the full text to display in the widget"""
@ -21,6 +28,7 @@ class I3BarOutput(object):
"""Manage output according to the i3bar protocol"""
def __init__(self, theme):
self._theme = theme
self._widgets = []
def start(self):
"""Print start preamble for i3bar protocol"""
@ -30,30 +38,29 @@ class I3BarOutput(object):
"""Finish i3bar protocol"""
sys.stdout.write("]\n")
def draw_widget(self, result, widget):
def draw(self, widget, engine=None):
"""Draw a single widget"""
full_text = widget.full_text()
prefix = self._theme.prefix(widget)
suffix = self._theme.suffix(widget)
if prefix:
full_text = "{}{}".format(prefix, full_text)
full_text = u"{}{}".format(prefix, full_text)
if suffix:
full_text = "{}{}".format(full_text, suffix)
result.append({
u"full_text": "{}".format(full_text)
full_text = u"{}{}".format(full_text, suffix)
self._widgets.append({
u"full_text": u"{}".format(full_text)
})
def draw(self, widgets, engine=None):
"""Draw a number of widgets"""
if not isinstance(widgets, list):
widgets = [widgets]
result = []
for widget in widgets:
self.draw_widget(result, widget)
sys.stdout.write(json.dumps(result))
def begin(self):
"""Start one output iteration"""
self._widgets = []
def flush(self):
"""Flushes output"""
sys.stdout.write(json.dumps(self._widgets))
def end(self):
"""Finalizes output"""
sys.stdout.write(",\n")
sys.stdout.flush()

View file

@ -55,9 +55,16 @@ class Theme(object):
module_theme = self._theme.get(widget.module(), {})
padding = None
if name != "padding":
padding = self._get(widget, "padding")
value = self._defaults.get(name, default)
value = module_theme.get(name, value)
if value and padding:
value = u"{}{}{}".format(padding, value, padding)
return value
# algorithm copied from

View file

@ -33,25 +33,34 @@ class TestI3BarOutput(unittest.TestCase):
@mock.patch("sys.stdout", new_callable=StringIO)
def test_draw_single_widget(self, stdout):
self.output.draw(self.someWidget)
self.output.flush()
result = json.loads(stdout.getvalue())[0]
self.assertEquals(result["full_text"], self.someWidget.full_text())
@mock.patch("sys.stdout", new_callable=StringIO)
def test_draw_multiple_widgets(self, stdout):
self.output.draw([self.someWidget, self.someWidget])
for widget in [self.someWidget, self.someWidget]:
self.output.draw(widget)
self.output.flush()
result = json.loads(stdout.getvalue())
for res in result:
self.assertEquals(res["full_text"], self.someWidget.full_text())
self.assertEquals(res["full_text"], widget.full_text())
@mock.patch("sys.stdout", new_callable=StringIO)
def test_flush(self, stdout):
self.output.flush()
def test_begin(self, stdout):
self.output.begin()
self.assertEquals("", stdout.getvalue())
@mock.patch("sys.stdout", new_callable=StringIO)
def test_end(self, stdout):
self.output.end()
self.assertEquals(",\n", stdout.getvalue())
@mock.patch("sys.stdout", new_callable=StringIO)
def test_prefix(self, stdout):
self.theme.set_prefix(" - ")
self.output.draw(self.someWidget)
self.output.flush()
result = json.loads(stdout.getvalue())[0]
self.assertEquals(result["full_text"], "{}{}".format(
self.theme.prefix(self.someWidget), self.someWidget.full_text())
@ -61,6 +70,7 @@ class TestI3BarOutput(unittest.TestCase):
def test_suffix(self, stdout):
self.theme.set_suffix(" - ")
self.output.draw(self.someWidget)
self.output.flush()
result = json.loads(stdout.getvalue())[0]
self.assertEquals(result["full_text"], "{}{}".format(
self.someWidget.full_text(), self.theme.suffix(self.someWidget))
@ -71,6 +81,7 @@ class TestI3BarOutput(unittest.TestCase):
self.theme.set_suffix(" - ")
self.theme.set_prefix(" * ")
self.output.draw(self.someWidget)
self.output.flush()
result = json.loads(stdout.getvalue())[0]
self.assertEquals(result["full_text"], "{}{}{}".format(
self.theme.prefix(self.someWidget), self.someWidget.full_text(), self.theme.suffix(self.someWidget))

View file

@ -13,12 +13,18 @@ class MockOutput(object):
def stop(self):
pass
def draw(self, widgets, engine):
def draw(self, widget, engine):
engine.stop()
def begin(self):
pass
def flush(self):
pass
def end(self):
pass
class MockWidget(object):
def __init__(self, text):
self._text = text

View file

@ -1,5 +1,5 @@
{
"defaults": { "separator": "" },
"defaults": { "separator": "", "padding": " " },
"date": { "prefix": "" },
"time": { "prefix": "" },
"memory": { "prefix": "" },