aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/ri/river-mk-keymap/src/wayland/render
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-06-29 10:32:13 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-06-29 10:32:13 +0200
commit3d507acb42554b2551024ee3ca8490c203a1a9f8 (patch)
treececa79f3696cf9eab522be55c07c32e38de5edaf /pkgs/by-name/ri/river-mk-keymap/src/wayland/render
parentflake.lock: Update (diff)
downloadnixos-config-3d507acb42554b2551024ee3ca8490c203a1a9f8.zip
pkgs/river-mk-keymap: Improve with key-chord support and which-key interface
Diffstat (limited to 'pkgs/by-name/ri/river-mk-keymap/src/wayland/render')
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/render/layout.rs57
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/render/mod.rs129
2 files changed, 186 insertions, 0 deletions
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/layout.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/layout.rs
new file mode 100644
index 00000000..7f0aaec9
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/layout.rs
@@ -0,0 +1,57 @@
+use ab_glyph::{point, Font, Glyph, Point, ScaleFont};
+
+use crate::wayland::ansi::{StyledChar, StyledString};
+
+/// Simple paragraph layout for glyphs into `target`.
+/// Starts at position `(0, ascent)`.
+///
+/// This is for testing and examples.
+pub(super) fn layout_paragraph<F, SF, BF, BSF>(
+ font: SF,
+ bold_font: BSF,
+ position: Point,
+ max_width: f32,
+ text: &StyledString,
+ target: &mut Vec<(Glyph, StyledChar)>,
+) where
+ F: Font,
+ SF: ScaleFont<F>,
+ BF: Font,
+ BSF: ScaleFont<BF>,
+{
+ let v_advance = font.height() + font.line_gap();
+ let mut caret = position + point(0.0, font.ascent());
+ let mut last_glyph: Option<Glyph> = None;
+
+ for c in text.chars() {
+ if c.as_char().is_control() {
+ if c.as_char() == '\n' {
+ caret = point(position.x, caret.y + v_advance);
+ last_glyph = None;
+ }
+ continue;
+ }
+
+ let mut glyph = if c.is_bold() {
+ bold_font.scaled_glyph(c.as_char())
+ } else {
+ font.scaled_glyph(c.as_char())
+ };
+
+ if let Some(previous) = last_glyph.take() {
+ caret.x += font.kern(previous.id, glyph.id);
+ }
+ glyph.position = caret;
+
+ last_glyph = Some(glyph.clone());
+ caret.x += font.h_advance(glyph.id);
+
+ if !c.as_char().is_whitespace() && caret.x > position.x + max_width {
+ caret = point(position.x, caret.y + v_advance);
+ glyph.position = caret;
+ last_glyph = None;
+ }
+
+ target.push((glyph, c));
+ }
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/mod.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/mod.rs
new file mode 100644
index 00000000..e92def3c
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/mod.rs
@@ -0,0 +1,129 @@
+use std::{fs::File, io::Read};
+
+use ab_glyph::{point, Font, FontVec, PxScale, ScaleFont};
+use anyhow::{Context, Result};
+use font_kit::{
+ family_name::FamilyName, handle::Handle, properties::Properties, source::SystemSource,
+};
+
+use crate::wayland::ansi::{Color, StyledString};
+
+mod layout;
+
+fn get_font(weight: f32) -> Result<impl Font> {
+ let handle = SystemSource::new()
+ .select_best_match(
+ &[FamilyName::Monospace],
+ Properties::new().weight(font_kit::properties::Weight(weight)),
+ )
+ .context("Failed to find a monospace font")?;
+
+ match handle {
+ Handle::Path { path, font_index } => {
+ let data = {
+ let mut buffer = vec![];
+
+ let mut file = File::open(&path)?;
+ file.read_to_end(&mut buffer)?;
+ buffer
+ };
+
+ FontVec::try_from_vec_and_index(data, font_index).with_context(|| {
+ format!(
+ "Failed to load font at '{}' with index {}",
+ path.display(),
+ font_index
+ )
+ })
+ }
+ Handle::Memory { .. } => unimplemented!(),
+ }
+}
+
+pub(super) type ColorVec = (Vec<f32>, Vec<Option<Color>>);
+pub(super) fn text(input: &StyledString) -> Result<(ColorVec, (u32, u32))> {
+ let normal_font = get_font(400.0)?;
+ let bold_font = get_font(600.0)?;
+
+ let height: f32 = 15.0;
+ let px_height = height.ceil() as usize;
+
+ let scale = PxScale {
+ x: height,
+ y: height,
+ };
+
+ let scaled_font = normal_font.into_scaled(scale);
+ let bold_scaled_font = bold_font.into_scaled(scale);
+
+ let mut glyphs = Vec::new();
+ layout::layout_paragraph(
+ &scaled_font,
+ &bold_scaled_font,
+ point(0.0, 0.0),
+ 9999.0,
+ input,
+ &mut glyphs,
+ );
+
+ let px_width = glyphs
+ .iter()
+ .fold(0.0, |acc, (g, c)| {
+ let next = g.position.x
+ + if c.is_bold() {
+ bold_scaled_font.h_advance(g.id)
+ } else {
+ scaled_font.h_advance(g.id)
+ };
+
+ if next > acc {
+ next
+ } else {
+ acc
+ }
+ })
+ .ceil() as usize;
+
+ // Rasterise to a f32 alpha vec
+ let mut pixel_data = vec![0.0; px_width * px_height];
+ let mut color_data = vec![None; px_width * px_height];
+ for (g, c) in glyphs {
+ let maybe_glyph = if c.is_bold() {
+ bold_scaled_font.outline_glyph(g)
+ } else {
+ scaled_font.outline_glyph(g)
+ };
+
+ if let Some(og) = maybe_glyph {
+ let bounds = og.px_bounds();
+ og.draw(|x, y, v| {
+ let x = x as f32 + bounds.min.x;
+ let y = y as f32 + bounds.min.y;
+ let next_idx = x as usize + y as usize * px_width;
+
+ assure_idx(&mut pixel_data, next_idx, 0.0);
+ assure_idx(&mut color_data, next_idx, None);
+
+ // save the coverage alpha
+ pixel_data[next_idx] += v;
+ color_data[next_idx] = c.color();
+ });
+ }
+ }
+
+ let len = pixel_data.len();
+ Ok((
+ (pixel_data, color_data),
+ (px_width as u32, (len / px_width) as u32),
+ ))
+}
+
+fn assure_idx<T: Copy + Clone>(pixel_data: &mut Vec<T>, next_idx: usize, fill: T) {
+ let last = pixel_data.len() - 1;
+
+ if next_idx > last {
+ let needed = next_idx - last;
+
+ pixel_data.extend(vec![fill; needed]);
+ }
+}