From cc4836abb754485c99a3a2ee2b40404c9d47e7c1 Mon Sep 17 00:00:00 2001 From: nrq6993-a11y Date: Tue, 20 Oct 2026 07:01:46 +0300 Subject: [PATCH] G4: reuse Character panel Shared/Metric for mixed paragraph fields. Co-authored-by: Cursor --- crates/ui-egui/src/panels/character.rs | 8 ++-- crates/ui-egui/src/panels/paragraph.rs | 64 +++++--------------------- 2 files changed, 15 insertions(+), 57 deletions(-) diff --git a/crates/ui-egui/src/panels/character.rs b/crates/ui-egui/src/panels/character.rs index 65cf0c039..e7f653a14 100644 --- a/crates/ui-egui/src/panels/character.rs +++ b/crates/ui-egui/src/panels/character.rs @@ -68,7 +68,7 @@ fn shared_effective(app: &VectorcraftApp, f: impl Fn(&CharStyle) -> f64) -> Opti shared_styles(app, |t, style| t.effective_char_style(style).map(|s| Metric(f(&s)))).map(|v| v.0) } -struct Metric(f64); +pub(crate) struct Metric(pub(crate) f64); impl PartialEq for Metric { fn eq(&self, other: &Self) -> bool { @@ -109,21 +109,21 @@ fn shared_styles(app: &VectorcraftApp, f: impl Fn(&vectorcraft_doc } /// The value a run of styles share so far ([`shared`]). -enum Shared { +pub(crate) enum Shared { None, Same(T), Differ, } impl Shared { - fn add(&mut self, v: T) { + pub(crate) fn add(&mut self, v: T) { match self { Shared::None => *self = Shared::Same(v), Shared::Same(x) if *x != v => *self = Shared::Differ, _ => {} } } - fn value(self) -> Option { + pub(crate) fn value(self) -> Option { match self { Shared::Same(v) => Some(v), _ => None, diff --git a/crates/ui-egui/src/panels/paragraph.rs b/crates/ui-egui/src/panels/paragraph.rs index 85e81bd16..bf16cb906 100644 --- a/crates/ui-egui/src/panels/paragraph.rs +++ b/crates/ui-egui/src/panels/paragraph.rs @@ -7,7 +7,7 @@ use egui::{Ui, vec2}; use serde_json::{Value, json}; use vectorcraft_doc::{Composer, HyphenationOptions, Justify, NodeKind, ParaDirection, ParaStyle}; -use super::character::text_style; +use super::character::{Metric, Shared, text_style}; use super::{pstate, set_pstate}; use crate::VectorcraftApp; use crate::theme::Tokens; @@ -142,79 +142,37 @@ fn collect_text_alignment(text: &vectorcraft_doc::TextObject, range: Option<(usi record(index, &plain, start, end) // Includes empty text and the paragraph after a final newline. } -/// Compare paragraph distances with the same tolerance as Character dimensional fields. -struct ParaMetric(f64); - -impl PartialEq for ParaMetric { - fn eq(&self, other: &Self) -> bool { - (self.0 - other.0).abs() <= 1e-9 * self.0.abs().max(other.0.abs()).max(1.0) - } -} - -enum ParaShared { - None, - Same(T), - Differ, -} - -impl ParaShared { - fn add(&mut self, v: T) { - match self { - Self::None => *self = Self::Same(v), - Self::Same(old) if *old == v => {} - _ => *self = Self::Differ, - } - } - fn value(self) -> Option { - match self { - Self::Same(v) => Some(v), - _ => None, - } - } -} - -/// Visit every paragraph the Paragraph panel acts on; stop early when `f` returns false. -fn visit_paragraphs(app: &VectorcraftApp, mut f: impl FnMut(&ParaStyle) -> bool) -> bool { - let Some(st) = app.session.active() else { return false }; +/// Visit every paragraph the Paragraph panel acts on. +fn visit_paragraphs(app: &VectorcraftApp, mut f: impl FnMut(&ParaStyle)) { + let Some(st) = app.session.active() else { return }; if let Some((id, a, b)) = super::character::text_editing(app) && let Some(NodeKind::Text(text)) = st.doc.node(id).map(|n| &n.kind) { let range = text.paragraphs_in(a, b); for i in range { - if !f(text.para_at(i)) { - return false; - } + f(text.para_at(i)); } - return true; + return; } - let mut ok = true; for node in st.selection.objects.iter().filter_map(|id| st.doc.node(*id)) { node.walk(&mut |c| { if let NodeKind::Text(t) = &c.kind { for i in 0..t.paragraph_count() { - if !f(t.para_at(i)) { - ok = false; - } + f(t.para_at(i)); } } }); - if !ok { - return false; - } } - ok } /// A distance shared by every paragraph in scope, or `None` when they differ (blank spin field). fn shared_para_field(app: &VectorcraftApp, field: impl Fn(&ParaStyle) -> f64) -> Option { - let mut acc = ParaShared::None; + let mut acc = Shared::None; let mut any = false; - let record = |p: &ParaStyle| { + visit_paragraphs(app, |p| { any = true; - acc.add(ParaMetric(field(p))); - true - }; - visit_paragraphs(app, record); + acc.add(Metric(field(p))); + }); if !any { return text_style(app).map(|(_, p)| field(&p)); }