From f72ac0ca991d3310620a82283e877645649f60d3 Mon Sep 17 00:00:00 2001 From: Tobias Witek Date: Sun, 5 Nov 2017 09:08:01 +0100 Subject: [PATCH 1/5] [core/theme] Add ~/.config/bumblebee-status/themes to theme directories Allow users to create themes outside the main theme tree. see #203 --- bumblebee/theme.py | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/bumblebee/theme.py b/bumblebee/theme.py index ee6f330..a7c99cb 100644 --- a/bumblebee/theme.py +++ b/bumblebee/theme.py @@ -12,14 +12,18 @@ import bumblebee.error def theme_path(): """Return the path of the theme directory""" - return os.path.dirname("{}/../themes/".format(os.path.dirname(os.path.realpath(__file__)))) + return [ + os.path.dirname("{}/../themes/".format(os.path.dirname(os.path.realpath(__file__)))), + os.path.dirname(os.path.expanduser("~/.config/bumblebee-status/themes/")), + ] def themes(): result = [] - for filename in glob.iglob("{}/*.json".format(theme_path())): - if "test" not in filename: - result.append(os.path.basename(filename).replace(".json", "")) + for path in theme_path(): + for filename in glob.iglob("{}/*.json".format(path)): + if "test" not in filename: + result.append(os.path.basename(filename).replace(".json", "")) return result class Theme(object): @@ -119,21 +123,26 @@ class Theme(object): def _load_icons(self, name): """Load icons for a theme""" - path = "{}/icons/".format(theme_path()) - return self.load(name, path=path) + result = {} + for path in theme_path(): + self._merge(result, self.load(name, path="{}/icons/".format(path))) + return result def load(self, name, path=theme_path()): """Load and parse a theme file""" - themefile = "{}/{}.json".format(path, name) + if not isinstance(path, list): + path = [path] + for p in path: + themefile = "{}/{}.json".format(p, name) - if os.path.isfile(themefile): - try: - with io.open(themefile, encoding="utf-8") as data: - return json.load(data) - except ValueError as exception: - raise bumblebee.error.ThemeLoadError("JSON error: {}".format(exception)) - else: - raise bumblebee.error.ThemeLoadError("no such theme: {}".format(name)) + if os.path.isfile(themefile): + try: + with io.open(themefile, encoding="utf-8") as data: + return json.load(data) + except ValueError as exception: + raise bumblebee.error.ThemeLoadError("JSON error: {}".format(exception)) + + return {} def _get(self, widget, name, default=None): """Return the config value 'name' for 'widget'""" From 9d7fc5c1d44705d7a67eaf3fd8ea46f18eaa6b93 Mon Sep 17 00:00:00 2001 From: Tobias Witek Date: Sat, 18 Nov 2017 14:51:25 +0100 Subject: [PATCH 2/5] [core/themes] De-duplicate theme names List themes only once, even if they are present multiple times in different locations. (Yes, I know that list(set(result)) would do the same, but here, I'd like to not waste memory and be a bit faster). see #203 --- bumblebee/theme.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bumblebee/theme.py b/bumblebee/theme.py index a7c99cb..4224c0c 100644 --- a/bumblebee/theme.py +++ b/bumblebee/theme.py @@ -18,12 +18,14 @@ def theme_path(): ] def themes(): - result = [] + themes = {} for path in theme_path(): for filename in glob.iglob("{}/*.json".format(path)): if "test" not in filename: - result.append(os.path.basename(filename).replace(".json", "")) + themes[os.path.basename(filename).replace(".json", "")] = 1 + result = themes.keys() + result.sort() return result class Theme(object): From 15c78cd6a6f145fff7f1a25d02be508b1342f7ad Mon Sep 17 00:00:00 2001 From: Tobias Witek Date: Sat, 18 Nov 2017 14:56:44 +0100 Subject: [PATCH 3/5] [core/themes] Add theme merging from different locations Theme files with the same name, but in different theme locations, are now merged together. see #203 --- bumblebee/theme.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bumblebee/theme.py b/bumblebee/theme.py index 4224c0c..ff4ab00 100644 --- a/bumblebee/theme.py +++ b/bumblebee/theme.py @@ -132,6 +132,7 @@ class Theme(object): def load(self, name, path=theme_path()): """Load and parse a theme file""" + result = None if not isinstance(path, list): path = [path] for p in path: @@ -140,11 +141,14 @@ class Theme(object): if os.path.isfile(themefile): try: with io.open(themefile, encoding="utf-8") as data: - return json.load(data) + if result is None: + result = json.load(data) + else: + self._merge(result, json.load(data)) except ValueError as exception: raise bumblebee.error.ThemeLoadError("JSON error: {}".format(exception)) - return {} + return result def _get(self, widget, name, default=None): """Return the config value 'name' for 'widget'""" From 56b2981379271a755e34069f8b9a6a654e8777b6 Mon Sep 17 00:00:00 2001 From: Tobias Witek Date: Sat, 18 Nov 2017 14:59:30 +0100 Subject: [PATCH 4/5] [core/themes] Fix exception on missing theme If no theme is found, raise an exception. see #203 --- bumblebee/theme.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bumblebee/theme.py b/bumblebee/theme.py index ff4ab00..41f2e32 100644 --- a/bumblebee/theme.py +++ b/bumblebee/theme.py @@ -148,6 +148,8 @@ class Theme(object): except ValueError as exception: raise bumblebee.error.ThemeLoadError("JSON error: {}".format(exception)) + if not result: + raise bumblebee.error.ThemeLoadError("no such theme") return result def _get(self, widget, name, default=None): From 3fe2088d06afca0ead454346d1933eac1f9cef42 Mon Sep 17 00:00:00 2001 From: Tobias Witek Date: Sun, 26 Nov 2017 19:56:57 +0100 Subject: [PATCH 5/5] [tests] fix unit tests --- bumblebee/theme.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bumblebee/theme.py b/bumblebee/theme.py index 41f2e32..b1b1324 100644 --- a/bumblebee/theme.py +++ b/bumblebee/theme.py @@ -36,7 +36,10 @@ class Theme(object): self._cycle = {} self._prevbg = None self._colorset = {} - self._init(self.load(name)) + data = self.load(name) + if not data: + raise bumblebee.error.ThemeLoadError("no such theme") + self._init(data) def _init(self, data): """Initialize theme from data structure""" @@ -148,8 +151,6 @@ class Theme(object): except ValueError as exception: raise bumblebee.error.ThemeLoadError("JSON error: {}".format(exception)) - if not result: - raise bumblebee.error.ThemeLoadError("no such theme") return result def _get(self, widget, name, default=None):