mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-17 05:54:59 +02:00
Implement changing transparent background colors via remote control
This commit is contained in:
@@ -2617,7 +2617,7 @@ class Boss:
|
|||||||
window.screen.disable_ligatures = strategy
|
window.screen.disable_ligatures = strategy
|
||||||
window.refresh()
|
window.refresh()
|
||||||
|
|
||||||
def patch_colors(self, spec: dict[str, Optional[int]], configured: bool = False) -> None:
|
def patch_colors(self, spec: dict[str, Optional[int]], transparent_background_colors: tuple[tuple[Color, float], ...], configured: bool = False) -> None:
|
||||||
opts = get_options()
|
opts = get_options()
|
||||||
if configured:
|
if configured:
|
||||||
for k, v in spec.items():
|
for k, v in spec.items():
|
||||||
@@ -2627,6 +2627,7 @@ class Boss:
|
|||||||
setattr(opts, k, None)
|
setattr(opts, k, None)
|
||||||
else:
|
else:
|
||||||
setattr(opts, k, color_from_int(v))
|
setattr(opts, k, color_from_int(v))
|
||||||
|
opts.transparent_background_colors = transparent_background_colors
|
||||||
for tm in self.all_tab_managers:
|
for tm in self.all_tab_managers:
|
||||||
tm.tab_bar.patch_colors(spec)
|
tm.tab_bar.patch_colors(spec)
|
||||||
tm.tab_bar.layout()
|
tm.tab_bar.layout()
|
||||||
|
|||||||
@@ -60,6 +60,17 @@ create_256_color_table(void) {
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_transparent_background_colors(TransparentDynamicColor *dest, PyObject *src) {
|
||||||
|
memset(dest, 0, sizeof(((ColorProfile*)0)->configured_transparent_colors));
|
||||||
|
for (Py_ssize_t i = 0; i < MIN(PyTuple_GET_SIZE(src), (Py_ssize_t)arraysz(((ColorProfile*)0)->configured_transparent_colors)); i++) {
|
||||||
|
PyObject *e = PyTuple_GET_ITEM(src, i);
|
||||||
|
dest[i].color = ((Color*)(PyTuple_GET_ITEM(e, 0)))->color.val & 0xffffff;
|
||||||
|
dest[i].opacity = (float)PyFloat_AsDouble(PyTuple_GET_ITEM(e, 1));
|
||||||
|
dest[i].is_set = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
set_configured_colors(ColorProfile *self, PyObject *opts) {
|
set_configured_colors(ColorProfile *self, PyObject *opts) {
|
||||||
#define n(which, attr) { \
|
#define n(which, attr) { \
|
||||||
@@ -82,15 +93,9 @@ set_configured_colors(ColorProfile *self, PyObject *opts) {
|
|||||||
n(highlight_fg, selection_foreground); n(highlight_bg, selection_background);
|
n(highlight_fg, selection_foreground); n(highlight_bg, selection_background);
|
||||||
n(visual_bell_color, visual_bell_color);
|
n(visual_bell_color, visual_bell_color);
|
||||||
#undef n
|
#undef n
|
||||||
memset(self->configured_transparent_colors, 0, sizeof(self->configured_transparent_colors));
|
|
||||||
RAII_PyObject(src, PyObject_GetAttrString(opts, "transparent_background_colors"));
|
RAII_PyObject(src, PyObject_GetAttrString(opts, "transparent_background_colors"));
|
||||||
if (!src) { PyErr_SetString(PyExc_TypeError, "No transparent_background_colors on opts object"); return false; }
|
if (!src) { PyErr_SetString(PyExc_TypeError, "No transparent_background_colors on opts object"); return false; }
|
||||||
for (Py_ssize_t i = 0; i < MIN(PyTuple_GET_SIZE(src), (Py_ssize_t)arraysz(self->configured_transparent_colors)); i++) {
|
set_transparent_background_colors(self->configured_transparent_colors, src);
|
||||||
PyObject *e = PyTuple_GET_ITEM(src, i);
|
|
||||||
self->configured_transparent_colors[i].color = ((Color*)(PyTuple_GET_ITEM(e, 0)))->color.val & 0xffffff;
|
|
||||||
self->configured_transparent_colors[i].opacity = (float)PyFloat_AsDouble(PyTuple_GET_ITEM(e, 1));
|
|
||||||
self->configured_transparent_colors[i].is_set = true;
|
|
||||||
}
|
|
||||||
return PyErr_Occurred() ? false : true;
|
return PyErr_Occurred() ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,8 +209,8 @@ patch_color_table(const char *key, PyObject *profiles, PyObject *spec, size_t wh
|
|||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
|
patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
|
||||||
PyObject *spec, *profiles, *v; ColorProfile *self; int change_configured;
|
PyObject *spec, *transparent_background_colors, *profiles, *v; ColorProfile *self; int change_configured;
|
||||||
if (!PyArg_ParseTuple(args, "O!O!p", &PyDict_Type, &spec, &PyTuple_Type, &profiles, &change_configured)) return NULL;
|
if (!PyArg_ParseTuple(args, "O!O!O!p", &PyDict_Type, &spec, &PyTuple_Type, &transparent_background_colors, &PyTuple_Type, &profiles, &change_configured)) return NULL;
|
||||||
char key[32] = {0};
|
char key[32] = {0};
|
||||||
for (size_t i = 0; i < arraysz(FG_BG_256); i++) {
|
for (size_t i = 0; i < arraysz(FG_BG_256); i++) {
|
||||||
snprintf(key, sizeof(key) - 1, "color%zu", i);
|
snprintf(key, sizeof(key) - 1, "color%zu", i);
|
||||||
@@ -239,7 +244,12 @@ patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
|
|||||||
S(cursor_text_color, cursor_text_color); S(visual_bell_color, visual_bell_color);
|
S(cursor_text_color, cursor_text_color); S(visual_bell_color, visual_bell_color);
|
||||||
#undef SI
|
#undef SI
|
||||||
#undef S
|
#undef S
|
||||||
// TODO: Patch transparent_colors
|
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) {
|
||||||
|
self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i);
|
||||||
|
set_transparent_background_colors(self->overriden_transparent_colors, transparent_background_colors);
|
||||||
|
if (change_configured) set_transparent_background_colors(self->configured_transparent_colors, transparent_background_colors);
|
||||||
|
}
|
||||||
|
if (PyErr_Occurred()) return NULL;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,20 +303,21 @@ colorprofile_to_color_with_fallback(ColorProfile *self, DynamicColor entry, Dyna
|
|||||||
}
|
}
|
||||||
return entry.rgb;
|
return entry.rgb;
|
||||||
}
|
}
|
||||||
|
static Color* alloc_color(unsigned char r, unsigned char g, unsigned char b, unsigned a);
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
as_dict(ColorProfile *self, PyObject *args UNUSED) {
|
as_dict(ColorProfile *self, PyObject *args UNUSED) {
|
||||||
#define as_dict_doc "Return all colors as a dictionary of color_name to integer or None (names are the same as used in kitty.conf)"
|
#define as_dict_doc "Return all colors as a dictionary of color_name to integer or None (names are the same as used in kitty.conf)"
|
||||||
PyObject *ans = PyDict_New();
|
RAII_PyObject(ans, PyDict_New());
|
||||||
if (ans == NULL) return PyErr_NoMemory();
|
if (ans == NULL) return PyErr_NoMemory();
|
||||||
for (unsigned i = 0; i < arraysz(self->color_table); i++) {
|
for (unsigned i = 0; i < arraysz(self->color_table); i++) {
|
||||||
static char buf[32] = {0};
|
static char buf[32] = {0};
|
||||||
snprintf(buf, sizeof(buf) - 1, "color%u", i);
|
snprintf(buf, sizeof(buf) - 1, "color%u", i);
|
||||||
PyObject *val = PyLong_FromUnsignedLong(self->color_table[i]);
|
PyObject *val = PyLong_FromUnsignedLong(self->color_table[i]);
|
||||||
if (!val) { Py_CLEAR(ans); return PyErr_NoMemory(); }
|
if (!val) { return PyErr_NoMemory(); }
|
||||||
int ret = PyDict_SetItemString(ans, buf, val);
|
int ret = PyDict_SetItemString(ans, buf, val);
|
||||||
Py_CLEAR(val);
|
Py_CLEAR(val);
|
||||||
if (ret != 0) { Py_CLEAR(ans); return NULL; }
|
if (ret != 0) { return NULL; }
|
||||||
}
|
}
|
||||||
#define D(attr, name) { \
|
#define D(attr, name) { \
|
||||||
if (self->overridden.attr.type != COLOR_NOT_SET) { \
|
if (self->overridden.attr.type != COLOR_NOT_SET) { \
|
||||||
@@ -317,18 +328,33 @@ as_dict(ColorProfile *self, PyObject *args UNUSED) {
|
|||||||
color_type c = colorprofile_to_color(self, self->overridden.attr, self->configured.attr).rgb; \
|
color_type c = colorprofile_to_color(self, self->overridden.attr, self->configured.attr).rgb; \
|
||||||
val = PyLong_FromUnsignedLong(c); \
|
val = PyLong_FromUnsignedLong(c); \
|
||||||
} \
|
} \
|
||||||
if (!val) { Py_CLEAR(ans); return NULL; } \
|
if (!val) { return NULL; } \
|
||||||
ret = PyDict_SetItemString(ans, #name, val); \
|
ret = PyDict_SetItemString(ans, #name, val); \
|
||||||
Py_CLEAR(val); \
|
Py_CLEAR(val); \
|
||||||
if (ret != 0) { Py_CLEAR(ans); return NULL; } \
|
if (ret != 0) { return NULL; } \
|
||||||
}}
|
}}
|
||||||
D(default_fg, foreground); D(default_bg, background);
|
D(default_fg, foreground); D(default_bg, background);
|
||||||
D(cursor_color, cursor); D(cursor_text_color, cursor_text); D(highlight_fg, selection_foreground);
|
D(cursor_color, cursor); D(cursor_text_color, cursor_text); D(highlight_fg, selection_foreground);
|
||||||
D(highlight_bg, selection_background); D(visual_bell_color, visual_bell_color);
|
D(highlight_bg, selection_background); D(visual_bell_color, visual_bell_color);
|
||||||
|
RAII_PyObject(transparent_background_colors, PyList_New(0));
|
||||||
|
if (!transparent_background_colors) return NULL;
|
||||||
|
for (size_t i = 0; i < arraysz(self->overriden_transparent_colors); i++) {
|
||||||
|
TransparentDynamicColor *c = NULL;
|
||||||
|
if (self->overriden_transparent_colors[i].is_set) c = self->overriden_transparent_colors + i;
|
||||||
|
else if (self->configured_transparent_colors[i].is_set) c = self->configured_transparent_colors + i;
|
||||||
|
if (c) {
|
||||||
|
RAII_PyObject(t, Py_BuildValue("Nf", alloc_color((c->color >> 16) & 0xff, (c->color >> 8) & 0xff, c->color & 0xff, 0), c->opacity));
|
||||||
|
if (!t) return NULL;
|
||||||
|
if (PyList_Append(transparent_background_colors, t) != 0) return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (PyList_GET_SIZE(transparent_background_colors)) {
|
||||||
|
RAII_PyObject(t, PyList_AsTuple(transparent_background_colors));
|
||||||
|
if (!t) return NULL;
|
||||||
|
if (PyDict_SetItemString(ans, "transparent_background_colors", t) != 0) return NULL;
|
||||||
|
}
|
||||||
#undef D
|
#undef D
|
||||||
// TODO: Add transparent_colors
|
return Py_NewRef(ans);
|
||||||
return ans;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
@@ -478,7 +504,6 @@ default_color_table(PyObject *self UNUSED, PyObject *args UNUSED) {
|
|||||||
|
|
||||||
// Boilerplate {{{
|
// Boilerplate {{{
|
||||||
|
|
||||||
static Color* alloc_color(unsigned char r, unsigned char g, unsigned char b, unsigned a);
|
|
||||||
#define CGETSET(name, nullable) \
|
#define CGETSET(name, nullable) \
|
||||||
static PyObject* name##_get(ColorProfile *self, void UNUSED *closure) { \
|
static PyObject* name##_get(ColorProfile *self, void UNUSED *closure) { \
|
||||||
DynamicColor ans = colorprofile_to_color(self, self->overridden.name, self->configured.name); \
|
DynamicColor ans = colorprofile_to_color(self, self->overridden.name, self->configured.name); \
|
||||||
|
|||||||
@@ -811,7 +811,7 @@ class ColorProfile:
|
|||||||
|
|
||||||
def __init__(self, opts: Optional[Options] = None): ...
|
def __init__(self, opts: Optional[Options] = None): ...
|
||||||
|
|
||||||
def as_dict(self) -> Dict[str, Optional[int]]:
|
def as_dict(self) -> Dict[str, int | None | tuple[tuple[Color, float], ...]]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def as_color(self, val: int) -> Optional[Color]:
|
def as_color(self, val: int) -> Optional[Color]:
|
||||||
@@ -833,7 +833,8 @@ class ColorProfile:
|
|||||||
|
|
||||||
|
|
||||||
def patch_color_profiles(
|
def patch_color_profiles(
|
||||||
spec: Dict[str, Optional[int]], profiles: Tuple[ColorProfile, ...], change_configured: bool
|
spec: Dict[str, Optional[int]], transparent_background_colors: tuple[tuple[Color, float], ...],
|
||||||
|
profiles: Tuple[ColorProfile, ...], change_configured: bool
|
||||||
) -> None:
|
) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -478,9 +478,9 @@ class LaunchKwds(TypedDict):
|
|||||||
|
|
||||||
def apply_colors(window: Window, spec: Sequence[str]) -> None:
|
def apply_colors(window: Window, spec: Sequence[str]) -> None:
|
||||||
from kitty.rc.set_colors import parse_colors
|
from kitty.rc.set_colors import parse_colors
|
||||||
colors = parse_colors(spec)
|
colors, transparent_background_colors = parse_colors(spec)
|
||||||
profiles = window.screen.color_profile,
|
profiles = window.screen.color_profile,
|
||||||
patch_color_profiles(colors, profiles, True)
|
patch_color_profiles(colors, transparent_background_colors, profiles, True)
|
||||||
|
|
||||||
|
|
||||||
def parse_var(defn: Iterable[str]) -> Iterator[tuple[str, str]]:
|
def parse_var(defn: Iterable[str]) -> Iterator[tuple[str, str]]:
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ configured colors.
|
|||||||
for k, v in windows[0].current_colors.items():
|
for k, v in windows[0].current_colors.items():
|
||||||
if v is None:
|
if v is None:
|
||||||
ans.pop(k, None)
|
ans.pop(k, None)
|
||||||
else:
|
elif isinstance(v, int):
|
||||||
ans[k] = color_from_int(v)
|
ans[k] = color_from_int(v)
|
||||||
tab = windows[0].tabref()
|
tab = windows[0].tabref()
|
||||||
tm = None if tab is None else tab.tab_manager_ref()
|
tm = None if tab is None else tab.tab_manager_ref()
|
||||||
|
|||||||
@@ -27,16 +27,19 @@ if TYPE_CHECKING:
|
|||||||
from kitty.cli_stub import SetColorsRCOptions as CLIOptions
|
from kitty.cli_stub import SetColorsRCOptions as CLIOptions
|
||||||
|
|
||||||
|
|
||||||
def parse_colors(args: Iterable[str]) -> Dict[str, Optional[int]]:
|
def parse_colors(args: Iterable[str]) -> tuple[Dict[str, Optional[int]], tuple[tuple[Color, float], ...]]:
|
||||||
from kitty.options.types import nullable_colors
|
from kitty.options.types import nullable_colors
|
||||||
colors: Dict[str, Optional[Color]] = {}
|
colors: Dict[str, Optional[Color]] = {}
|
||||||
nullable_color_map: Dict[str, Optional[int]] = {}
|
nullable_color_map: Dict[str, Optional[int]] = {}
|
||||||
|
transparent_background_colors = ()
|
||||||
for spec in args:
|
for spec in args:
|
||||||
if '=' in spec:
|
if '=' in spec:
|
||||||
colors.update(parse_config((spec.replace('=', ' '),)))
|
conf = parse_config((spec.replace('=', ' '),))
|
||||||
else:
|
else:
|
||||||
with open(os.path.expanduser(spec), encoding='utf-8', errors='replace') as f:
|
with open(os.path.expanduser(spec), encoding='utf-8', errors='replace') as f:
|
||||||
colors.update(parse_config(f))
|
conf = parse_config(f)
|
||||||
|
transparent_background_colors = conf.pop('transparent_background_colors', ())
|
||||||
|
colors.update(conf)
|
||||||
for k in nullable_colors:
|
for k in nullable_colors:
|
||||||
q = colors.pop(k, False)
|
q = colors.pop(k, False)
|
||||||
if q is not False:
|
if q is not False:
|
||||||
@@ -44,13 +47,13 @@ def parse_colors(args: Iterable[str]) -> Dict[str, Optional[int]]:
|
|||||||
nullable_color_map[k] = val
|
nullable_color_map[k] = val
|
||||||
ans: Dict[str, Optional[int]] = {k: int(v) for k, v in colors.items() if isinstance(v, Color)}
|
ans: Dict[str, Optional[int]] = {k: int(v) for k, v in colors.items() if isinstance(v, Color)}
|
||||||
ans.update(nullable_color_map)
|
ans.update(nullable_color_map)
|
||||||
return ans
|
return ans, transparent_background_colors
|
||||||
|
|
||||||
|
|
||||||
class SetColors(RemoteCommand):
|
class SetColors(RemoteCommand):
|
||||||
|
|
||||||
protocol_spec = __doc__ = '''
|
protocol_spec = __doc__ = '''
|
||||||
colors+/dict.colors: An object mapping names to colors as 24-bit RGB integers or null for nullable colors
|
colors+/dict.colors: An object mapping names to colors as 24-bit RGB integers or null for nullable colors. Or a string for transparent_background_colors.
|
||||||
match_window/str: Window to change colors in
|
match_window/str: Window to change colors in
|
||||||
match_tab/str: Tab to change colors in
|
match_tab/str: Tab to change colors in
|
||||||
all/bool: Boolean indicating change colors everywhere or not
|
all/bool: Boolean indicating change colors everywhere or not
|
||||||
@@ -88,14 +91,18 @@ this option, any color arguments are ignored and :option:`kitten @ set-colors --
|
|||||||
completion=RemoteCommand.CompletionSpec.from_string('type:file group:"CONF files", ext:conf'))
|
completion=RemoteCommand.CompletionSpec.from_string('type:file group:"CONF files", ext:conf'))
|
||||||
|
|
||||||
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
||||||
final_colors: Dict[str, Optional[int]] = {}
|
final_colors: Dict[str, int | None | str] = {}
|
||||||
|
transparent_background_colors: tuple[tuple[Color, float], ...] = ()
|
||||||
if not opts.reset:
|
if not opts.reset:
|
||||||
try:
|
try:
|
||||||
final_colors = parse_colors(args)
|
fc, transparent_background_colors = parse_colors(args)
|
||||||
except FileNotFoundError as err:
|
except FileNotFoundError as err:
|
||||||
raise ParsingOfArgsFailed(f'The colors configuration file {emph(err.filename)} was not found.') from err
|
raise ParsingOfArgsFailed(f'The colors configuration file {emph(err.filename)} was not found.') from err
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise ParsingOfArgsFailed(str(err)) from err
|
raise ParsingOfArgsFailed(str(err)) from err
|
||||||
|
final_colors.update(fc)
|
||||||
|
if transparent_background_colors:
|
||||||
|
final_colors['transparent_background_colors'] = ' '.join(f'{c.as_sharp}@{f}' for c, f in transparent_background_colors)
|
||||||
ans = {
|
ans = {
|
||||||
'match_window': opts.match, 'match_tab': opts.match_tab,
|
'match_window': opts.match, 'match_tab': opts.match_tab,
|
||||||
'all': opts.all or opts.reset, 'configured': opts.configured or opts.reset,
|
'all': opts.all or opts.reset, 'configured': opts.configured or opts.reset,
|
||||||
@@ -105,12 +112,18 @@ this option, any color arguments are ignored and :option:`kitten @ set-colors --
|
|||||||
|
|
||||||
def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType:
|
def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType:
|
||||||
windows = self.windows_for_payload(boss, window, payload_get)
|
windows = self.windows_for_payload(boss, window, payload_get)
|
||||||
colors: Dict[str, Optional[int]] = payload_get('colors')
|
colors: Dict[str, int | None] = payload_get('colors')
|
||||||
|
tbc = colors.get('transparent_background_colors')
|
||||||
if payload_get('reset'):
|
if payload_get('reset'):
|
||||||
colors = {k: None if v is None else int(v) for k, v in boss.color_settings_at_startup.items()}
|
colors = {k: None if v is None else int(v) for k, v in boss.color_settings_at_startup.items()}
|
||||||
profiles = tuple(w.screen.color_profile for w in windows if w)
|
profiles = tuple(w.screen.color_profile for w in windows if w)
|
||||||
patch_color_profiles(colors, profiles, payload_get('configured'))
|
if tbc:
|
||||||
boss.patch_colors(colors, payload_get('configured'))
|
from kitty.options.utils import transparent_background_colors
|
||||||
|
parsed_tbc = transparent_background_colors(str(tbc))
|
||||||
|
else:
|
||||||
|
parsed_tbc = ()
|
||||||
|
patch_color_profiles(colors, parsed_tbc, profiles, payload_get('configured'))
|
||||||
|
boss.patch_colors(colors, parsed_tbc, payload_get('configured'))
|
||||||
default_bg_changed = 'background' in colors
|
default_bg_changed = 'background' in colors
|
||||||
for w in windows:
|
for w in windows:
|
||||||
if w:
|
if w:
|
||||||
|
|||||||
@@ -805,7 +805,7 @@ class Window:
|
|||||||
return tab.overlay_parent(self)
|
return tab.overlay_parent(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_colors(self) -> dict[str, Optional[int]]:
|
def current_colors(self) -> dict[str, int | tuple[tuple[Color, float], ...] | None]:
|
||||||
return self.screen.color_profile.as_dict()
|
return self.screen.color_profile.as_dict()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ func set_color_in_color_map(key, val string, ans map[string]any, check_nullable,
|
|||||||
return fmt.Errorf("The color %s cannot be set to none", key)
|
return fmt.Errorf("The color %s cannot be set to none", key)
|
||||||
}
|
}
|
||||||
ans[key] = nil
|
ans[key] = nil
|
||||||
|
} else if key == "transparent_background_colors" {
|
||||||
|
ans[key] = val
|
||||||
} else {
|
} else {
|
||||||
col, err := style.ParseColor(val)
|
col, err := style.ParseColor(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user