diff --git a/docs/changelog.rst b/docs/changelog.rst index b76d0d88c..d6599827b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -40,6 +40,8 @@ Detailed list of changes - A new escape code ``[22J`` that moves the current contents of the screen into the scrollback before clearing it +- A new option :opt:`text_fg_override_threshold` to force text colors to have high contrast regardless of color scheme (:pull:`6283`) + - unicode_input kitten: Fix a regression in 0.28.0 that caused the order of recent and favorites entries to not be respected (:iss:`6214`) - unicode_input kitten: Fix a regression in 0.28.0 that caused editing of favorites to sometimes hang diff --git a/kitty/conf/generate.py b/kitty/conf/generate.py index b2dbf1773..f4cd886c8 100644 --- a/kitty/conf/generate.py +++ b/kitty/conf/generate.py @@ -391,7 +391,7 @@ def generate_c_conversion(loc: str, ctypes: List[Union[Option, MultiOption]]) -> lines: List[str] = [] basic_converters = { 'int': 'PyLong_AsLong', 'uint': 'PyLong_AsUnsignedLong', 'bool': 'PyObject_IsTrue', - 'float': 'PyFloat_AsFloat', 'double': 'PyFloat_AsDouble', + 'float': 'PyFloat_AsFloat', 'double': 'PyFloat_AsDouble', 'percent': 'percent', 'time': 'parse_s_double_to_monotonic_t', 'time-ms': 'parse_ms_long_to_monotonic_t' } diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 37d7e82eb..2d5fdd1f0 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -70,7 +70,7 @@ words "HELLO WORLD" display in kitty as "WORLD HELLO", and if you try to select a substring of an RTL-shaped string, you will get the character that would be there had the the string been LTR. For example, assuming the Hebrew word ירושלים, selecting the character that on the screen appears to be ם actually -writes into the selection buffer the character י. kitty's default behavior is +writes into the selection buffer the character י. kitty's default behavior is useful in conjunction with a filter to reverse the word order, however, if you wish to manipulate RTL glyphs, it can be very challenging to work with, so this option is provided to turn it off. Furthermore, this option can be used with the @@ -269,15 +269,15 @@ Then adjust the second parameter until it looks good. Then switch to a light the and adjust the first parameter until the perceived thickness matches the dark theme. ''') -opt('text_fg_override_threshold', 0, - ctype='!text_fg_override_threshold', - long_text=''' -The minimum accepted difference in luminance between the foreground and background -color, below which kitty will override the foreground color. It is percentage -ranging from :code:`0` to :code:`100`. If the difference in luminance of the -foreground and background is below this threshold, the foreground color will be set -to white if the background is dark or black if the background is light. The default -value is :code:`0`. +opt('text_fg_override_threshold', 0, option_type='float', ctype='percent', long_text=''' +The minimum accepted difference in luminance between the foreground and background +color, below which kitty will override the foreground color. It is percentage +ranging from :code:`0` to :code:`100`. If the difference in luminance of the +foreground and background is below this threshold, the foreground color will be set +to white if the background is dark or black if the background is light. The default +value is :code:`0`, which means no overriding is performed. Useful when working with applications +that use colors that do not contrast well with your preferred color scheme. Changing this option +from zero to a non-zero value or vice versa requires a restart of kitty. ''') egr() # }}} diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 7bd395c28..caa679873 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -1285,7 +1285,7 @@ class Parser: ans['text_composition_strategy'] = str(val) def text_fg_override_threshold(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: - ans['text_fg_override_threshold'] = str(val) + ans['text_fg_override_threshold'] = float(val) def touch_scroll_multiplier(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['touch_scroll_multiplier'] = float(val) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index 8fb15102e..a4f57e5c7 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -72,7 +72,7 @@ convert_from_opts_text_composition_strategy(PyObject *py_opts, Options *opts) { static void convert_from_python_text_fg_override_threshold(PyObject *val, Options *opts) { - text_fg_override_threshold(val, opts); + opts->text_fg_override_threshold = percent(val); } static void diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index fa4661c6d..1f2c9f3f6 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -14,6 +14,13 @@ PyFloat_AsFloat(PyObject *o) { return (float)PyFloat_AsDouble(o); } +static inline float +percent(PyObject *o) { + float ans = PyFloat_AsFloat(o); + return MAX(0.f, MIN(ans, 100.f)) * 0.01f; +} + + static inline color_type color_as_int(PyObject *color) { if (!PyObject_TypeCheck(color, &Color_Type)) { PyErr_SetString(PyExc_TypeError, "Not a Color object"); return 0; } @@ -211,18 +218,6 @@ text_composition_strategy(PyObject *val, Options *opts) { } } -static void -text_fg_override_threshold(PyObject *val, Options *opts) { - if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "text_fg_override_threshold must be a string"); return; } - opts->text_fg_override_threshold = 0.f; - - DECREF_AFTER_FUNCTION PyObject *text_fg_override_threshold = PyFloat_FromString(val); - if (PyErr_Occurred()) return; - opts->text_fg_override_threshold = MAX(0.f, PyFloat_AsFloat(text_fg_override_threshold)); - opts->text_fg_override_threshold = MIN(100.f, PyFloat_AsFloat(text_fg_override_threshold)); - -} - static char_type* list_of_chars(PyObject *chars) { if (!PyUnicode_Check(chars)) { PyErr_SetString(PyExc_TypeError, "list_of_chars must be a string"); return NULL; } diff --git a/kitty/options/types.py b/kitty/options/types.py index f4a738db3..1471b5142 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -599,7 +599,7 @@ class Options: tab_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title}' term: str = 'xterm-kitty' text_composition_strategy: str = 'platform' - text_fg_override_threshold: str = '0' + text_fg_override_threshold: float = 0.0 touch_scroll_multiplier: float = 1.0 undercurl_style: choices_for_undercurl_style = 'thin-sparse' update_check_interval: float = 24.0 diff --git a/kitty/shaders.c b/kitty/shaders.c index 5d77d6bb4..56462e58b 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -576,9 +576,8 @@ set_cell_uniforms(float current_inactive_text_alpha, bool force) { S(CELL_PROGRAM, text_contrast, text_contrast, 1f); S(CELL_FG_PROGRAM, text_contrast, text_contrast, 1f); float text_gamma_adjustment = OPT(text_gamma_adjustment) < 0.01f ? 1.0f : 1.0f / OPT(text_gamma_adjustment); S(CELL_PROGRAM, text_gamma_adjustment, text_gamma_adjustment, 1f); S(CELL_FG_PROGRAM, text_gamma_adjustment, text_gamma_adjustment, 1f); - - float text_fg_override_threshold = OPT(text_fg_override_threshold) * 0.01f; - S(CELL_PROGRAM, text_fg_override_threshold, text_fg_override_threshold, 1f); S(CELL_FG_PROGRAM, text_fg_override_threshold, text_fg_override_threshold, 1f); + S(CELL_PROGRAM, text_fg_override_threshold, OPT(text_fg_override_threshold), 1f); + S(CELL_FG_PROGRAM, text_fg_override_threshold, OPT(text_fg_override_threshold), 1f); #undef S #define SV(prog, name, num, val, type) { bind_program(prog); glUniform##type(glGetUniformLocation(program_id(prog), #name), num, val); } SV(CELL_PROGRAM, gamma_lut, 256, srgb_lut, 1fv); SV(CELL_FG_PROGRAM, gamma_lut, 256, srgb_lut, 1fv); diff --git a/kitty/window.py b/kitty/window.py index f5e207932..2885d9434 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -397,7 +397,7 @@ class LoadShaderPrograms: DECORATION_MASK=DECORATION_MASK, STRIKE_SPRITE_INDEX=NUM_UNDERLINE_STYLES + 1, ) - if get_options().text_fg_override_threshold != '0': + if get_options().text_fg_override_threshold != 0.: ff = ff.replace('#define NO_FG_OVERRIDE', '#define FG_OVERRIDE') if semi_transparent: vv = vv.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')