perf(core): cache ribbon_groups() with OnceLock, avoid per-frame tree allocations

Change CadModule::ribbon_groups() from -> Vec<RibbonGroup> to -> &[RibbonGroup],
backed by a per-module OnceLock cache. The ribbon group tree is static data
(&'static str, Copy enums) that was being fully reconstructed — Vecs, String
clones, enum discriminants — on every call, including all three per-frame
call sites (view(), dropdown_overlay(), style_combo_overlay()).

- 7 built-in modules + 2 plugin templates cache via function-local OnceLock.
  Safe because each is a unit struct with exactly one instance per process
  (plugins run in isolated child processes; see PluginProcess::spawn()).
- SharedCadModule avoids the static pattern entirely, storing groups in an
  instance field (owned.rs).
- IPC runner converts &RibbonGroup -> OwnedRibbonGroup directly via new
  From<&T> impls, avoiding an intermediate clone.
- render_small/render_large and the two make_tool_row closures now borrow
  (&RibbonItem, &[ToolDef]) instead of taking ownership, so the view loop
  doesn't need to clone items to satisfy the old by-value signatures.
- Various match-ergonomics deref fixes (*id, *default, *icon, etc.) from
  the &RibbonItem pattern change.
This commit is contained in:
Karim Jerbi committed 2026-07-02 21:00:57 +01:00
1 parent 8164e14a19
commit 73b05e1dec
14 files changed
+947 -690

No files matched your search

+190 -183
View File
@@ -38,193 +38,200 @@ impl CadModule for AnnotateModule {
"Annotate"
}
fn ribbon_groups(&self) -> Vec<RibbonGroup> {
fn ribbon_groups(&self) -> &[RibbonGroup] {
use crate::modules::draw::draw::{revcloud, wipeout};
vec![
// ── Text ─────────────────────────────────────────────────────
RibbonGroup {
title: "Text",
tools: vec![
RibbonItem::LargeDropdown {
id: "ANNOTATE_TEXT",
label: "Multiline\nText",
icon: mtext::ICON,
items: vec![
(mtext::tool().id, mtext::tool().label, mtext::tool().icon),
(text::tool().id, text::tool().label, text::tool().icon),
(ddedit::tool().id, ddedit::tool().label, ddedit::tool().icon),
],
default: "MTEXT",
},
RibbonItem::StyleComboGroup {
style_key: StyleKey::TextStyle,
combo_id: "TEXT_STYLE_COMBO",
manager_cmd: Some("STYLE"),
rows: vec![vec![crate::modules::ToolDef {
id: "FIND",
label: "Find",
icon: crate::modules::IconKind::Svg(include_bytes!(
"../../../assets/icons/find.svg"
)),
event: crate::modules::ModuleEvent::Command("FIND".to_string()),
}]],
},
],
},
// ── Dimensions ───────────────────────────────────────────────
RibbonGroup {
title: "Dimensions",
tools: vec![
RibbonItem::LargeDropdown {
id: "ANNOTATE_DIM",
label: "Dimension",
icon: linear_dim::ICON,
items: vec![
(
linear_dim::tool().id,
linear_dim::tool().label,
linear_dim::tool().icon,
),
(
aligned_dim::tool().id,
aligned_dim::tool().label,
aligned_dim::tool().icon,
),
(
angular_dim::tool().id,
angular_dim::tool().label,
angular_dim::tool().icon,
),
(
radius_dim::tool().id,
radius_dim::tool().label,
radius_dim::tool().icon,
),
(
diameter_dim::tool().id,
diameter_dim::tool().label,
diameter_dim::tool().icon,
),
(
ordinate_dim::tool().id,
ordinate_dim::tool().label,
ordinate_dim::tool().icon,
),
(qdim::tool().id, qdim::tool().label, qdim::tool().icon),
],
default: "DIMLINEAR",
},
RibbonItem::StyleComboGroup {
style_key: StyleKey::DimStyle,
combo_id: "DIM_STYLE_COMBO",
manager_cmd: Some("DIMSTYLE"),
rows: vec![
vec![qdim::tool(), dim_continue::tool(), dim_baseline::tool()],
vec![
tolerance_cmd::tool(),
dimedit::tool(),
dimtedit::tool(),
dimbreak::tool(),
dimspace::tool(),
dimjogline::tool(),
static GROUPS: std::sync::OnceLock<Vec<RibbonGroup>> = std::sync::OnceLock::new();
GROUPS.get_or_init(|| {
vec![
// ── Text ─────────────────────────────────────────────────────
RibbonGroup {
title: "Text",
tools: vec![
RibbonItem::LargeDropdown {
id: "ANNOTATE_TEXT",
label: "Multiline\nText",
icon: mtext::ICON,
items: vec![
(mtext::tool().id, mtext::tool().label, mtext::tool().icon),
(text::tool().id, text::tool().label, text::tool().icon),
(ddedit::tool().id, ddedit::tool().label, ddedit::tool().icon),
],
],
},
],
},
// ── Leaders ──────────────────────────────────────────────────
RibbonGroup {
title: "Leaders",
tools: vec![
RibbonItem::LargeDropdown {
id: "ANNOTATE_LEADER",
label: "Multileader",
icon: mleader_cmd::ICON,
items: vec![
(
mleader_cmd::tool().id,
mleader_cmd::tool().label,
mleader_cmd::tool().icon,
default: "MTEXT",
},
RibbonItem::StyleComboGroup {
style_key: StyleKey::TextStyle,
combo_id: "TEXT_STYLE_COMBO",
manager_cmd: Some("STYLE"),
rows: vec![vec![crate::modules::ToolDef {
id: "FIND",
label: "Find",
icon: crate::modules::IconKind::Svg(include_bytes!(
"../../../assets/icons/find.svg"
)),
event: crate::modules::ModuleEvent::Command("FIND".to_string()),
}]],
},
],
},
// ── Dimensions ───────────────────────────────────────────────
RibbonGroup {
title: "Dimensions",
tools: vec![
RibbonItem::LargeDropdown {
id: "ANNOTATE_DIM",
label: "Dimension",
icon: linear_dim::ICON,
items: vec![
(
linear_dim::tool().id,
linear_dim::tool().label,
linear_dim::tool().icon,
),
(
aligned_dim::tool().id,
aligned_dim::tool().label,
aligned_dim::tool().icon,
),
(
angular_dim::tool().id,
angular_dim::tool().label,
angular_dim::tool().icon,
),
(
radius_dim::tool().id,
radius_dim::tool().label,
radius_dim::tool().icon,
),
(
diameter_dim::tool().id,
diameter_dim::tool().label,
diameter_dim::tool().icon,
),
(
ordinate_dim::tool().id,
ordinate_dim::tool().label,
ordinate_dim::tool().icon,
),
(qdim::tool().id, qdim::tool().label, qdim::tool().icon),
],
default: "DIMLINEAR",
},
RibbonItem::StyleComboGroup {
style_key: StyleKey::DimStyle,
combo_id: "DIM_STYLE_COMBO",
manager_cmd: Some("DIMSTYLE"),
rows: vec![
vec![qdim::tool(), dim_continue::tool(), dim_baseline::tool()],
vec![
tolerance_cmd::tool(),
dimedit::tool(),
dimtedit::tool(),
dimbreak::tool(),
dimspace::tool(),
dimjogline::tool(),
],
],
},
],
},
// ── Leaders ──────────────────────────────────────────────────
RibbonGroup {
title: "Leaders",
tools: vec![
RibbonItem::LargeDropdown {
id: "ANNOTATE_LEADER",
label: "Multileader",
icon: mleader_cmd::ICON,
items: vec![
(
mleader_cmd::tool().id,
mleader_cmd::tool().label,
mleader_cmd::tool().icon,
),
(
leader_cmd::tool().id,
leader_cmd::tool().label,
leader_cmd::tool().icon,
),
],
default: "MLEADER",
},
RibbonItem::StyleComboGroup {
style_key: StyleKey::MLeaderStyle,
combo_id: "MLEADER_STYLE_COMBO",
manager_cmd: Some("MLEADERSTYLE"),
rows: vec![
vec![mleader_edit::tool_add(), mleader_edit::tool_remove()],
vec![mleader_edit::tool_align(), mleader_edit::tool_collect()],
],
},
],
},
// ── Tables ───────────────────────────────────────────────────
RibbonGroup {
title: "Tables",
tools: vec![
RibbonItem::LargeTool(table_cmd::tool()),
RibbonItem::StyleComboGroup {
style_key: StyleKey::TableStyle,
combo_id: "TABLE_STYLE_COMBO",
manager_cmd: Some("TABLESTYLE"),
rows: vec![vec![data_extract::tool(), data_link::tool()]],
},
],
},
// ── Markup ───────────────────────────────────────────────────
RibbonGroup {
title: "Markup",
tools: vec![
RibbonItem::LargeTool(wipeout::tool()),
RibbonItem::LargeTool(revcloud::tool()),
],
},
// ── Annotation Scaling ───────────────────────────────────────
RibbonGroup {
title: "Annotation Scaling",
tools: vec![
RibbonItem::Tool(crate::modules::ToolDef {
id: "ANNOSCALE",
label: "Scale List",
icon: crate::modules::IconKind::Svg(include_bytes!(
"../../../assets/icons/scale_list.svg"
)),
event: crate::modules::ModuleEvent::Command("ANNOSCALE".to_string()),
}),
RibbonItem::Tool(crate::modules::ToolDef {
id: "OBJECTSCALE",
label: "Add Scale",
icon: crate::modules::IconKind::Svg(include_bytes!(
"../../../assets/icons/add_scale.svg"
)),
event: crate::modules::ModuleEvent::Command("OBJECTSCALE".to_string()),
}),
RibbonItem::Tool(crate::modules::ToolDef {
id: "SCALELISTEDIT",
label: "Scale Edit",
icon: crate::modules::IconKind::Svg(include_bytes!(
"../../../assets/icons/scale_list.svg"
)),
event: crate::modules::ModuleEvent::Command(
"SCALELISTEDIT".to_string(),
),
(
leader_cmd::tool().id,
leader_cmd::tool().label,
leader_cmd::tool().icon,
}),
RibbonItem::Tool(crate::modules::ToolDef {
id: "SYNCPVIEWPORTS",
label: "Sync Scales",
icon: crate::modules::IconKind::Svg(include_bytes!(
"../../../assets/icons/sync.svg"
)),
event: crate::modules::ModuleEvent::Command(
"SYNCPVIEWPORTS".to_string(),
),
],
default: "MLEADER",
},
RibbonItem::StyleComboGroup {
style_key: StyleKey::MLeaderStyle,
combo_id: "MLEADER_STYLE_COMBO",
manager_cmd: Some("MLEADERSTYLE"),
rows: vec![
vec![mleader_edit::tool_add(), mleader_edit::tool_remove()],
vec![mleader_edit::tool_align(), mleader_edit::tool_collect()],
],
},
],
},
// ── Tables ───────────────────────────────────────────────────
RibbonGroup {
title: "Tables",
tools: vec![
RibbonItem::LargeTool(table_cmd::tool()),
RibbonItem::StyleComboGroup {
style_key: StyleKey::TableStyle,
combo_id: "TABLE_STYLE_COMBO",
manager_cmd: Some("TABLESTYLE"),
rows: vec![vec![data_extract::tool(), data_link::tool()]],
},
],
},
// ── Markup ───────────────────────────────────────────────────
RibbonGroup {
title: "Markup",
tools: vec![
RibbonItem::LargeTool(wipeout::tool()),
RibbonItem::LargeTool(revcloud::tool()),
],
},
// ── Annotation Scaling ───────────────────────────────────────
RibbonGroup {
title: "Annotation Scaling",
tools: vec![
RibbonItem::Tool(crate::modules::ToolDef {
id: "ANNOSCALE",
label: "Scale List",
icon: crate::modules::IconKind::Svg(include_bytes!(
"../../../assets/icons/scale_list.svg"
)),
event: crate::modules::ModuleEvent::Command("ANNOSCALE".to_string()),
}),
RibbonItem::Tool(crate::modules::ToolDef {
id: "OBJECTSCALE",
label: "Add Scale",
icon: crate::modules::IconKind::Svg(include_bytes!(
"../../../assets/icons/add_scale.svg"
)),
event: crate::modules::ModuleEvent::Command("OBJECTSCALE".to_string()),
}),
RibbonItem::Tool(crate::modules::ToolDef {
id: "SCALELISTEDIT",
label: "Scale Edit",
icon: crate::modules::IconKind::Svg(include_bytes!(
"../../../assets/icons/scale_list.svg"
)),
event: crate::modules::ModuleEvent::Command("SCALELISTEDIT".to_string()),
}),
RibbonItem::Tool(crate::modules::ToolDef {
id: "SYNCPVIEWPORTS",
label: "Sync Scales",
icon: crate::modules::IconKind::Svg(include_bytes!(
"../../../assets/icons/sync.svg"
)),
event: crate::modules::ModuleEvent::Command("SYNCPVIEWPORTS".to_string()),
}),
],
},
]
}),
],
},
]
})
}
}