From 32588939aeba3e472e2ac4dfc4114fc3d4183e31 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Jul 2022 14:57:45 +0530 Subject: [PATCH] Deprecate the adjust_baseline adjust_line_height and adjust_column_width options in favor of modify_font Unifies handling and allow using pt units for those adjustments. Note that the behavior of percentage sizes for adjust baseline is backwards incompatible. It now uses the baseline value as the base rather than the cell height. --- docs/changelog.rst | 6 +++-- kitty/core_text.m | 16 ------------ kitty/fonts.c | 29 ++++++++++++++++------ kitty/fonts/__init__.py | 3 +++ kitty/freetype.c | 15 ------------ kitty/options/definition.py | 40 +++++++++--------------------- kitty/options/parse.py | 26 ++++++++++---------- kitty/options/to-c-generated.h | 45 ---------------------------------- kitty/options/to-c.h | 19 +------------- kitty/options/types.py | 6 ----- kitty/options/utils.py | 33 ++++++++++--------------- kitty/state.h | 4 +-- 12 files changed, 68 insertions(+), 174 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 84195243a..dd6cf8fa7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -40,7 +40,9 @@ Detailed list of changes - Reduce startup latency by ~50 milliseconds when running kittens via key-bindings or remote control (:iss:`5159`) -- A new option :opt:`modify_font` to adjust various font metrics like underline thickness, etc. (:pull:`5265`) +- A new option :opt:`modify_font` to adjust various font metrics like underlines, cell sizes etc. (:pull:`5265`) + +- Deprecate the ``adjust_baseline``, ``adjust_line_height`` and ``adjust_column_width`` options in favor of :opt:`modify_font` - Wayland: Fix a regression in the previous release that caused mouse cursor animation and keyboard repeat to stop working when switching seats (:iss:`5188`) @@ -724,7 +726,7 @@ Detailed list of changes 0.21.2 [2021-06-28] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- A new :opt:`adjust_baseline` option to adjust the vertical alignment of text +- A new ``adjust_baseline`` option to adjust the vertical alignment of text inside a line (:pull:`3734`) - A new :opt:`url_excluded_characters` option to exclude additional characters diff --git a/kitty/core_text.m b/kitty/core_text.m index 984c6f383..31fbc2497 100644 --- a/kitty/core_text.m +++ b/kitty/core_text.m @@ -324,13 +324,6 @@ harfbuzz_font_for_face(PyObject* s) { return self->hb_font; } -static unsigned int -adjust_ypos(unsigned int pos, unsigned int cell_height, int adjustment) { - if (adjustment >= 0) adjustment = MIN(adjustment, (int)pos - 1); - else adjustment = MAX(adjustment, (int)pos - (int)cell_height + 1); - return pos - adjustment; -} - void cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness, unsigned int* strikethrough_position, unsigned int* strikethrough_thickness) { // See https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/TypoFeatures/TextSystemFeatures.html @@ -376,9 +369,6 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u CTLineGetTypographicBounds(line, &typographic_ascent, &typographic_descent, &typographic_leading); *cell_height = MAX(4u, (unsigned int)ceilf(line_height)); CGFloat bounds_ascent = bounds_without_leading.size.height + bounds_without_leading.origin.y; - int baseline_offset = 0; - if (OPT(adjust_baseline_px) != 0) baseline_offset = OPT(adjust_baseline_px); - else if (OPT(adjust_baseline_frac) != 0) baseline_offset = (int)(*cell_height * OPT(adjust_baseline_frac)); *baseline = (unsigned int)floor(bounds_ascent + 0.5); // Not sure if we should add this to bounds ascent and then round it or add // it to already rounded baseline and round again. @@ -393,12 +383,6 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u debug("\tline metrics: ascent: %f descent: %f leading: %f\n", typographic_ascent, typographic_descent, typographic_leading); debug("\tfont metrics: ascent: %f descent: %f leading: %f underline_position: %f\n", self->ascent, self->descent, self->leading, self->underline_position); debug("\tcell_height: %u baseline: %u underline_position: %u strikethrough_position: %u\n", *cell_height, *baseline, *underline_position, *strikethrough_position); - if (baseline_offset) { - *baseline = adjust_ypos(*baseline, *cell_height, baseline_offset); - *underline_position = adjust_ypos(*underline_position, *cell_height, baseline_offset); - *strikethrough_position = adjust_ypos(*strikethrough_position, *cell_height, baseline_offset); - } - CFRelease(test_frame); CFRelease(path); CFRelease(framesetter); #undef count diff --git a/kitty/fonts.c b/kitty/fonts.c index dc5b295f1..0d26718a0 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -346,24 +346,29 @@ adjust_metric(unsigned int *metric, float adj, AdjustmentUnit unit, double dpi) *metric = (a < 0 && -a > (int)*metric) ? 0 : *metric + a; } +static unsigned int +adjust_ypos(unsigned int pos, unsigned int cell_height, int adjustment) { + if (adjustment >= 0) adjustment = MIN(adjustment, (int)pos - 1); + else adjustment = MAX(adjustment, (int)pos - (int)cell_height + 1); + return pos - adjustment; +} + static void calc_cell_metrics(FontGroup *fg) { unsigned int cell_height, cell_width, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness; cell_metrics(fg->fonts[fg->medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness, &strikethrough_position, &strikethrough_thickness); if (!cell_width) fatal("Failed to calculate cell width for the specified font"); unsigned int before_cell_height = cell_height; - int cw = cell_width, ch = cell_height; - if (OPT(adjust_line_height_px) != 0) ch += OPT(adjust_line_height_px); - if (OPT(adjust_line_height_frac) != 0.f) ch = (int)(ch * OPT(adjust_line_height_frac)); - if (OPT(adjust_column_width_px != 0)) cw += OPT(adjust_column_width_px); - if (OPT(adjust_column_width_frac) != 0.f) cw = (int)(cw * OPT(adjust_column_width_frac)); + unsigned int cw = cell_width, ch = cell_height; + adjust_metric(&cw, OPT(cell_width).val, OPT(cell_width).unit, fg->logical_dpi_x); + adjust_metric(&ch, OPT(cell_height).val, OPT(cell_height).unit, fg->logical_dpi_y); #define MAX_DIM 1000 #define MIN_WIDTH 2 #define MIN_HEIGHT 4 if (cw >= MIN_WIDTH && cw <= MAX_DIM) cell_width = cw; - else log_error("Cell width invalid after adjustment, ignoring adjust_column_width"); + else log_error("Cell width invalid after adjustment, ignoring modify_font cell_width"); if (ch >= MIN_HEIGHT && ch <= MAX_DIM) cell_height = ch; - else log_error("Cell height invalid after adjustment, ignoring adjust_line_height"); + else log_error("Cell height invalid after adjustment, ignoring modify_font cell_height"); int line_height_adjustment = cell_height - before_cell_height; if (cell_height < MIN_HEIGHT) fatal("Line height too small: %u", cell_height); if (cell_height > MAX_DIM) fatal("Line height too large: %u", cell_height); @@ -373,10 +378,18 @@ calc_cell_metrics(FontGroup *fg) { #undef MIN_HEIGHT #undef MAX_DIM + unsigned int baseline_before = baseline; #define A(which, dpi) adjust_metric(&which, OPT(which).val, OPT(which).unit, fg->logical_dpi_##dpi); - A(underline_thickness, y); A(underline_position, y); A(strikethrough_thickness, y); A(strikethrough_position, y); + A(underline_thickness, y); A(underline_position, y); A(strikethrough_thickness, y); A(strikethrough_position, y); A(baseline, y); #undef A + if (baseline_before != baseline) { + int adjustment = baseline - baseline_before; + baseline = adjust_ypos(baseline_before, cell_height, adjustment); + underline_position = adjust_ypos(underline_position, cell_height, adjustment); + strikethrough_position = adjust_ypos(underline_position, cell_height, adjustment); + } + underline_position = MIN(cell_height - 1, underline_position); // ensure there is at least a couple of pixels available to render styled underlines while (underline_position > baseline + 1 && cell_height - underline_position < 2) underline_position--; diff --git a/kitty/fonts/__init__.py b/kitty/fonts/__init__.py index 97f44ecdd..7a5af5ed6 100644 --- a/kitty/fonts/__init__.py +++ b/kitty/fonts/__init__.py @@ -29,6 +29,9 @@ class ModificationType(Enum): underline_thickness = auto() strikethrough_position = auto() strikethrough_thickness = auto() + cell_width = auto() + cell_height = auto() + baseline = auto() size = auto() diff --git a/kitty/freetype.c b/kitty/freetype.c index f25fb7dd5..bd028202c 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -306,21 +306,11 @@ calc_cell_width(Face *self) { } -static unsigned int -adjust_ypos(unsigned int pos, unsigned int cell_height, int adjustment) { - if (adjustment >= 0) adjustment = MIN(adjustment, (int)pos - 1); - else adjustment = MAX(adjustment, (int)pos - (int)cell_height + 1); - return pos - adjustment; -} - void cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness, unsigned int* strikethrough_position, unsigned int* strikethrough_thickness) { Face *self = (Face*)s; *cell_width = calc_cell_width(self); *cell_height = calc_cell_height(self, true); - int baseline_offset = 0; - if (OPT(adjust_baseline_px) != 0) baseline_offset = OPT(adjust_baseline_px); - else if (OPT(adjust_baseline_frac) != 0) baseline_offset = (int)(*cell_height * OPT(adjust_baseline_frac)); *baseline = font_units_to_pixels_y(self, self->ascender); *underline_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->underline_position))); *underline_thickness = MAX(1, font_units_to_pixels_y(self, self->underline_thickness)); @@ -335,11 +325,6 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u } else { *strikethrough_thickness = *underline_thickness; } - if (baseline_offset) { - *baseline = adjust_ypos(*baseline, *cell_height, baseline_offset); - *underline_position = adjust_ypos(*underline_position, *cell_height, baseline_offset); - *strikethrough_position = adjust_ypos(*strikethrough_position, *cell_height, baseline_offset); - } } unsigned int diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 01624e29d..3f663d64c 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -18,6 +18,7 @@ definition = Definition( definition.add_deprecation('deprecated_hide_window_decorations_aliases', 'x11_hide_window_decorations', 'macos_hide_titlebar') definition.add_deprecation('deprecated_macos_show_window_title_in_menubar_alias', 'macos_show_window_title_in_menubar') definition.add_deprecation('deprecated_send_text', 'send_text') +definition.add_deprecation('deprecated_adjust_line_height', 'adjust_line_height', 'adjust_column_width', 'adjust_baseline') agr = definition.add_group egr = definition.end_group @@ -80,34 +81,6 @@ terminals. ''' ) -opt('adjust_line_height', '0', - option_type='adjust_line_height', ctype='!adjust_line_height', - long_text=''' -Change the size of each character cell kitty renders. You can use either -numbers, which are interpreted as pixels or percentages (number followed by %), -which are interpreted as percentages of the unmodified values. You can use -negative pixels or percentages less than 100% to reduce sizes (but this might -cause rendering artifacts). -''' - ) - -opt('adjust_column_width', '0', - option_type='adjust_line_height', ctype='!adjust_column_width', - ) - -opt('adjust_baseline', '0', - option_type='adjust_baseline', ctype='!adjust_baseline', - add_to_default=False, - long_text=''' -Adjust the vertical alignment of text (the height in the cell at which text is -positioned). You can use either numbers, which are interpreted as pixels or -percentages (number followed by %), which are interpreted as the percentage of -the line height. A positive value moves the baseline up, and a negative value -moves them down. The underline and strikethrough positions are adjusted -accordingly. -''' - ) - opt('+symbol_map', 'U+E0A0-U+E0A3,U+E0C0-U+E0C7 PowerlineSymbols', option_type='symbol_map', add_to_default=False, @@ -227,6 +200,17 @@ No suffix means use pts. For example:: modify_font underline_position -2 modify_font underline_thickness 150% modify_font strikethrough_position 2px + +Additionally, you can modify the size of the cell in which each font glyph is rendered and the baseline +at which the glyph is placed in the cell. For example:: + + modify_font cell_width 80% + modify_font cell_height -2px + modify_font baseline 3 + +Note that modifying the baseline will automatically adjust the underline and strikethrough positions +by the same amount. Increasing the baseline raises glyphs inside the cell and decreasing it lowers them. +Decreasing the cell size might cause rendering artifacts, so use with care. ''') opt('box_drawing_scale', '0.001, 1, 1.5, 2', diff --git a/kitty/options/parse.py b/kitty/options/parse.py index c6d500270..a46de39fe 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -6,10 +6,10 @@ from kitty.conf.utils import ( unit_float ) from kitty.options.utils import ( - action_alias, active_tab_title_template, adjust_baseline, adjust_line_height, allow_hyperlinks, - allow_remote_control, bell_on_tab, box_drawing_scale, clear_all_mouse_actions, clear_all_shortcuts, - clipboard_control, clone_source_strategies, config_or_absolute_path, copy_on_select, - cursor_text_color, deprecated_hide_window_decorations_aliases, + action_alias, active_tab_title_template, allow_hyperlinks, allow_remote_control, bell_on_tab, + box_drawing_scale, clear_all_mouse_actions, clear_all_shortcuts, clipboard_control, + clone_source_strategies, config_or_absolute_path, copy_on_select, cursor_text_color, + deprecated_adjust_line_height, deprecated_hide_window_decorations_aliases, deprecated_macos_show_window_title_in_menubar_alias, deprecated_send_text, disable_ligatures, edge_width, env, font_features, hide_window_decorations, macos_option_as_alt, macos_titlebar_color, modify_font, narrow_symbols, optional_edge_width, parse_map, parse_mouse_map, paste_actions, @@ -42,15 +42,6 @@ class Parser: def active_tab_title_template(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['active_tab_title_template'] = active_tab_title_template(val) - def adjust_baseline(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: - ans['adjust_baseline'] = adjust_baseline(val) - - def adjust_column_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: - ans['adjust_column_width'] = adjust_line_height(val) - - def adjust_line_height(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: - ans['adjust_line_height'] = adjust_line_height(val) - def allow_cloning(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_allow_cloning: @@ -1352,6 +1343,15 @@ class Parser: def send_text(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: deprecated_send_text('send_text', val, ans) + def adjust_line_height(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + deprecated_adjust_line_height('adjust_line_height', val, ans) + + def adjust_column_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + deprecated_adjust_line_height('adjust_column_width', val, ans) + + def adjust_baseline(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + deprecated_adjust_line_height('adjust_baseline', val, ans) + def map(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: for k in parse_map(val): ans['map'].append(k) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index d601a269b..60a6d1d45 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -31,45 +31,6 @@ convert_from_opts_force_ltr(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } -static void -convert_from_python_adjust_line_height(PyObject *val, Options *opts) { - adjust_line_height(val, opts); -} - -static void -convert_from_opts_adjust_line_height(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "adjust_line_height"); - if (ret == NULL) return; - convert_from_python_adjust_line_height(ret, opts); - Py_DECREF(ret); -} - -static void -convert_from_python_adjust_column_width(PyObject *val, Options *opts) { - adjust_column_width(val, opts); -} - -static void -convert_from_opts_adjust_column_width(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "adjust_column_width"); - if (ret == NULL) return; - convert_from_python_adjust_column_width(ret, opts); - Py_DECREF(ret); -} - -static void -convert_from_python_adjust_baseline(PyObject *val, Options *opts) { - adjust_baseline(val, opts); -} - -static void -convert_from_opts_adjust_baseline(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "adjust_baseline"); - if (ret == NULL) return; - convert_from_python_adjust_baseline(ret, opts); - Py_DECREF(ret); -} - static void convert_from_python_disable_ligatures(PyObject *val, Options *opts) { opts->disable_ligatures = PyLong_AsLong(val); @@ -1051,12 +1012,6 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_force_ltr(py_opts, opts); if (PyErr_Occurred()) return false; - convert_from_opts_adjust_line_height(py_opts, opts); - if (PyErr_Occurred()) return false; - convert_from_opts_adjust_column_width(py_opts, opts); - if (PyErr_Occurred()) return false; - convert_from_opts_adjust_baseline(py_opts, opts); - if (PyErr_Occurred()) return false; convert_from_opts_disable_ligatures(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_modify_font(py_opts, opts); diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index ba2ed1820..0696429a2 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -129,6 +129,7 @@ static void modify_font(PyObject *mf, Options *opts) { #define S(which) { PyObject *v = PyDict_GetItemString(mf, #which); if (v) parse_font_mod_size(v, &opts->which.val, &opts->which.unit); } S(underline_position); S(underline_thickness); S(strikethrough_thickness); S(strikethrough_position); + S(cell_height); S(cell_width); S(baseline); #undef S } @@ -224,21 +225,3 @@ tab_bar_margin_height(PyObject *val, Options *opts) { opts->tab_bar_margin_height.outer = PyFloat_AsDouble(PyTuple_GET_ITEM(val, 0)); opts->tab_bar_margin_height.inner = PyFloat_AsDouble(PyTuple_GET_ITEM(val, 1)); } - -#define read_adjust(name) { \ - if (PyFloat_Check(al)) { \ - opts->name##_frac = (float)PyFloat_AsDouble(al); \ - opts->name##_px = 0; \ - } else { \ - opts->name##_frac = 0; \ - opts->name##_px = (int)PyLong_AsLong(al); \ - } \ -} - -static void -adjust_line_height(PyObject *al, Options *opts) { read_adjust(adjust_line_height); } -static void -adjust_column_width(PyObject *al, Options *opts) { read_adjust(adjust_column_width); } -static void -adjust_baseline(PyObject *al, Options *opts) { read_adjust(adjust_baseline); } -#undef read_adjust diff --git a/kitty/options/types.py b/kitty/options/types.py index 698276c1a..639e137e5 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -52,9 +52,6 @@ option_names = ( # {{{ 'active_tab_font_style', 'active_tab_foreground', 'active_tab_title_template', - 'adjust_baseline', - 'adjust_column_width', - 'adjust_line_height', 'allow_cloning', 'allow_hyperlinks', 'allow_remote_control', @@ -467,9 +464,6 @@ class Options: active_tab_font_style: typing.Tuple[bool, bool] = (True, True) active_tab_foreground: Color = Color(0, 0, 0) active_tab_title_template: typing.Optional[str] = None - adjust_baseline: typing.Union[int, float] = 0 - adjust_column_width: typing.Union[int, float] = 0 - adjust_line_height: typing.Union[int, float] = 0 allow_cloning: choices_for_allow_cloning = 'ask' allow_hyperlinks: int = 1 allow_remote_control: str = 'n' diff --git a/kitty/options/utils.py b/kitty/options/utils.py index d214ef536..9f205fad6 100644 --- a/kitty/options/utils.py +++ b/kitty/options/utils.py @@ -449,26 +449,6 @@ def parse_shortcut(sc: str) -> SingleKey: return SingleKey(mods, is_native, key or 0) -def adjust_line_height(x: str) -> Union[int, float]: - if x.endswith('%'): - ans = float(x[:-1].strip()) / 100.0 - if ans < 0: - log_error('Percentage adjustments of cell sizes must be positive numbers') - return 0 - return ans - return int(x) - - -def adjust_baseline(x: str) -> Union[int, float]: - if x.endswith('%'): - ans = float(x[:-1].strip()) / 100.0 - if abs(ans) > 1: - log_error('Percentage adjustments of the baseline cannot exceed 100%') - return 0 - return ans - return int(x) - - def to_font_size(x: str) -> float: return max(MINIMUM_FONT_SIZE, float(x)) @@ -1203,3 +1183,16 @@ def deprecated_send_text(key: str, val: str, ans: Dict[str, Any]) -> None: key_str = f'{sc} send_text {mode} {text}' for k in parse_map(key_str): ans['map'].append(k) + + +def deprecated_adjust_line_height(key: str, x: str, opts_dict: Dict[str, Any]) -> None: + fm = {'adjust_line_height': 'cell_height', 'adjust_baseline': 'baseline', 'adjust_column_width': 'cell_width'}[key] + mtype = getattr(ModificationType, fm) + if x.endswith('%'): + ans = float(x[:-1].strip()) + if ans < 0: + log_error(f'Percentage adjustments of {key} must be positive numbers') + return + opts_dict['modify_font'][fm] = FontModification(mtype, ModificationValue(ans, ModificationUnit.percent)) + else: + opts_dict['modify_font'][fm] = FontModification(mtype, ModificationValue(int(x), ModificationUnit.pixel)) diff --git a/kitty/state.h b/kitty/state.h index e7dbb8019..1acd20b9d 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -46,8 +46,6 @@ typedef struct { float macos_thicken_font; WindowTitleIn macos_show_window_title_in; char *bell_path; - int adjust_line_height_px, adjust_column_width_px, adjust_baseline_px; - float adjust_line_height_frac, adjust_column_width_frac, adjust_baseline_frac; float background_opacity, dim_opacity; char *background_image, *default_window_logo; @@ -88,7 +86,7 @@ typedef struct { int macos_colorspace; struct { float val; AdjustmentUnit unit; - } underline_position, underline_thickness, strikethrough_position, strikethrough_thickness; + } underline_position, underline_thickness, strikethrough_position, strikethrough_thickness, cell_width, cell_height, baseline; } Options; typedef struct WindowLogoRenderData {