diff --git a/config/src/config.rs b/config/src/config.rs index 21a8dc187..9c3674198 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -57,6 +57,18 @@ pub struct Config { #[dynamic(default = "default_one_point_oh_f64")] pub cell_width: f64, + #[dynamic(try_from = "crate::units::OptPixelUnit", default)] + pub cursor_thickness: Option, + + #[dynamic(try_from = "crate::units::OptPixelUnit", default)] + pub underline_thickness: Option, + + #[dynamic(try_from = "crate::units::OptPixelUnit", default)] + pub underline_position: Option, + + #[dynamic(try_from = "crate::units::OptPixelUnit", default)] + pub strikethrough_position: Option, + #[dynamic(default)] pub allow_square_glyphs_to_overflow_width: AllowSquareGlyphOverflow, diff --git a/docs/changelog.md b/docs/changelog.md index 7fb419681..5995cccba 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -18,6 +18,12 @@ As features stabilize some brief notes about them will accumulate here. * [normalize_to_nfc](config/lua/config/normalize_to_nfc.md) option to normalize terminal output to Unicode NFC prior to applying it to the terminal model. [#2482](https://github.com/wez/wezterm/issues/2482) +* [cursor_thickness](config/lua/config/cursor_thickness.md), + [underline_thickness](config/lua/config/underline_thickness.md), + [underline_position](config/lua/config/underline_position.md) and + [strikethrough_position](config/lua/config/strikethrough_position.md) options + to fine tune appearance. [#2505](https://github.com/wez/wezterm/issues/2505) + [#2326](https://github.com/wez/wezterm/issues/2326) #### Fixed * Wayland: key repeat gets stuck after pressing two keys in quick succession. diff --git a/docs/config/lua/config/cursor_thickness.md b/docs/config/lua/config/cursor_thickness.md new file mode 100644 index 000000000..5c624dc1c --- /dev/null +++ b/docs/config/lua/config/cursor_thickness.md @@ -0,0 +1,16 @@ +# cursor_thickness + +*Since: nightly builds only* + +If specified, overrides the base thickness of the lines used to +render the textual cursor glyph. + +The default is to use the [underline_thickness](underline_thickness.md). + +This config option accepts different units that have slightly different interpretations: + +* `2`, `2.0` or `"2px"` all specify a thickness of 2 pixels +* `"2pt"` specifies a thickness of 2 points, which scales according to the DPI of the window +* `"200%"` takes the `underline_thickness` and multiplies it by 2 to arrive at a thickness double the normal size +* `"0.1cell"` takes the cell height, scales it by `0.1` and uses that as the thickness + diff --git a/docs/config/lua/config/strikethrough_position.md b/docs/config/lua/config/strikethrough_position.md new file mode 100644 index 000000000..da5ccb42e --- /dev/null +++ b/docs/config/lua/config/strikethrough_position.md @@ -0,0 +1,16 @@ +# strikethrough_position + +*Since: nightly builds only* + +If specified, overrides the position of strikethrough lines. + +The default is derived from the underline position metric specified by the designer +of the primary font. + +This config option accepts different units that have slightly different interpretations: + +* `2`, `2.0` or `"2px"` all specify a position of 2 pixels +* `"2pt"` specifies a position of 2 points, which scales according to the DPI of the window +* `"200%"` takes the font-specified `underline_position` and multiplies it by 2 +* `"0.5cell"` takes the cell height, scales it by `0.5` and uses that as the position + diff --git a/docs/config/lua/config/underline_position.md b/docs/config/lua/config/underline_position.md new file mode 100644 index 000000000..a1d61d777 --- /dev/null +++ b/docs/config/lua/config/underline_position.md @@ -0,0 +1,19 @@ +# underline_position + +*Since: nightly builds only* + +If specified, overrides the position of underlines. + +The default is to use the underline position metric specified by the designer +of the primary font. + +This config option accepts different units that have slightly different interpretations: + +* `2`, `2.0` or `"2px"` all specify a position of 2 pixels +* `"2pt"` specifies a position of 2 points, which scales according to the DPI of the window +* `"200%"` takes the font-specified `underline_position` and multiplies it by 2 +* `"0.1cell"` takes the cell height, scales it by `0.1` and uses that as the position + +Note that the `underline_position` is often a small negative number like `-2` +or `-4` and specifies an offset from the baseline of the font. + diff --git a/docs/config/lua/config/underline_thickness.md b/docs/config/lua/config/underline_thickness.md new file mode 100644 index 000000000..6ddea15ce --- /dev/null +++ b/docs/config/lua/config/underline_thickness.md @@ -0,0 +1,19 @@ +# underline_thickness + +*Since: nightly builds only* + +If specified, overrides the base thickness of underlines. The underline +thickness is also used for rendering split pane dividers and a number of other +lines in custom glyphs. + +The default is to use the underline thickness metric specified by the designer +of the primary font. + +This config option accepts different units that have slightly different interpretations: + +* `2`, `2.0` or `"2px"` all specify a thickness of 2 pixels +* `"2pt"` specifies a thickness of 2 points, which scales according to the DPI of the window +* `"200%"` takes the font-specified `underline_thickness` and multiplies it by 2 to arrive at a thickness double the normal size +* `"0.1cell"` takes the cell height, scales it by `0.1` and uses that as the thickness + + diff --git a/wezterm-gui/src/customglyph.rs b/wezterm-gui/src/customglyph.rs index e5dd87bb8..7ce8de105 100644 --- a/wezterm-gui/src/customglyph.rs +++ b/wezterm-gui/src/customglyph.rs @@ -2,6 +2,7 @@ use crate::glyphcache::{GlyphCache, SizedBlockKey}; use crate::utilsprites::RenderMetrics; use ::window::bitmaps::atlas::Sprite; use ::window::color::{LinearRgba, SrgbaPixel}; +use config::DimensionContext; use std::ops::Range; use termwiz::surface::CursorShape; use tiny_skia::{FillRule, Paint, Path, PathBuilder, PixmapMut, Stroke, Transform}; @@ -3707,7 +3708,14 @@ impl GlyphCache { return Ok(sprite.clone()); } - let metrics = metrics.scale_cell_width(width as f64); + let mut metrics = metrics.scale_cell_width(width as f64); + if let Some(d) = &self.fonts.config().cursor_thickness { + metrics.underline_height = d.evaluate_as_pixels(DimensionContext { + dpi: self.fonts.get_dpi() as f32, + pixel_max: metrics.underline_height as f32, + pixel_cell: metrics.cell_size.height as f32, + }) as isize; + } let mut buffer = Image::new( metrics.cell_size.width as usize, diff --git a/wezterm-gui/src/glyphcache.rs b/wezterm-gui/src/glyphcache.rs index 6913667dd..bc1c014fa 100644 --- a/wezterm-gui/src/glyphcache.rs +++ b/wezterm-gui/src/glyphcache.rs @@ -248,7 +248,7 @@ impl DecodedImage { pub struct GlyphCache { glyph_cache: HashMap>>, pub atlas: Atlas, - fonts: Rc, + pub fonts: Rc, pub image_cache: LfuCacheU64, frame_cache: HashMap<[u8; 32], Sprite>, line_glyphs: HashMap>, @@ -402,6 +402,7 @@ impl GlyphCache { pub fn config_changed(&mut self) { let config = self.fonts.config(); self.image_cache.update_config(&config); + self.cursor_glyphs.clear(); } /// Perform the load and render of a glyph @@ -783,7 +784,8 @@ impl GlyphCache { metrics.cell_size.height - (cell_rect.origin.y + metrics.descender_row); let half_height = (wave_height as f32 / 4.).max(1.); - let y = (cell_rect.origin.y + metrics.descender_row) as usize - half_height as usize; + let y = ((cell_rect.origin.y + metrics.descender_row) as usize) + .saturating_sub(half_height as usize); fn add(x: usize, y: usize, val: u8, max_y: usize, buffer: &mut Image) { let y = y.min(max_y); @@ -800,8 +802,20 @@ impl GlyphCache { for row in 0..metrics.underline_height as usize { let value = (255. * (vertical - v1).abs()) as u8; - add(x, row + y + v1 as usize, 255 - value, max_y, buffer); - add(x, row + y + v2 as usize, value, max_y, buffer); + add( + x, + row.saturating_add(y).saturating_add(v1 as usize), + 255u8.saturating_sub(value), + max_y, + buffer, + ); + add( + x, + row.saturating_add(y).saturating_add(v2 as usize), + value, + max_y, + buffer, + ); } } }; diff --git a/wezterm-gui/src/utilsprites.rs b/wezterm-gui/src/utilsprites.rs index 8b3a72af5..b8f68164d 100644 --- a/wezterm-gui/src/utilsprites.rs +++ b/wezterm-gui/src/utilsprites.rs @@ -4,6 +4,7 @@ use ::window::bitmaps::{BitmapImage, Image, Texture2d}; use ::window::color::SrgbaPixel; use ::window::{Point, Rect, Size}; use anyhow::Context; +use config::DimensionContext; use std::rc::Rc; use wezterm_font::units::*; use wezterm_font::{FontConfiguration, FontMetrics}; @@ -84,14 +85,41 @@ impl RenderMetrics { // such that we are horizontally centered. let line_height_y_adjust = (cell_height as f64 - metrics.cell_height.get().ceil()) / 2.; - let underline_height = metrics.underline_thickness.get().round().max(1.) as isize; + let config = fonts.config(); + let underline_height = match &config.underline_thickness { + None => metrics.underline_thickness.get().round().max(1.) as isize, + Some(d) => d + .evaluate_as_pixels(DimensionContext { + dpi: fonts.get_dpi() as f32, + pixel_max: metrics.underline_thickness.get() as f32, + pixel_cell: cell_height as f32, + }) + .max(1.) as isize, + }; - let descender_row = (cell_height as f64 - + (metrics.descender - metrics.underline_position).get() + let underline_position = match &config.underline_position { + None => metrics.underline_position.get(), + Some(d) => d.evaluate_as_pixels(DimensionContext { + dpi: fonts.get_dpi() as f32, + pixel_max: metrics.underline_position.get() as f32, + pixel_cell: cell_height as f32, + }) as f64, + }; + + let descender_row = (cell_height as f64 + (metrics.descender.get() - underline_position) - line_height_y_adjust) as isize; let descender_plus_two = (2 * underline_height + descender_row).min(cell_height as isize - underline_height); - let strike_row = descender_row / 2; + let strike_row = match &config.strikethrough_position { + None => descender_row / 2, + Some(d) => d + .evaluate_as_pixels(DimensionContext { + dpi: fonts.get_dpi() as f32, + pixel_max: descender_row as f32 / 2., + pixel_cell: cell_height as f32, + }) + .round() as isize, + }; Ok(Self { descender: metrics.descender - PixelLength::new(line_height_y_adjust),