From 2fb9908329a111457d927aa3e0f7f83d07514b49 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Sun, 3 Aug 2025 10:08:03 +0200 Subject: [PATCH] Fix markdown monospace in headings --- src/app.rs | 41 +++++++++++++++++++------------------ src/handwriting/mod.rs | 10 ++++----- src/lib.rs | 1 + src/markdown/highlighter.rs | 31 +++++++++++++++++++--------- src/markdown/parser.rs | 10 ++++----- src/text_styles.rs | 14 +++++++++++++ 6 files changed, 65 insertions(+), 42 deletions(-) create mode 100644 src/text_styles.rs diff --git a/src/app.rs b/src/app.rs index 90895e9..30aecf3 100644 --- a/src/app.rs +++ b/src/app.rs @@ -11,10 +11,11 @@ use crate::{ file_editor::{FileEditor, SaveStatus}, folder::Folder, preferences::Preferences, + text_styles::{H1, H1_MONO, H2, H2_MONO, H3, H3_MONO, H4, H4_MONO, H5, H5_MONO, H6, H6_MONO}, util::{GuiSender, file_mtime, log_error}, }; use egui::{ - Align, Button, Context, FontData, FontDefinitions, FontId, Image, Key, Modifiers, + Align, Button, Context, FontData, FontDefinitions, FontFamily, FontId, Image, Key, Modifiers, PointerButton, RichText, ScrollArea, Theme, Widget, include_image, }; use eyre::eyre; @@ -190,35 +191,35 @@ impl App { .map(|(name, data)| (name.to_string(), Arc::new(FontData::from_static(data)))) .collect(); - fonts.families.insert( - egui::FontFamily::Proportional, - vec!["IosevkaAile-Regular".into()], - ); + fonts + .families + .insert(FontFamily::Proportional, vec!["IosevkaAile-Regular".into()]); fonts .families - .insert(egui::FontFamily::Monospace, vec!["Iosevka-Thin".into()]); + .insert(FontFamily::Monospace, vec!["Iosevka-Thin".into()]); cc.egui_ctx.set_fonts(fonts); // markdown font styles for theme in [Theme::Dark, Theme::Light] { cc.egui_ctx.style_mut_of(theme, |style| { - for (name, size) in [ - ("H1", 28.0), - ("H2", 26.0), - ("H3", 24.0), - ("H4", 22.0), - ("H5", 20.0), - ("H6", 18.0), + for (name, size, family) in [ + (H1, 28.0, FontFamily::Proportional), + (H2, 26.0, FontFamily::Proportional), + (H3, 24.0, FontFamily::Proportional), + (H4, 22.0, FontFamily::Proportional), + (H5, 20.0, FontFamily::Proportional), + (H6, 18.0, FontFamily::Proportional), + (H1_MONO, 28.0, FontFamily::Monospace), + (H2_MONO, 26.0, FontFamily::Monospace), + (H3_MONO, 24.0, FontFamily::Monospace), + (H4_MONO, 22.0, FontFamily::Monospace), + (H5_MONO, 20.0, FontFamily::Monospace), + (H6_MONO, 18.0, FontFamily::Monospace), ] { - style.text_styles.insert( - egui::TextStyle::Name(name.into()), - FontId { - size, - family: egui::FontFamily::Proportional, - }, - ); + let name = egui::TextStyle::Name(name.into()); + style.text_styles.insert(name, FontId { size, family }); } }); } diff --git a/src/handwriting/mod.rs b/src/handwriting/mod.rs index 5560937..62fcaa8 100644 --- a/src/handwriting/mod.rs +++ b/src/handwriting/mod.rs @@ -82,6 +82,8 @@ struct Ephemeral { current_stroke: Vec, /// The lines that have not been blitted to `texture` yet. + /// + /// Each pair of [Pos2]s is the start and end of one line. unblitted_lines: Vec<[Pos2; 2]>, tessellator: Option, @@ -425,7 +427,7 @@ impl Handwriting { last_mesh_ctx, .. } = &mut self.e; - // TODO: don't tessellate and rasterize on the GUI thread + // TODO: avoid tessellating and rasterizing on the GUI thread *last_mesh_ctx = Some(mesh_context); @@ -444,10 +446,7 @@ impl Handwriting { .iter() .chain([&*current_stroke]) .filter(|stroke| stroke.len() >= 2) - .map(|stroke| { - //let points: Vec = stroke.iter().map(|&p| to_screen * p).collect(); - egui::Shape::line(stroke.clone(), style.stroke) - }) + .map(|stroke| egui::Shape::line(stroke.clone(), style.stroke)) .for_each(|shape| { tessellator.tessellate_shape(shape, mesh); }); @@ -480,7 +479,6 @@ impl Handwriting { ui.vertical_centered_justified(|ui| { self.ui_control(None, ui, &mut response); - //ui.label("Paint with your mouse/touch!"); Frame::canvas(ui.style()) .corner_radius(20.0) .stroke(Stroke::new(5.0, Color32::from_black_alpha(40))) diff --git a/src/lib.rs b/src/lib.rs index ba1c882..38641e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub mod markdown; pub mod preferences; pub mod rasterizer; pub mod text_editor; +pub mod text_styles; pub mod util; pub use app::App; diff --git a/src/markdown/highlighter.rs b/src/markdown/highlighter.rs index e1a5877..3ae9fe9 100644 --- a/src/markdown/highlighter.rs +++ b/src/markdown/highlighter.rs @@ -1,6 +1,9 @@ use egui::text::{CCursorRange, LayoutJob}; -use crate::markdown::Heading; +use crate::{ + markdown::Heading, + text_styles::{H1, H1_MONO, H2, H2_MONO, H3, H3_MONO, H4, H4_MONO, H5, H5_MONO, H6, H6_MONO}, +}; use super::{Item, Style, parse}; @@ -36,7 +39,6 @@ pub fn highlight_markdown( _cursor: Option, ) -> LayoutJob { let mut job = LayoutJob::default(); - let code_style = Style { code: true, ..Default::default() @@ -74,14 +76,23 @@ fn format_from_style(egui_style: &egui::Style, style: &Style) -> egui::text::Tex }; let text_style = if let Some(heading) = style.heading { - match heading { - Heading::H1 => TextStyle::Name("H1".into()), - Heading::H2 => TextStyle::Name("H2".into()), - Heading::H3 => TextStyle::Name("H3".into()), - Heading::H4 => TextStyle::Name("H4".into()), - Heading::H5 => TextStyle::Name("H5".into()), - Heading::H6 => TextStyle::Name("H6".into()), - } + let text_style = match (heading, style.code) { + (Heading::H1, false) => H1, + (Heading::H2, false) => H2, + (Heading::H3, false) => H3, + (Heading::H4, false) => H4, + (Heading::H5, false) => H5, + (Heading::H6, false) => H6, + + (Heading::H1, true) => H1_MONO, + (Heading::H2, true) => H2_MONO, + (Heading::H3, true) => H3_MONO, + (Heading::H4, true) => H4_MONO, + (Heading::H5, true) => H5_MONO, + (Heading::H6, true) => H6_MONO, + }; + + TextStyle::Name(text_style.into()) } else if style.code { TextStyle::Monospace } else if style.small | style.raised { diff --git a/src/markdown/parser.rs b/src/markdown/parser.rs index 5bf7b68..c522c42 100644 --- a/src/markdown/parser.rs +++ b/src/markdown/parser.rs @@ -16,11 +16,6 @@ pub fn parse_tokens<'a>(mut tokens: &[Token<'a>]) -> Vec> { let mut style = Style::default(); - let mono_style = Style { - code: true, - ..Default::default() - }; - iter::from_fn(move || { if tokens.is_empty() { return None; @@ -73,7 +68,10 @@ pub fn parse_tokens<'a>(mut tokens: &[Token<'a>]) -> Vec> { any_of([TokenKind::Mono, TokenKind::CodeBlock, TokenKind::Newline]), ); - return Some(Item::Text { span, style: mono_style}); + let mut style = style; + style.code = true; + + return Some(Item::Text { span, style }); } // TODO: different heading strengths diff --git a/src/text_styles.rs b/src/text_styles.rs new file mode 100644 index 0000000..1a4b0b8 --- /dev/null +++ b/src/text_styles.rs @@ -0,0 +1,14 @@ +//! Name of custom [egui::TextStyle]s + +pub const H1: &str = "H1"; +pub const H2: &str = "H2"; +pub const H3: &str = "H3"; +pub const H4: &str = "H4"; +pub const H5: &str = "H5"; +pub const H6: &str = "H6"; +pub const H1_MONO: &str = "H1-mono"; +pub const H2_MONO: &str = "H2-mono"; +pub const H3_MONO: &str = "H3-mono"; +pub const H4_MONO: &str = "H4-mono"; +pub const H5_MONO: &str = "H5-mono"; +pub const H6_MONO: &str = "H6-mono";