[core/themes] Add "cycling" support
Allow a theme to define a "cycle" of attributes that are cycled through on a widget-per-widget basis (e.g. for alternating the widget background). These cycles take precedence over the default values, but can be overridden by module-specific theme instructions. see #23
This commit is contained in:
parent
59fb47ae3b
commit
527489e0de
6 changed files with 87 additions and 32 deletions
|
@ -57,6 +57,7 @@ class I3BarOutput(object):
|
||||||
def begin(self):
|
def begin(self):
|
||||||
"""Start one output iteration"""
|
"""Start one output iteration"""
|
||||||
self._widgets = []
|
self._widgets = []
|
||||||
|
self._theme.reset()
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
"""Flushes output"""
|
"""Flushes output"""
|
||||||
|
|
|
@ -16,6 +16,7 @@ class Theme(object):
|
||||||
"""Represents a collection of icons and colors"""
|
"""Represents a collection of icons and colors"""
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self._init(self.load(name))
|
self._init(self.load(name))
|
||||||
|
self._widget = None
|
||||||
|
|
||||||
def _init(self, data):
|
def _init(self, data):
|
||||||
"""Initialize theme from data structure"""
|
"""Initialize theme from data structure"""
|
||||||
|
@ -23,14 +24,30 @@ class Theme(object):
|
||||||
self._merge(data, self._load_icons(iconset))
|
self._merge(data, self._load_icons(iconset))
|
||||||
self._theme = data
|
self._theme = data
|
||||||
self._defaults = data.get("defaults", {})
|
self._defaults = data.get("defaults", {})
|
||||||
|
self._cycles = self._theme.get("cycle", [])
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def data(self):
|
||||||
|
"""Return the raw theme data"""
|
||||||
|
return self._theme
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
"""Reset theme to initial state"""
|
||||||
|
self._cycle = self._cycles[0] if len(self._cycles) > 0 else {}
|
||||||
|
self._cycle_idx = 0
|
||||||
|
self._widget = None
|
||||||
|
|
||||||
def prefix(self, widget):
|
def prefix(self, widget):
|
||||||
"""Return the theme prefix for a widget's full text"""
|
"""Return the theme prefix for a widget's full text"""
|
||||||
return self._get(widget, "prefix", None)
|
padding = self._get(widget, "padding", "")
|
||||||
|
pre = self._get(widget, "prefix", None)
|
||||||
|
return u"{}{}{}".format(padding, pre, padding) if pre else None
|
||||||
|
|
||||||
def suffix(self, widget):
|
def suffix(self, widget):
|
||||||
"""Return the theme suffix for a widget's full text"""
|
"""Return the theme suffix for a widget's full text"""
|
||||||
return self._get(widget, "suffix", None)
|
padding = self._get(widget, "padding", "")
|
||||||
|
suf = self._get(widget, "suffix", None)
|
||||||
|
return u"{}{}{}".format(padding, suf, padding) if suf else None
|
||||||
|
|
||||||
def fg(self, widget):
|
def fg(self, widget):
|
||||||
"""Return the foreground color for this widget"""
|
"""Return the foreground color for this widget"""
|
||||||
|
@ -65,18 +82,21 @@ class Theme(object):
|
||||||
|
|
||||||
def _get(self, widget, name, default=None):
|
def _get(self, widget, name, default=None):
|
||||||
"""Return the config value 'name' for 'widget'"""
|
"""Return the config value 'name' for 'widget'"""
|
||||||
|
|
||||||
|
if not self._widget:
|
||||||
|
self._widget = widget
|
||||||
|
|
||||||
|
if self._widget != widget:
|
||||||
|
self._widget = widget
|
||||||
|
self._cycle_idx = (self._cycle_idx + 1) % len(self._cycles)
|
||||||
|
self._cycle = self._cycles[self._cycle_idx]
|
||||||
|
|
||||||
module_theme = self._theme.get(widget.module, {})
|
module_theme = self._theme.get(widget.module, {})
|
||||||
|
|
||||||
padding = None
|
|
||||||
if name != "padding":
|
|
||||||
padding = self._get(widget, "padding")
|
|
||||||
|
|
||||||
value = self._defaults.get(name, default)
|
value = self._defaults.get(name, default)
|
||||||
|
value = self._cycle.get(name, value)
|
||||||
value = module_theme.get(name, value)
|
value = module_theme.get(name, value)
|
||||||
|
|
||||||
if value and padding:
|
|
||||||
value = u"{}{}{}".format(padding, value, padding)
|
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
# algorithm copied from
|
# algorithm copied from
|
||||||
|
|
|
@ -10,18 +10,22 @@ class TestTheme(unittest.TestCase):
|
||||||
self.nonexistentThemeName = "no-such-theme"
|
self.nonexistentThemeName = "no-such-theme"
|
||||||
self.invalidThemeName = "invalid"
|
self.invalidThemeName = "invalid"
|
||||||
self.validThemeName = "test"
|
self.validThemeName = "test"
|
||||||
self.someWidget = MockWidget("foo")
|
self.themedWidget = MockWidget("foo")
|
||||||
self.theme = Theme(self.validThemeName)
|
self.theme = Theme(self.validThemeName)
|
||||||
|
self.cycleTheme = Theme("test_cycle")
|
||||||
|
self.anyWidget = MockWidget("bla")
|
||||||
|
self.anotherWidget = MockWidget("blub")
|
||||||
|
|
||||||
|
data = self.theme.data()
|
||||||
self.widgetTheme = "test-widget"
|
self.widgetTheme = "test-widget"
|
||||||
self.defaultColor = "#000000"
|
self.defaultColor = data["defaults"]["fg"]
|
||||||
self.defaultBgColor = "#111111"
|
self.defaultBgColor = data["defaults"]["bg"]
|
||||||
self.widgetBgColor = "#222222"
|
self.widgetColor = data[self.widgetTheme]["fg"]
|
||||||
self.defaultPrefix = "default-prefix"
|
self.widgetBgColor = data[self.widgetTheme]["bg"]
|
||||||
self.defaultSuffix = "default-suffix"
|
self.defaultPrefix = data["defaults"]["prefix"]
|
||||||
self.widgetPrefix = "widget-prefix"
|
self.defaultSuffix = data["defaults"]["suffix"]
|
||||||
self.widgetSuffix = "widget-suffix"
|
self.widgetPrefix = data[self.widgetTheme]["prefix"]
|
||||||
self.widgetColor = "#ababab"
|
self.widgetSuffix = data[self.widgetTheme]["suffix"]
|
||||||
|
|
||||||
def test_load_valid_theme(self):
|
def test_load_valid_theme(self):
|
||||||
try:
|
try:
|
||||||
|
@ -38,23 +42,32 @@ class TestTheme(unittest.TestCase):
|
||||||
Theme(self.invalidThemeName)
|
Theme(self.invalidThemeName)
|
||||||
|
|
||||||
def test_default_prefix(self):
|
def test_default_prefix(self):
|
||||||
self.assertEquals(self.theme.prefix(self.someWidget), self.defaultPrefix)
|
self.assertEquals(self.theme.prefix(self.themedWidget), self.defaultPrefix)
|
||||||
|
|
||||||
def test_default_suffix(self):
|
def test_default_suffix(self):
|
||||||
self.assertEquals(self.theme.suffix(self.someWidget), self.defaultSuffix)
|
self.assertEquals(self.theme.suffix(self.themedWidget), self.defaultSuffix)
|
||||||
|
|
||||||
def test_widget_prefix(self):
|
def test_widget_prefix(self):
|
||||||
self.someWidget.module = self.widgetTheme
|
self.themedWidget.module = self.widgetTheme
|
||||||
self.assertEquals(self.theme.prefix(self.someWidget), self.widgetPrefix)
|
self.assertEquals(self.theme.prefix(self.themedWidget), self.widgetPrefix)
|
||||||
|
|
||||||
def test_widget_fg(self):
|
def test_widget_fg(self):
|
||||||
self.assertEquals(self.theme.fg(self.someWidget), self.defaultColor)
|
self.assertEquals(self.theme.fg(self.themedWidget), self.defaultColor)
|
||||||
self.someWidget.module = self.widgetTheme
|
self.themedWidget.module = self.widgetTheme
|
||||||
self.assertEquals(self.theme.fg(self.someWidget), self.widgetColor)
|
self.assertEquals(self.theme.fg(self.themedWidget), self.widgetColor)
|
||||||
|
|
||||||
def test_widget_bg(self):
|
def test_widget_bg(self):
|
||||||
self.assertEquals(self.theme.bg(self.someWidget), self.defaultBgColor)
|
self.assertEquals(self.theme.bg(self.themedWidget), self.defaultBgColor)
|
||||||
self.someWidget.module = self.widgetTheme
|
self.themedWidget.module = self.widgetTheme
|
||||||
self.assertEquals(self.theme.bg(self.someWidget), self.widgetBgColor)
|
self.assertEquals(self.theme.bg(self.themedWidget), self.widgetBgColor)
|
||||||
|
|
||||||
|
def test_reset(self):
|
||||||
|
theme = self.cycleTheme
|
||||||
|
data = theme.data()
|
||||||
|
theme.reset()
|
||||||
|
self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"])
|
||||||
|
self.assertEquals(theme.fg(self.anotherWidget), data["cycle"][1]["fg"])
|
||||||
|
theme.reset()
|
||||||
|
self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"])
|
||||||
|
|
||||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||||
|
|
|
@ -46,6 +46,9 @@ class MockTheme(object):
|
||||||
self.attr_fg = None
|
self.attr_fg = None
|
||||||
self.attr_bg = None
|
self.attr_bg = None
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def prefix(self, widget):
|
def prefix(self, widget):
|
||||||
return self.attr_prefix
|
return self.attr_prefix
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,6 @@
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"default-separators": false,
|
"default-separators": false,
|
||||||
"separator-block-width": 0,
|
"separator-block-width": 0,
|
||||||
"cycle": [
|
|
||||||
{ "fg": "#93a1a1", "bg": "#002b36" },
|
|
||||||
{ "fg": "#eee8d5", "bg": "#586e75" }
|
|
||||||
],
|
|
||||||
"warning": {
|
"warning": {
|
||||||
"fg": "#002b36",
|
"fg": "#002b36",
|
||||||
"bg": "#b58900"
|
"bg": "#b58900"
|
||||||
|
@ -16,6 +12,10 @@
|
||||||
"bg": "#dc322f"
|
"bg": "#dc322f"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cycle": [
|
||||||
|
{ "fg": "#93a1a1", "bg": "#002b36" },
|
||||||
|
{ "fg": "#eee8d5", "bg": "#586e75" }
|
||||||
|
],
|
||||||
"dnf": {
|
"dnf": {
|
||||||
"good": {
|
"good": {
|
||||||
"fg": "#002b36",
|
"fg": "#002b36",
|
||||||
|
|
18
themes/test_cycle.json
Normal file
18
themes/test_cycle.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"icons": [ "test" ],
|
||||||
|
"defaults": {
|
||||||
|
"prefix": "default-prefix",
|
||||||
|
"suffix": "default-suffix",
|
||||||
|
"fg": "#000000",
|
||||||
|
"bg": "#111111"
|
||||||
|
},
|
||||||
|
"cycle": [
|
||||||
|
{ "fg": "#aa0000" },
|
||||||
|
{ "fg": "#00aa00" },
|
||||||
|
{ "fg": "#0000aa" }
|
||||||
|
],
|
||||||
|
"test-widget": {
|
||||||
|
"fg": "#ababab",
|
||||||
|
"bg": "#222222"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue