mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-19 15:04:50 +02:00
config: number with unit for min contrast ratio
This commit is contained in:
@@ -22,6 +22,7 @@ from ..typing import Protocol
|
|||||||
from ..utils import expandvars, log_error, shlex_split
|
from ..utils import expandvars, log_error, shlex_split
|
||||||
|
|
||||||
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
||||||
|
number_unit_pat = re.compile(r'\s*([-+]?\d+\.?\d*)\s*([^\d\s]*)?')
|
||||||
ItemParser = Callable[[str, str, dict[str, Any]], bool]
|
ItemParser = Callable[[str, str, dict[str, Any]], bool]
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
|
|
||||||
@@ -66,6 +67,19 @@ def unit_float(x: ConvertibleToNumbers) -> float:
|
|||||||
return max(0, min(float(x), 1))
|
return max(0, min(float(x), 1))
|
||||||
|
|
||||||
|
|
||||||
|
def number_with_unit(x: str, default_unit: str = '') -> tuple[float, str]:
|
||||||
|
mat = number_unit_pat.match(x)
|
||||||
|
exception = None
|
||||||
|
if mat is not None:
|
||||||
|
try:
|
||||||
|
value, unit = float(mat.group(1)), mat.group(2)
|
||||||
|
unit = default_unit if not unit else unit
|
||||||
|
return value, unit
|
||||||
|
except Exception as e:
|
||||||
|
exception = e
|
||||||
|
raise ValueError(f'Invalid number with unit config option provided: {x if exception is None else exception}')
|
||||||
|
|
||||||
|
|
||||||
def to_bool(x: str) -> bool:
|
def to_bool(x: str) -> bool:
|
||||||
return x.lower() in ('y', 'yes', 'true')
|
return x.lower() in ('y', 'yes', 'true')
|
||||||
|
|
||||||
|
|||||||
@@ -265,21 +265,21 @@ 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.
|
and adjust the first parameter until the perceived thickness matches the dark theme.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
opt('text_fg_override_threshold', 0, option_type='float', long_text='''
|
opt('text_fg_override_threshold', '0 %', option_type='number_with_unit', long_text='''
|
||||||
A setting to prevent low contrast scenarios, configurable in two different modes (positive and negative value).
|
A setting to prevent low contrast scenarios, configurable in two different modes (suffix :code:` %` and suffix :code:` ratio`).
|
||||||
The default value is :code:`0`, which means no overriding is performed. Useful when working with applications
|
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.
|
that use colors that do not contrast well with your preferred color scheme.
|
||||||
|
|
||||||
A positive value represents the minimum accepted difference in luminance between the foreground and background
|
A value with the suffix :code:` %` represents the minimum accepted difference in luminance between the foreground and background
|
||||||
color, below which kitty will override the foreground color. It is percentage
|
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
|
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
|
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.
|
to white if the background is dark or black if the background is light.
|
||||||
|
|
||||||
A negative value represents the minimum accepted contrast ratio between the foreground and background color.
|
A value with the suffix :code:` ratio` represents the minimum accepted contrast ratio between the foreground and background color.
|
||||||
Possible values range from :code:`-0.0` to :code:`-21.0`.
|
Possible values range from :code:`0.0 ratio` to :code:`21.0 ratio`.
|
||||||
To for example meet :link:`WCAG level AA <https://en.wikipedia.org/wiki/Web_Content_Accessibility_Guidelines>`
|
To for example meet :link:`WCAG level AA <https://en.wikipedia.org/wiki/Web_Content_Accessibility_Guidelines>`
|
||||||
a value of :code:`-4.5` can be provided.
|
a value of :code:`4.5 ratio` can be provided.
|
||||||
The algorithm is implemented using :link:`HSLuv <https://www.hsluv.org/>` which enables it to change
|
The algorithm is implemented using :link:`HSLuv <https://www.hsluv.org/>` which enables it to change
|
||||||
the perceived lightness of a color just as much as needed without really changing its hue and saturation.
|
the perceived lightness of a color just as much as needed without really changing its hue and saturation.
|
||||||
|
|
||||||
|
|||||||
17
kitty/options/parse.py
generated
17
kitty/options/parse.py
generated
@@ -4,8 +4,16 @@
|
|||||||
import typing
|
import typing
|
||||||
import collections.abc # noqa: F401, RUF100
|
import collections.abc # noqa: F401, RUF100
|
||||||
from kitty.conf.utils import (
|
from kitty.conf.utils import (
|
||||||
merge_dicts, positive_float, positive_int, python_string, to_bool, to_cmdline, to_color,
|
merge_dicts,
|
||||||
to_color_or_none, unit_float
|
positive_float,
|
||||||
|
positive_int,
|
||||||
|
python_string,
|
||||||
|
number_with_unit,
|
||||||
|
to_bool,
|
||||||
|
to_cmdline,
|
||||||
|
to_color,
|
||||||
|
to_color_or_none,
|
||||||
|
unit_float,
|
||||||
)
|
)
|
||||||
from kitty.options.utils import (
|
from kitty.options.utils import (
|
||||||
action_alias, active_tab_title_template, allow_hyperlinks, bell_on_tab, box_drawing_scale,
|
action_alias, active_tab_title_template, allow_hyperlinks, bell_on_tab, box_drawing_scale,
|
||||||
@@ -1324,7 +1332,10 @@ class Parser:
|
|||||||
ans['text_composition_strategy'] = str(val)
|
ans['text_composition_strategy'] = str(val)
|
||||||
|
|
||||||
def text_fg_override_threshold(self, val: str, ans: dict[str, typing.Any]) -> None:
|
def text_fg_override_threshold(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||||
ans['text_fg_override_threshold'] = float(val)
|
value, unit = number_with_unit(val, default_unit='%')
|
||||||
|
if unit not in ['%', 'ratio']:
|
||||||
|
raise ValueError(f'The unit {unit} is not a valid choice for text_fg_override_threshold')
|
||||||
|
ans['text_fg_override_threshold'] = value, unit
|
||||||
|
|
||||||
def touch_scroll_multiplier(self, val: str, ans: dict[str, typing.Any]) -> None:
|
def touch_scroll_multiplier(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||||
ans['touch_scroll_multiplier'] = float(val)
|
ans['touch_scroll_multiplier'] = float(val)
|
||||||
|
|||||||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
@@ -612,7 +612,7 @@ class Options:
|
|||||||
term: str = 'xterm-kitty'
|
term: str = 'xterm-kitty'
|
||||||
terminfo_type: choices_for_terminfo_type = 'path'
|
terminfo_type: choices_for_terminfo_type = 'path'
|
||||||
text_composition_strategy: str = 'platform'
|
text_composition_strategy: str = 'platform'
|
||||||
text_fg_override_threshold: float = 0.0
|
text_fg_override_threshold: tuple[float, str] = 0.0, '%'
|
||||||
touch_scroll_multiplier: float = 1.0
|
touch_scroll_multiplier: float = 1.0
|
||||||
transparent_background_colors: tuple[tuple[kitty.fast_data_types.Color, float], ...] = ()
|
transparent_background_colors: tuple[tuple[kitty.fast_data_types.Color, float], ...] = ()
|
||||||
undercurl_style: choices_for_undercurl_style = 'thin-sparse'
|
undercurl_style: choices_for_undercurl_style = 'thin-sparse'
|
||||||
|
|||||||
@@ -131,8 +131,8 @@ null_replacer = MultiReplacer()
|
|||||||
|
|
||||||
|
|
||||||
class LoadShaderPrograms:
|
class LoadShaderPrograms:
|
||||||
|
|
||||||
text_fg_override_threshold: float = 0
|
text_fg_override_threshold: float = 0
|
||||||
|
text_fg_override_threshold_unit: str = '%'
|
||||||
text_old_gamma: bool = False
|
text_old_gamma: bool = False
|
||||||
semi_transparent: bool = False
|
semi_transparent: bool = False
|
||||||
cell_program_replacer: MultiReplacer = null_replacer
|
cell_program_replacer: MultiReplacer = null_replacer
|
||||||
@@ -140,7 +140,10 @@ class LoadShaderPrograms:
|
|||||||
@property
|
@property
|
||||||
def needs_recompile(self) -> bool:
|
def needs_recompile(self) -> bool:
|
||||||
opts = get_options()
|
opts = get_options()
|
||||||
return opts.text_fg_override_threshold != self.text_fg_override_threshold or (opts.text_composition_strategy == 'legacy') != self.text_old_gamma
|
return (
|
||||||
|
opts.text_fg_override_threshold != (self.text_fg_override_threshold, self.text_fg_override_threshold_unit)
|
||||||
|
or (opts.text_composition_strategy == 'legacy') != self.text_old_gamma
|
||||||
|
)
|
||||||
|
|
||||||
def recompile_if_needed(self) -> None:
|
def recompile_if_needed(self) -> None:
|
||||||
if self.needs_recompile:
|
if self.needs_recompile:
|
||||||
@@ -150,10 +153,14 @@ class LoadShaderPrograms:
|
|||||||
self.semi_transparent = semi_transparent
|
self.semi_transparent = semi_transparent
|
||||||
opts = get_options()
|
opts = get_options()
|
||||||
self.text_old_gamma = opts.text_composition_strategy == 'legacy'
|
self.text_old_gamma = opts.text_composition_strategy == 'legacy'
|
||||||
if opts.text_fg_override_threshold < 0:
|
|
||||||
self.text_fg_override_threshold = opts.text_fg_override_threshold
|
self.text_fg_override_threshold, self.text_fg_override_threshold_unit = opts.text_fg_override_threshold
|
||||||
else:
|
match self.text_fg_override_threshold_unit:
|
||||||
self.text_fg_override_threshold = max(0, min(opts.text_fg_override_threshold, 100)) * 0.01
|
case '%':
|
||||||
|
self.text_fg_override_threshold = max(0, min(self.text_fg_override_threshold, 100.0)) * 0.01
|
||||||
|
case 'ratio':
|
||||||
|
self.text_fg_override_threshold = max(0, min(self.text_fg_override_threshold, 21.0))
|
||||||
|
|
||||||
cell = program_for('cell')
|
cell = program_for('cell')
|
||||||
if self.cell_program_replacer is null_replacer:
|
if self.cell_program_replacer is null_replacer:
|
||||||
self.cell_program_replacer = MultiReplacer(
|
self.cell_program_replacer = MultiReplacer(
|
||||||
@@ -171,9 +178,9 @@ class LoadShaderPrograms:
|
|||||||
r['WHICH_PHASE'] = f'PHASE_{which}'
|
r['WHICH_PHASE'] = f'PHASE_{which}'
|
||||||
r['TRANSPARENT'] = '1' if semi_transparent else '0'
|
r['TRANSPARENT'] = '1' if semi_transparent else '0'
|
||||||
r['FG_OVERRIDE_THRESHOLD'] = str(self.text_fg_override_threshold)
|
r['FG_OVERRIDE_THRESHOLD'] = str(self.text_fg_override_threshold)
|
||||||
r['FG_OVERRIDE'] = '1' if self.text_fg_override_threshold > 0 else '0'
|
r['FG_OVERRIDE'] = str(int(bool(self.text_fg_override_threshold_unit == '%')))
|
||||||
r['MIN_CONTRAST_RATIO'] = str(-self.text_fg_override_threshold)
|
r['MIN_CONTRAST_RATIO'] = str(self.text_fg_override_threshold)
|
||||||
r['APPLY_MIN_CONTRAST_RATIO'] = '1' if self.text_fg_override_threshold < 0 else '0'
|
r['APPLY_MIN_CONTRAST_RATIO'] = str(int(bool(self.text_fg_override_threshold_unit == 'ratio')))
|
||||||
r['TEXT_NEW_GAMMA'] = '0' if self.text_old_gamma else '1'
|
r['TEXT_NEW_GAMMA'] = '0' if self.text_old_gamma else '1'
|
||||||
return self.cell_program_replacer(src)
|
return self.cell_program_replacer(src)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user