120 lines
3.4 KiB
Python
120 lines
3.4 KiB
Python
|
#!/usr/bin/env python3
|
||
|
|
||
|
"""
|
||
|
Based on labwc-gtktheme.py:
|
||
|
Create labwc theme based on the current Gtk theme
|
||
|
|
||
|
SPDX-License-Identifier: GPL-2.0-only
|
||
|
|
||
|
Copyright (C) @Misko_2083 2019
|
||
|
Copyright (C) Johan Malm 2019-2022
|
||
|
"""
|
||
|
|
||
|
import os
|
||
|
import errno
|
||
|
from tokenize import tokenize, NUMBER, NAME, OP
|
||
|
from io import BytesIO
|
||
|
import gi
|
||
|
gi.require_version("Gtk", "3.0")
|
||
|
from gi.repository import Gtk
|
||
|
|
||
|
def parse(tokens):
|
||
|
"""
|
||
|
Parse css color expression token list and return red/green/blue values
|
||
|
Valid name-tokens include 'rgb' and 'rgba', whereas 'alpha', 'shade' and
|
||
|
'mix' are ignored. @other-color references are still to be implemented.
|
||
|
"""
|
||
|
nr_colors_to_parse = 0
|
||
|
in_label = False
|
||
|
color = []
|
||
|
for toknum, tokval, _, _, _ in tokens:
|
||
|
if '@' in tokval:
|
||
|
in_label = True
|
||
|
continue
|
||
|
if toknum == NAME and in_label:
|
||
|
color.clear()
|
||
|
color.append(f"@{tokval}")
|
||
|
return color
|
||
|
if nr_colors_to_parse > 0:
|
||
|
if toknum == OP and tokval in ')':
|
||
|
print("warn: still parsing numbers; did not expect ')'")
|
||
|
if toknum == NUMBER:
|
||
|
color.append(tokval)
|
||
|
nr_colors_to_parse -= 1
|
||
|
continue
|
||
|
if toknum == NAME and tokval in 'rgb':
|
||
|
nr_colors_to_parse = 3
|
||
|
elif toknum == NAME and tokval in 'rgba':
|
||
|
nr_colors_to_parse = 4
|
||
|
return color
|
||
|
|
||
|
def color_hex(color):
|
||
|
""" return rrggbb color hex from list [r, g, b,...] """
|
||
|
if not color:
|
||
|
return "None"
|
||
|
elif len(color) < 3:
|
||
|
return f"{color[0]}"
|
||
|
return '{:02x}{:02x}{:02x}'.format(*(int(x) for x in color[:3]))
|
||
|
|
||
|
def hex_from_expr(line):
|
||
|
""" parse color expression to return hex style rrggbb """
|
||
|
tokens = tokenize(BytesIO(line.encode('utf-8')).readline)
|
||
|
color = parse(tokens)
|
||
|
return color_hex(color)
|
||
|
|
||
|
def parse_section(lines, name):
|
||
|
theme = {}
|
||
|
inside = False
|
||
|
for line in lines:
|
||
|
if f"{name} {{" in line:
|
||
|
inside = True
|
||
|
continue
|
||
|
if inside:
|
||
|
if "}" in line or "{" in line:
|
||
|
inside = False
|
||
|
break
|
||
|
if 'color' not in line:
|
||
|
continue
|
||
|
key, value = line.strip().split(":", maxsplit=1)
|
||
|
theme[f'{name}.{key.replace(" ", "")}'] = hex_from_expr(value)
|
||
|
return theme
|
||
|
|
||
|
def resolve_labels(theme):
|
||
|
for key, label in theme.items():
|
||
|
if '@' in label:
|
||
|
for tmp, value in theme.items():
|
||
|
if tmp == label[1:]:
|
||
|
theme[key] = value
|
||
|
return resolve_labels(theme)
|
||
|
return theme
|
||
|
|
||
|
def main():
|
||
|
""" main """
|
||
|
|
||
|
gset = Gtk.Settings.get_default()
|
||
|
themename = gset.get_property("gtk-theme-name")
|
||
|
css = Gtk.CssProvider.get_named(themename).to_string()
|
||
|
|
||
|
lines = css.split("\n")
|
||
|
theme = {}
|
||
|
|
||
|
# Parse @define-color lines using syntax "@define-color <key> <value>"
|
||
|
for line in lines:
|
||
|
if "@define-color" not in line:
|
||
|
continue
|
||
|
x = line.split(" ", maxsplit=2)
|
||
|
theme[x[1]] = hex_from_expr(x[2])
|
||
|
|
||
|
# Add the color definitions in the headerbar{} and menu{} sections
|
||
|
theme |= parse_section(lines, "headerbar")
|
||
|
theme |= parse_section(lines, "menu")
|
||
|
|
||
|
theme = resolve_labels(theme)
|
||
|
|
||
|
for key, value in theme.items():
|
||
|
print(f"{key}: {value}")
|
||
|
return
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|