[themes] Make it possible to merge themes to get "icon themes"

Add code that allows themes to be merged (i.e. if certain elements are
not present in a theme, another theme can be "overlaid" to add missing
elements).

Effectively, this is used to create the logical concept of an "icon
theme", which is loaded after the main theme. So, the main theme can
define colors, and the icon theme fills in any missing elements
(practically, all the icons in the form of prefixes and suffixes).

Icon sets are defined in a theme using the "icons" directive, which
should be an array.

see #17
This commit is contained in:
Tobi-wan Kenobi 2016-11-25 21:06:24 +01:00
parent f6db8b0a85
commit 209fa83324
10 changed files with 296 additions and 600 deletions

View file

@ -1,4 +1,5 @@
import os
import copy
import json
import yaml
import glob
@ -14,16 +15,44 @@ class Theme:
def __init__(self, config):
self._config = config
if os.path.isfile("{}/{}.yaml".format(getpath(), config.theme())):
with open("{}/{}.yaml".format(getpath(), config.theme())) as f:
self._data = yaml.load(f)
else:
with open("{}/{}.json".format(getpath(), config.theme())) as f:
self._data = json.load(f)
self._data = self.get_theme(config.theme())
for iconset in self._data.get("icons", []):
self.merge(self._data, self.get_theme(iconset))
self._defaults = self._data.get("defaults", {})
self._cycles = self._defaults.get("cycle", [])
self.begin()
def get_theme(self, name):
for path in [ getpath(), "{}/icons/".format(getpath()) ]:
if os.path.isfile("{}/{}.yaml".format(path, name)):
with open("{}/{}.yaml".format(path, name)) as f:
return yaml.load(f)
if os.path.isfile("{}/{}.json".format(path, name)):
with open("{}/{}.json".format(path, name)) as f:
return json.load(f)
return None
# algorithm copied from
# http://blog.impressiver.com/post/31434674390/deep-merge-multiple-python-dicts
# nicely done :)
def merge(self, target, *args):
if len(args) > 1:
for item in args:
self.merge(item)
return target
item = args[0]
if not isinstance(item, dict):
return item
for key, value in item.iteritems():
if key in target and isinstance(target[key], dict):
self.merge(target[key], value)
else:
target[key] = copy.deepcopy(value)
return target
def begin(self):
self._config.set("theme.cycleidx", 0)
self._cycle = self._cycles[0] if len(self._cycles) > 0 else {}