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()),
}),
],
},
]
}),
],
},
]
})
}
}
+189 -186
View File
@@ -26,7 +26,7 @@ impl CadModule for DrawModule {
"Draw"
}
fn ribbon_groups(&self) -> Vec<RibbonGroup> {
fn ribbon_groups(&self) -> &[RibbonGroup] {
use crate::modules::annotate::{
angular_dim, leader_cmd, linear_dim, mleader_cmd, mtext, radius_dim, text,
};
@@ -43,190 +43,193 @@ impl CadModule for DrawModule {
};
use properties::match_prop;
vec![
RibbonGroup {
title: "Draw",
tools: vec![
RibbonItem::LargeTool(line::tool()),
RibbonItem::LargeTool(polyline::tool()),
RibbonItem::LargeDropdown {
id: circle::DROPDOWN_ID,
label: "Circle",
icon: circle::ICON,
items: circle::DROPDOWN_ITEMS.to_vec(),
default: "CIRCLE",
},
RibbonItem::LargeDropdown {
id: arc::DROPDOWN_ID,
label: "Arc",
icon: arc::ICON,
items: arc::DROPDOWN_ITEMS.to_vec(),
default: "ARC",
},
RibbonItem::Dropdown {
id: shapes::DROPDOWN_ID,
icon: shapes::ICON,
items: shapes::DROPDOWN_ITEMS.to_vec(),
default: "RECT",
},
RibbonItem::Dropdown {
id: ellipse::DROPDOWN_ID,
icon: ellipse::ICON,
items: ellipse::DROPDOWN_ITEMS.to_vec(),
default: "ELLIPSE",
},
RibbonItem::Dropdown {
id: hatch::DROPDOWN_ID,
icon: hatch::ICON,
items: hatch::DROPDOWN_ITEMS.to_vec(),
default: "HATCH",
},
],
},
RibbonGroup {
title: "Modify",
tools: vec![
translate::tool().into(),
copy::tool().into(),
stretch::tool().into(),
rotate::tool().into(),
mirror::tool().into(),
scale::tool().into(),
RibbonItem::Dropdown {
id: trim::DROPDOWN_ID,
icon: trim::ICON,
items: trim::DROPDOWN_ITEMS.to_vec(),
default: "TRIM",
},
RibbonItem::Dropdown {
id: fillet::DROPDOWN_ID,
icon: fillet::ICON,
items: fillet::DROPDOWN_ITEMS.to_vec(),
default: "FILLET",
},
RibbonItem::Dropdown {
id: array::DROPDOWN_ID,
icon: array::ICON,
items: array::DROPDOWN_ITEMS.to_vec(),
default: "ARRAYRECT",
},
delete::tool().into(),
explode::tool().into(),
offset::tool().into(),
],
},
RibbonGroup {
title: "Annotation",
tools: vec![
RibbonItem::LargeDropdown {
id: "ANNOTATION_TEXT",
label: "Text",
icon: text::ICON,
items: vec![
(text::tool().id, text::tool().label, text::tool().icon),
(mtext::tool().id, mtext::tool().label, mtext::tool().icon),
],
default: "TEXT",
},
RibbonItem::LargeDropdown {
id: "ANNOTATION_DIMENSIONS",
label: "Dimensions",
icon: linear_dim::ICON,
items: vec![
(
linear_dim::tool().id,
linear_dim::tool().label,
linear_dim::tool().icon,
),
(
radius_dim::tool().id,
radius_dim::tool().label,
radius_dim::tool().icon,
),
(
angular_dim::tool().id,
angular_dim::tool().label,
angular_dim::tool().icon,
),
],
default: "DIMLINEAR",
},
RibbonItem::LargeDropdown {
id: "ANNOTATION_LEADER",
label: "Leader",
icon: leader_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",
},
],
},
RibbonGroup {
title: "Layers",
tools: vec![
RibbonItem::LargeTool(panel::tool()),
RibbonItem::LayerComboGroup {
row2: vec![
layoff::tool(),
layfrz::tool(),
laylck::tool(),
make_current::tool(),
],
row3: vec![
layon::tool(),
laythw::tool(),
layulk::tool(),
match_layer::tool(),
],
},
],
},
RibbonGroup {
title: "Block",
tools: vec![
RibbonItem::LargeTool(create_block::tool()),
RibbonItem::LargeTool(insert_block::tool()),
],
},
RibbonGroup {
title: "Properties",
tools: vec![RibbonItem::PropertiesGroup {
match_prop: match_prop::tool(),
}],
},
RibbonGroup {
title: "Groups",
tools: vec![
RibbonItem::LargeTool(group::tool()),
RibbonItem::LargeTool(ungroup::tool()),
],
},
RibbonGroup {
title: "Clipboard",
tools: vec![
RibbonItem::LargeDropdown {
id: "PASTE_MENU",
label: "Paste",
icon: paste::ICON,
items: paste::MENU_ITEMS.to_vec(),
default: "PASTECLIP",
},
copy_clip::tool().into(),
cut::tool().into(),
],
},
// Support group lives on the Start tab now (see view.rs:
// start_page_view). Removed from the Draw ribbon to declutter.
]
static GROUPS: std::sync::OnceLock<Vec<RibbonGroup>> = std::sync::OnceLock::new();
GROUPS.get_or_init(|| {
vec![
RibbonGroup {
title: "Draw",
tools: vec![
RibbonItem::LargeTool(line::tool()),
RibbonItem::LargeTool(polyline::tool()),
RibbonItem::LargeDropdown {
id: circle::DROPDOWN_ID,
label: "Circle",
icon: circle::ICON,
items: circle::DROPDOWN_ITEMS.to_vec(),
default: "CIRCLE",
},
RibbonItem::LargeDropdown {
id: arc::DROPDOWN_ID,
label: "Arc",
icon: arc::ICON,
items: arc::DROPDOWN_ITEMS.to_vec(),
default: "ARC",
},
RibbonItem::Dropdown {
id: shapes::DROPDOWN_ID,
icon: shapes::ICON,
items: shapes::DROPDOWN_ITEMS.to_vec(),
default: "RECT",
},
RibbonItem::Dropdown {
id: ellipse::DROPDOWN_ID,
icon: ellipse::ICON,
items: ellipse::DROPDOWN_ITEMS.to_vec(),
default: "ELLIPSE",
},
RibbonItem::Dropdown {
id: hatch::DROPDOWN_ID,
icon: hatch::ICON,
items: hatch::DROPDOWN_ITEMS.to_vec(),
default: "HATCH",
},
],
},
RibbonGroup {
title: "Modify",
tools: vec![
translate::tool().into(),
copy::tool().into(),
stretch::tool().into(),
rotate::tool().into(),
mirror::tool().into(),
scale::tool().into(),
RibbonItem::Dropdown {
id: trim::DROPDOWN_ID,
icon: trim::ICON,
items: trim::DROPDOWN_ITEMS.to_vec(),
default: "TRIM",
},
RibbonItem::Dropdown {
id: fillet::DROPDOWN_ID,
icon: fillet::ICON,
items: fillet::DROPDOWN_ITEMS.to_vec(),
default: "FILLET",
},
RibbonItem::Dropdown {
id: array::DROPDOWN_ID,
icon: array::ICON,
items: array::DROPDOWN_ITEMS.to_vec(),
default: "ARRAYRECT",
},
delete::tool().into(),
explode::tool().into(),
offset::tool().into(),
],
},
RibbonGroup {
title: "Annotation",
tools: vec![
RibbonItem::LargeDropdown {
id: "ANNOTATION_TEXT",
label: "Text",
icon: text::ICON,
items: vec![
(text::tool().id, text::tool().label, text::tool().icon),
(mtext::tool().id, mtext::tool().label, mtext::tool().icon),
],
default: "TEXT",
},
RibbonItem::LargeDropdown {
id: "ANNOTATION_DIMENSIONS",
label: "Dimensions",
icon: linear_dim::ICON,
items: vec![
(
linear_dim::tool().id,
linear_dim::tool().label,
linear_dim::tool().icon,
),
(
radius_dim::tool().id,
radius_dim::tool().label,
radius_dim::tool().icon,
),
(
angular_dim::tool().id,
angular_dim::tool().label,
angular_dim::tool().icon,
),
],
default: "DIMLINEAR",
},
RibbonItem::LargeDropdown {
id: "ANNOTATION_LEADER",
label: "Leader",
icon: leader_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",
},
],
},
RibbonGroup {
title: "Layers",
tools: vec![
RibbonItem::LargeTool(panel::tool()),
RibbonItem::LayerComboGroup {
row2: vec![
layoff::tool(),
layfrz::tool(),
laylck::tool(),
make_current::tool(),
],
row3: vec![
layon::tool(),
laythw::tool(),
layulk::tool(),
match_layer::tool(),
],
},
],
},
RibbonGroup {
title: "Block",
tools: vec![
RibbonItem::LargeTool(create_block::tool()),
RibbonItem::LargeTool(insert_block::tool()),
],
},
RibbonGroup {
title: "Properties",
tools: vec![RibbonItem::PropertiesGroup {
match_prop: match_prop::tool(),
}],
},
RibbonGroup {
title: "Groups",
tools: vec![
RibbonItem::LargeTool(group::tool()),
RibbonItem::LargeTool(ungroup::tool()),
],
},
RibbonGroup {
title: "Clipboard",
tools: vec![
RibbonItem::LargeDropdown {
id: "PASTE_MENU",
label: "Paste",
icon: paste::ICON,
items: paste::MENU_ITEMS.to_vec(),
default: "PASTECLIP",
},
copy_clip::tool().into(),
cut::tool().into(),
],
},
// Support group lives on the Start tab now (see view.rs:
// start_page_view). Removed from the Draw ribbon to declutter.
]
})
}
}
+89 -86
View File
@@ -35,91 +35,94 @@ impl CadModule for InsertModule {
"Insert"
}
fn ribbon_groups(&self) -> Vec<RibbonGroup> {
vec![
// ── Reference ────────────────────────────────────────────────────
RibbonGroup {
title: "Reference",
tools: vec![
RibbonItem::LargeTool(xattach::tool()),
RibbonItem::LargeTool(xclip::tool()),
RibbonItem::LargeTool(xadjust::tool()),
RibbonItem::Tool(underlay_layers::tool()),
RibbonItem::Dropdown {
id: "FRAMES_DROPDOWN",
icon: IconKind::Svg(include_bytes!(
"../../../assets/icons/underlay_frames.svg"
)),
items: vec![
(
"FRAMES0",
"Frames Off",
IconKind::Svg(include_bytes!(
"../../../assets/icons/underlay_frames.svg"
)),
),
(
"FRAMES1",
"Frames On",
IconKind::Svg(include_bytes!(
"../../../assets/icons/underlay_frames.svg"
)),
),
(
"FRAMES2",
"Frames & Print",
IconKind::Svg(include_bytes!(
"../../../assets/icons/underlay_frames.svg"
)),
),
],
default: "FRAMES1",
},
RibbonItem::Tool(snap_underlays::tool()),
],
},
// ── Point Cloud ───────────────────────────────────────────────────
RibbonGroup {
title: "Point Cloud",
tools: vec![RibbonItem::LargeTool(pc_attach::tool())],
},
// ── Block ─────────────────────────────────────────────────────────
RibbonGroup {
title: "Block",
tools: vec![
RibbonItem::LargeTool(mview_block::tool()),
RibbonItem::LargeTool(insert_block::tool()),
RibbonItem::Tool(create_block::tool()),
RibbonItem::Tool(edit_block::tool()),
RibbonItem::Tool(base_point::tool()),
],
},
// ── Attributes ────────────────────────────────────────────────────
RibbonGroup {
title: "Attributes",
tools: vec![
RibbonItem::LargeTool(attdef::tool()),
RibbonItem::LargeTool(attedit::tool()),
RibbonItem::Tool(attman::tool()),
RibbonItem::Tool(attsync::tool()),
],
},
// ── Import ────────────────────────────────────────────────────────
RibbonGroup {
title: "Import",
tools: vec![
RibbonItem::LargeTool(open_obj::tool()),
RibbonItem::LargeTool(landxml::tool()),
],
},
// ── Content ───────────────────────────────────────────────────────
RibbonGroup {
title: "Content",
tools: vec![
RibbonItem::LargeTool(content_browser::tool()),
RibbonItem::LargeTool(design_center::tool()),
],
},
]
fn ribbon_groups(&self) -> &[RibbonGroup] {
static GROUPS: std::sync::OnceLock<Vec<RibbonGroup>> = std::sync::OnceLock::new();
GROUPS.get_or_init(|| {
vec![
// ── Reference ────────────────────────────────────────────────────
RibbonGroup {
title: "Reference",
tools: vec![
RibbonItem::LargeTool(xattach::tool()),
RibbonItem::LargeTool(xclip::tool()),
RibbonItem::LargeTool(xadjust::tool()),
RibbonItem::Tool(underlay_layers::tool()),
RibbonItem::Dropdown {
id: "FRAMES_DROPDOWN",
icon: IconKind::Svg(include_bytes!(
"../../../assets/icons/underlay_frames.svg"
)),
items: vec![
(
"FRAMES0",
"Frames Off",
IconKind::Svg(include_bytes!(
"../../../assets/icons/underlay_frames.svg"
)),
),
(
"FRAMES1",
"Frames On",
IconKind::Svg(include_bytes!(
"../../../assets/icons/underlay_frames.svg"
)),
),
(
"FRAMES2",
"Frames & Print",
IconKind::Svg(include_bytes!(
"../../../assets/icons/underlay_frames.svg"
)),
),
],
default: "FRAMES1",
},
RibbonItem::Tool(snap_underlays::tool()),
],
},
// ── Point Cloud ───────────────────────────────────────────────────
RibbonGroup {
title: "Point Cloud",
tools: vec![RibbonItem::LargeTool(pc_attach::tool())],
},
// ── Block ─────────────────────────────────────────────────────────
RibbonGroup {
title: "Block",
tools: vec![
RibbonItem::LargeTool(mview_block::tool()),
RibbonItem::LargeTool(insert_block::tool()),
RibbonItem::Tool(create_block::tool()),
RibbonItem::Tool(edit_block::tool()),
RibbonItem::Tool(base_point::tool()),
],
},
// ── Attributes ────────────────────────────────────────────────────
RibbonGroup {
title: "Attributes",
tools: vec![
RibbonItem::LargeTool(attdef::tool()),
RibbonItem::LargeTool(attedit::tool()),
RibbonItem::Tool(attman::tool()),
RibbonItem::Tool(attsync::tool()),
],
},
// ── Import ────────────────────────────────────────────────────────
RibbonGroup {
title: "Import",
tools: vec![
RibbonItem::LargeTool(open_obj::tool()),
RibbonItem::LargeTool(landxml::tool()),
],
},
// ── Content ───────────────────────────────────────────────────────
RibbonGroup {
title: "Content",
tools: vec![
RibbonItem::LargeTool(content_browser::tool()),
RibbonItem::LargeTool(design_center::tool()),
],
},
]
})
}
}
+31 -26
View File
@@ -36,31 +36,36 @@ impl CadModule for LayoutModule {
"Layout"
}
fn ribbon_groups(&self) -> Vec<RibbonGroup> {
vec![
RibbonGroup {
title: "Viewport",
tools: vec![mview::tool().into()],
},
RibbonGroup {
title: "Plot",
tools: vec![
ToolDef {
id: "PAGESETUP",
label: "Page Setup",
icon: IconKind::Svg(include_bytes!("../../../assets/icons/pagesetup.svg")),
event: ModuleEvent::Command("PAGESETUP".to_string()),
}
.into(),
ToolDef {
id: "PLOT",
label: "Export PDF",
icon: IconKind::Svg(include_bytes!("../../../assets/icons/plot.svg")),
event: ModuleEvent::Command("PLOT".to_string()),
}
.into(),
],
},
]
fn ribbon_groups(&self) -> &[RibbonGroup] {
static GROUPS: std::sync::OnceLock<Vec<RibbonGroup>> = std::sync::OnceLock::new();
GROUPS.get_or_init(|| {
vec![
RibbonGroup {
title: "Viewport",
tools: vec![mview::tool().into()],
},
RibbonGroup {
title: "Plot",
tools: vec![
ToolDef {
id: "PAGESETUP",
label: "Page Setup",
icon: IconKind::Svg(include_bytes!(
"../../../assets/icons/pagesetup.svg"
)),
event: ModuleEvent::Command("PAGESETUP".to_string()),
}
.into(),
ToolDef {
id: "PLOT",
label: "Export PDF",
icon: IconKind::Svg(include_bytes!("../../../assets/icons/plot.svg")),
event: ModuleEvent::Command("PLOT".to_string()),
}
.into(),
],
},
]
})
}
}
+56 -53
View File
@@ -21,58 +21,61 @@ impl CadModule for ManageModule {
"Manage"
}
fn ribbon_groups(&self) -> Vec<RibbonGroup> {
vec![
// ── Customization ─────────────────────────────────────────────────
RibbonGroup {
title: "Customization",
tools: vec![
RibbonItem::LargeTool(user_interface::tool()),
RibbonItem::LargeTool(crate::modules::ToolDef {
id: "TOOLPALETTES",
label: "Tool\nPalettes",
icon: IconKind::Svg(include_bytes!(
"../../../assets/icons/tool_palettes.svg"
)),
event: crate::modules::ModuleEvent::Command("TOOLPALETTES".to_string()),
}),
RibbonItem::Tool(cui_import::tool()),
RibbonItem::Tool(cui_export::tool()),
RibbonItem::Dropdown {
id: "ALIASEDIT_DROPDOWN",
icon: IconKind::Svg(include_bytes!(
"../../../assets/icons/edit_aliases.svg"
)),
items: vec![
(
"ALIASEDIT",
"Edit Aliases",
IconKind::Svg(include_bytes!(
"../../../assets/icons/edit_aliases.svg"
)),
),
(
"CUILOAD",
"Load Partial CUI",
IconKind::Svg(include_bytes!(
"../../../assets/icons/cui_import.svg"
)),
),
],
default: "ALIASEDIT",
},
],
},
// ── Cleanup ───────────────────────────────────────────────────────
RibbonGroup {
title: "Cleanup",
tools: vec![
RibbonItem::LargeTool(find_nonpurgeable::tool()),
RibbonItem::Tool(purge::tool()),
RibbonItem::Tool(overkill::tool()),
RibbonItem::Tool(audit::tool()),
],
},
]
fn ribbon_groups(&self) -> &[RibbonGroup] {
static GROUPS: std::sync::OnceLock<Vec<RibbonGroup>> = std::sync::OnceLock::new();
GROUPS.get_or_init(|| {
vec![
// ── Customization ─────────────────────────────────────────────────
RibbonGroup {
title: "Customization",
tools: vec![
RibbonItem::LargeTool(user_interface::tool()),
RibbonItem::LargeTool(crate::modules::ToolDef {
id: "TOOLPALETTES",
label: "Tool\nPalettes",
icon: IconKind::Svg(include_bytes!(
"../../../assets/icons/tool_palettes.svg"
)),
event: crate::modules::ModuleEvent::Command("TOOLPALETTES".to_string()),
}),
RibbonItem::Tool(cui_import::tool()),
RibbonItem::Tool(cui_export::tool()),
RibbonItem::Dropdown {
id: "ALIASEDIT_DROPDOWN",
icon: IconKind::Svg(include_bytes!(
"../../../assets/icons/edit_aliases.svg"
)),
items: vec![
(
"ALIASEDIT",
"Edit Aliases",
IconKind::Svg(include_bytes!(
"../../../assets/icons/edit_aliases.svg"
)),
),
(
"CUILOAD",
"Load Partial CUI",
IconKind::Svg(include_bytes!(
"../../../assets/icons/cui_import.svg"
)),
),
],
default: "ALIASEDIT",
},
],
},
// ── Cleanup ───────────────────────────────────────────────────────
RibbonGroup {
title: "Cleanup",
tools: vec![
RibbonItem::LargeTool(find_nonpurgeable::tool()),
RibbonItem::Tool(purge::tool()),
RibbonItem::Tool(overkill::tool()),
RibbonItem::Tool(audit::tool()),
],
},
]
})
}
}
+32 -29
View File
@@ -40,34 +40,37 @@ impl CadModule for ModelModule {
"Model"
}
fn ribbon_groups(&self) -> Vec<RibbonGroup> {
vec![
RibbonGroup {
title: "Model",
tools: vec![
RibbonItem::LargeTool(tool("BOX", "Box", BOX_ICON)),
RibbonItem::LargeTool(tool("CYLINDER", "Cylinder", CYLINDER_ICON)),
RibbonItem::LargeTool(tool("CONE", "Cone", CONE_ICON)),
RibbonItem::LargeTool(tool("SPHERE", "Sphere", SPHERE_ICON)),
RibbonItem::Dropdown {
id: "MODEL_MORE",
icon: IconKind::Svg(WEDGE_ICON),
items: vec![
("WEDGE", "Wedge", IconKind::Svg(WEDGE_ICON)),
("TORUS", "Torus", IconKind::Svg(TORUS_ICON)),
],
default: "WEDGE",
},
],
},
RibbonGroup {
title: "Design",
tools: vec![
RibbonItem::LargeTool(tool("UNION", "Union", UNION_ICON)),
RibbonItem::LargeTool(tool("SUBTRACT", "Subtract", SUBTRACT_ICON)),
RibbonItem::LargeTool(tool("INTERSECT", "Intersect", INTERSECT_ICON)),
],
},
]
fn ribbon_groups(&self) -> &[RibbonGroup] {
static GROUPS: std::sync::OnceLock<Vec<RibbonGroup>> = std::sync::OnceLock::new();
GROUPS.get_or_init(|| {
vec![
RibbonGroup {
title: "Model",
tools: vec![
RibbonItem::LargeTool(tool("BOX", "Box", BOX_ICON)),
RibbonItem::LargeTool(tool("CYLINDER", "Cylinder", CYLINDER_ICON)),
RibbonItem::LargeTool(tool("CONE", "Cone", CONE_ICON)),
RibbonItem::LargeTool(tool("SPHERE", "Sphere", SPHERE_ICON)),
RibbonItem::Dropdown {
id: "MODEL_MORE",
icon: IconKind::Svg(WEDGE_ICON),
items: vec![
("WEDGE", "Wedge", IconKind::Svg(WEDGE_ICON)),
("TORUS", "Torus", IconKind::Svg(TORUS_ICON)),
],
default: "WEDGE",
},
],
},
RibbonGroup {
title: "Design",
tools: vec![
RibbonItem::LargeTool(tool("UNION", "Union", UNION_ICON)),
RibbonItem::LargeTool(tool("SUBTRACT", "Subtract", SUBTRACT_ICON)),
RibbonItem::LargeTool(tool("INTERSECT", "Intersect", INTERSECT_ICON)),
],
},
]
})
}
}
+93 -90
View File
@@ -45,96 +45,99 @@ impl CadModule for ViewModule {
"View"
}
fn ribbon_groups(&self) -> Vec<RibbonGroup> {
vec![
// ── Viewport Tools ───────────────────────────────────────────────
RibbonGroup {
title: "Viewport Tools",
tools: vec![
RibbonItem::LargeTool(ucs_icon::tool()),
RibbonItem::LargeTool(viewcube::tool()),
],
},
// ── Navigate ─────────────────────────────────────────────────────
RibbonGroup {
title: "Navigate",
tools: vec![
RibbonItem::LargeTool(zoom_ext::tool()),
RibbonItem::Tool(zoom_window::tool()),
RibbonItem::Tool(zoom_in::tool()),
RibbonItem::Tool(zoom_out::tool()),
RibbonItem::Tool(pan::tool()),
RibbonItem::Tool(orbit::tool()),
],
},
// ── Model Viewports ───────────────────────────────────────────────
RibbonGroup {
title: "Model Viewports",
tools: vec![
RibbonItem::LargeTool(vports_config::tool()),
RibbonItem::Tool(vports_named::tool()),
RibbonItem::Tool(vports_join::tool()),
RibbonItem::Tool(vports_restore::tool()),
],
},
// ── Visual Style ──────────────────────────────────────────────────
RibbonGroup {
// WIREFRAME and SOLID ids are special-cased in ribbon.rs
// for toggle-state highlighting based on Ribbon::wireframe.
title: "Visual Style",
tools: vec![RibbonItem::LargeDropdown {
id: "VISUAL_STYLE",
label: "Visual\nStyle",
icon: wireframe::tool().icon,
items: vec![
("WIREFRAME", "Wireframe", wireframe::tool().icon),
("SOLID", "Shaded", solid::tool().icon),
("HIDDEN", "Hidden", hidden::tool().icon),
("XRAY", "X-Ray", xray::tool().icon),
fn ribbon_groups(&self) -> &[RibbonGroup] {
static GROUPS: std::sync::OnceLock<Vec<RibbonGroup>> = std::sync::OnceLock::new();
GROUPS.get_or_init(|| {
vec![
// ── Viewport Tools ───────────────────────────────────────────────
RibbonGroup {
title: "Viewport Tools",
tools: vec![
RibbonItem::LargeTool(ucs_icon::tool()),
RibbonItem::LargeTool(viewcube::tool()),
],
default: "WIREFRAME",
}],
},
// ── Projection ────────────────────────────────────────────────────
RibbonGroup {
// ORTHO and PERSP ids are special-cased in ribbon.rs
// for toggle-state highlighting based on Camera::projection.
title: "Projection",
tools: vec![
RibbonItem::LargeTool(ortho::tool()),
RibbonItem::LargeTool(persp::tool()),
],
},
// ── Preset Views ──────────────────────────────────────────────────
RibbonGroup {
title: "Preset",
tools: vec![
RibbonItem::Tool(view_top::tool()),
RibbonItem::Tool(view_front::tool()),
RibbonItem::Tool(view_right::tool()),
RibbonItem::Tool(view_iso::tool()),
],
},
// ── Palettes ──────────────────────────────────────────────────────
RibbonGroup {
title: "Palettes",
tools: vec![
RibbonItem::LargeTool(tool_palettes::tool()),
RibbonItem::LargeTool(properties_palette::tool()),
RibbonItem::LargeTool(sheetset::tool()),
],
},
// ── Interface ─────────────────────────────────────────────────────
RibbonGroup {
title: "Interface",
tools: vec![
RibbonItem::LargeTool(file_tabs::tool()),
RibbonItem::LargeTool(layout_tabs::tool()),
RibbonItem::Tool(tile_horiz::tool()),
RibbonItem::Tool(tile_vert::tool()),
RibbonItem::Tool(cascade::tool()),
],
},
]
},
// ── Navigate ─────────────────────────────────────────────────────
RibbonGroup {
title: "Navigate",
tools: vec![
RibbonItem::LargeTool(zoom_ext::tool()),
RibbonItem::Tool(zoom_window::tool()),
RibbonItem::Tool(zoom_in::tool()),
RibbonItem::Tool(zoom_out::tool()),
RibbonItem::Tool(pan::tool()),
RibbonItem::Tool(orbit::tool()),
],
},
// ── Model Viewports ───────────────────────────────────────────────
RibbonGroup {
title: "Model Viewports",
tools: vec![
RibbonItem::LargeTool(vports_config::tool()),
RibbonItem::Tool(vports_named::tool()),
RibbonItem::Tool(vports_join::tool()),
RibbonItem::Tool(vports_restore::tool()),
],
},
// ── Visual Style ──────────────────────────────────────────────────
RibbonGroup {
// WIREFRAME and SOLID ids are special-cased in ribbon.rs
// for toggle-state highlighting based on Ribbon::wireframe.
title: "Visual Style",
tools: vec![RibbonItem::LargeDropdown {
id: "VISUAL_STYLE",
label: "Visual\nStyle",
icon: wireframe::tool().icon,
items: vec![
("WIREFRAME", "Wireframe", wireframe::tool().icon),
("SOLID", "Shaded", solid::tool().icon),
("HIDDEN", "Hidden", hidden::tool().icon),
("XRAY", "X-Ray", xray::tool().icon),
],
default: "WIREFRAME",
}],
},
// ── Projection ────────────────────────────────────────────────────
RibbonGroup {
// ORTHO and PERSP ids are special-cased in ribbon.rs
// for toggle-state highlighting based on Camera::projection.
title: "Projection",
tools: vec![
RibbonItem::LargeTool(ortho::tool()),
RibbonItem::LargeTool(persp::tool()),
],
},
// ── Preset Views ──────────────────────────────────────────────────
RibbonGroup {
title: "Preset",
tools: vec![
RibbonItem::Tool(view_top::tool()),
RibbonItem::Tool(view_front::tool()),
RibbonItem::Tool(view_right::tool()),
RibbonItem::Tool(view_iso::tool()),
],
},
// ── Palettes ──────────────────────────────────────────────────────
RibbonGroup {
title: "Palettes",
tools: vec![
RibbonItem::LargeTool(tool_palettes::tool()),
RibbonItem::LargeTool(properties_palette::tool()),
RibbonItem::LargeTool(sheetset::tool()),
],
},
// ── Interface ─────────────────────────────────────────────────────
RibbonGroup {
title: "Interface",
tools: vec![
RibbonItem::LargeTool(file_tabs::tool()),
RibbonItem::LargeTool(layout_tabs::tool()),
RibbonItem::Tool(tile_horiz::tool()),
RibbonItem::Tool(tile_vert::tool()),
RibbonItem::Tool(cascade::tool()),
],
},
]
})
}
}