diff --git a/assets/icons/add_scale.svg b/assets/icons/add_scale.svg
new file mode 100644
index 000000000..d5a9aa869
--- /dev/null
+++ b/assets/icons/add_scale.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/box3d.svg b/assets/icons/box3d.svg
new file mode 100644
index 000000000..5d2e02e43
--- /dev/null
+++ b/assets/icons/box3d.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/icons/bylayer.svg b/assets/icons/bylayer.svg
new file mode 100644
index 000000000..2d9f9e6f4
--- /dev/null
+++ b/assets/icons/bylayer.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/clear_tool.svg b/assets/icons/clear_tool.svg
new file mode 100644
index 000000000..4c3f4cad1
--- /dev/null
+++ b/assets/icons/clear_tool.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/icons/color_palette.svg b/assets/icons/color_palette.svg
new file mode 100644
index 000000000..83e030924
--- /dev/null
+++ b/assets/icons/color_palette.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/cone3d.svg b/assets/icons/cone3d.svg
new file mode 100644
index 000000000..786be248c
--- /dev/null
+++ b/assets/icons/cone3d.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/cylinder3d.svg b/assets/icons/cylinder3d.svg
new file mode 100644
index 000000000..971b7470a
--- /dev/null
+++ b/assets/icons/cylinder3d.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/import_obj.svg b/assets/icons/import_obj.svg
new file mode 100644
index 000000000..0f33211ea
--- /dev/null
+++ b/assets/icons/import_obj.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/intersect.svg b/assets/icons/intersect.svg
new file mode 100644
index 000000000..cb8d30eea
--- /dev/null
+++ b/assets/icons/intersect.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/options_tool.svg b/assets/icons/options_tool.svg
new file mode 100644
index 000000000..00aacd91e
--- /dev/null
+++ b/assets/icons/options_tool.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/pagesetup.svg b/assets/icons/pagesetup.svg
new file mode 100644
index 000000000..670071847
--- /dev/null
+++ b/assets/icons/pagesetup.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/plot.svg b/assets/icons/plot.svg
new file mode 100644
index 000000000..6919dfb54
--- /dev/null
+++ b/assets/icons/plot.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/scale_list.svg b/assets/icons/scale_list.svg
new file mode 100644
index 000000000..f69a11745
--- /dev/null
+++ b/assets/icons/scale_list.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/sphere3d.svg b/assets/icons/sphere3d.svg
new file mode 100644
index 000000000..e31784e6c
--- /dev/null
+++ b/assets/icons/sphere3d.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/subtract.svg b/assets/icons/subtract.svg
new file mode 100644
index 000000000..467707538
--- /dev/null
+++ b/assets/icons/subtract.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/sync.svg b/assets/icons/sync.svg
new file mode 100644
index 000000000..a2862207e
--- /dev/null
+++ b/assets/icons/sync.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/torus3d.svg b/assets/icons/torus3d.svg
new file mode 100644
index 000000000..58d5e55dc
--- /dev/null
+++ b/assets/icons/torus3d.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/ui/arrow_long_right.svg b/assets/icons/ui/arrow_long_right.svg
new file mode 100644
index 000000000..bb1c6af58
--- /dev/null
+++ b/assets/icons/ui/arrow_long_right.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/bolt.svg b/assets/icons/ui/bolt.svg
new file mode 100644
index 000000000..8a15b6db5
--- /dev/null
+++ b/assets/icons/ui/bolt.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/check.svg b/assets/icons/ui/check.svg
new file mode 100644
index 000000000..4b591b1ea
--- /dev/null
+++ b/assets/icons/ui/check.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/close.svg b/assets/icons/ui/close.svg
new file mode 100644
index 000000000..2c99b30d3
--- /dev/null
+++ b/assets/icons/ui/close.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/doc.svg b/assets/icons/ui/doc.svg
new file mode 100644
index 000000000..80125d9c6
--- /dev/null
+++ b/assets/icons/ui/doc.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/doc_new.svg b/assets/icons/ui/doc_new.svg
new file mode 100644
index 000000000..4621403ae
--- /dev/null
+++ b/assets/icons/ui/doc_new.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/dot.svg b/assets/icons/ui/dot.svg
new file mode 100644
index 000000000..7bd84fbda
--- /dev/null
+++ b/assets/icons/ui/dot.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/folder.svg b/assets/icons/ui/folder.svg
new file mode 100644
index 000000000..e3070d521
--- /dev/null
+++ b/assets/icons/ui/folder.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/gear.svg b/assets/icons/ui/gear.svg
new file mode 100644
index 000000000..fefd22e21
--- /dev/null
+++ b/assets/icons/ui/gear.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/heart.svg b/assets/icons/ui/heart.svg
new file mode 100644
index 000000000..cf6eee6ae
--- /dev/null
+++ b/assets/icons/ui/heart.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/help.svg b/assets/icons/ui/help.svg
new file mode 100644
index 000000000..6d780c085
--- /dev/null
+++ b/assets/icons/ui/help.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/menu.svg b/assets/icons/ui/menu.svg
new file mode 100644
index 000000000..4e35c167e
--- /dev/null
+++ b/assets/icons/ui/menu.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/move.svg b/assets/icons/ui/move.svg
new file mode 100644
index 000000000..a9a0a99c1
--- /dev/null
+++ b/assets/icons/ui/move.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/plus.svg b/assets/icons/ui/plus.svg
new file mode 100644
index 000000000..da0f8d59e
--- /dev/null
+++ b/assets/icons/ui/plus.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/print.svg b/assets/icons/ui/print.svg
new file mode 100644
index 000000000..a638b027f
--- /dev/null
+++ b/assets/icons/ui/print.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/redo.svg b/assets/icons/ui/redo.svg
new file mode 100644
index 000000000..5c18e6988
--- /dev/null
+++ b/assets/icons/ui/redo.svg
@@ -0,0 +1,43 @@
+
+
diff --git a/assets/icons/ui/save.svg b/assets/icons/ui/save.svg
new file mode 100644
index 000000000..63c3b94e2
--- /dev/null
+++ b/assets/icons/ui/save.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/split_h.svg b/assets/icons/ui/split_h.svg
new file mode 100644
index 000000000..3255bbd52
--- /dev/null
+++ b/assets/icons/ui/split_h.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/split_v.svg b/assets/icons/ui/split_v.svg
new file mode 100644
index 000000000..1535d053f
--- /dev/null
+++ b/assets/icons/ui/split_v.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/trash.svg b/assets/icons/ui/trash.svg
new file mode 100644
index 000000000..4f8ba53dc
--- /dev/null
+++ b/assets/icons/ui/trash.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/tri_down.svg b/assets/icons/ui/tri_down.svg
new file mode 100644
index 000000000..3a249b0f7
--- /dev/null
+++ b/assets/icons/ui/tri_down.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/tri_left.svg b/assets/icons/ui/tri_left.svg
new file mode 100644
index 000000000..ed0e987d0
--- /dev/null
+++ b/assets/icons/ui/tri_left.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/tri_right.svg b/assets/icons/ui/tri_right.svg
new file mode 100644
index 000000000..247aeed96
--- /dev/null
+++ b/assets/icons/ui/tri_right.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/tri_up.svg b/assets/icons/ui/tri_up.svg
new file mode 100644
index 000000000..3f21b0174
--- /dev/null
+++ b/assets/icons/ui/tri_up.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/ui/undo.svg b/assets/icons/ui/undo.svg
new file mode 100644
index 000000000..32f020142
--- /dev/null
+++ b/assets/icons/ui/undo.svg
@@ -0,0 +1,43 @@
+
+
diff --git a/assets/icons/ui/up.svg b/assets/icons/ui/up.svg
new file mode 100644
index 000000000..b1e7b4521
--- /dev/null
+++ b/assets/icons/ui/up.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/union.svg b/assets/icons/union.svg
new file mode 100644
index 000000000..fcee8244c
--- /dev/null
+++ b/assets/icons/union.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/viewport.svg b/assets/icons/viewport.svg
new file mode 100644
index 000000000..4f19585c0
--- /dev/null
+++ b/assets/icons/viewport.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/wedge3d.svg b/assets/icons/wedge3d.svg
new file mode 100644
index 000000000..753610968
--- /dev/null
+++ b/assets/icons/wedge3d.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/app/view.rs b/src/app/view.rs
index ffd5220e4..d792efc03 100644
--- a/src/app/view.rs
+++ b/src/app/view.rs
@@ -367,13 +367,17 @@ impl OpenCADStudio {
if !is_paper && !tab.is_start {
// Render-mode picker + viewport-split buttons (horizontal /
// vertical divider of the active Model tile).
- let split_btn = |glyph: &'static str, horizontal: bool| {
- button(text(glyph).size(13).color(Color {
- r: 0.85,
- g: 0.85,
- b: 0.85,
- a: 1.0,
- }))
+ let split_btn = |bytes: &'static [u8], horizontal: bool| {
+ button(crate::ui::icons::tinted(
+ bytes,
+ 14.0,
+ Color {
+ r: 0.85,
+ g: 0.85,
+ b: 0.85,
+ a: 1.0,
+ },
+ ))
.on_press(Message::SplitModelViewport(horizontal))
.padding([4, 8])
.style(|_: &Theme, status| iced::widget::button::Style {
@@ -410,7 +414,12 @@ impl OpenCADStudio {
..Default::default()
})
};
- let bar = row![info, split_btn("▤", true), split_btn("▥", false)].spacing(4);
+ let bar = row![
+ info,
+ split_btn(crate::ui::icons::SPLIT_H, true),
+ split_btn(crate::ui::icons::SPLIT_V, false)
+ ]
+ .spacing(4);
// Position the bar at the active model tile's top-left corner so
// it follows the active panel in a tiled layout (full canvas when
// a single tile fills the window). Leading Spaces offset it.
@@ -636,9 +645,20 @@ impl OpenCADStudio {
let mut col = column![].spacing(0).width(iced::Length::Fixed(row_w));
for (idx, name) in popup.items.iter().enumerate() {
let is_cur = popup.current == Some(idx);
- let label = format!("{} {}", if is_cur { "✓" } else { " " }, name);
- let btn = button(text(label).size(12).color(Color::WHITE))
- .on_press(Message::VisibilityPick(idx))
+ let mark: Element<'_, Message> = if is_cur {
+ crate::ui::icons::tinted(crate::ui::icons::CHECK, 11.0, Color::WHITE)
+ } else {
+ Space::new().width(11).into()
+ };
+ let btn = button(
+ row![
+ container(mark).width(16),
+ text(name).size(12).color(Color::WHITE),
+ ]
+ .spacing(2)
+ .align_y(iced::Center),
+ )
+ .on_press(Message::VisibilityPick(idx))
.padding([3, 10])
.width(Fill)
.style(move |_: &Theme, status| iced::widget::button::Style {
@@ -1851,13 +1871,28 @@ pub(super) fn doc_tab_bar<'a>(tabs: &'a [DocumentTab], active_tab: usize) -> Ele
for (idx, tab) in tabs.iter().enumerate() {
let is_active = idx == active_tab;
let name = crate::ui::text_util::elide(&tab.tab_display_name(), 24);
- let label = if tab.dirty {
- format!("● {}", name)
+ let title_inner: Element<'_, Message> = if tab.dirty {
+ row![
+ crate::ui::icons::tinted(
+ crate::ui::icons::DOT,
+ 7.0,
+ Color {
+ r: 0.90,
+ g: 0.75,
+ b: 0.30,
+ a: 1.0,
+ },
+ ),
+ text(name).size(12),
+ ]
+ .spacing(5)
+ .align_y(iced::Center)
+ .into()
} else {
- name
+ text(name).size(12).into()
};
- let title_btn = button(text(label).size(12))
+ let title_btn = button(title_inner)
.on_press(Message::TabSwitch(idx))
.padding([5, 12])
.style(move |_: &Theme, status| button::Style {
@@ -1888,12 +1923,16 @@ pub(super) fn doc_tab_bar<'a>(tabs: &'a [DocumentTab], active_tab: usize) -> Ele
let row_inner: Row<'_, Message> = if tab.is_start {
row![title_btn].spacing(0).align_y(iced::Center)
} else {
- let close_btn = button(text("×").size(11).color(Color {
- r: 0.55,
- g: 0.55,
- b: 0.55,
- a: 1.0,
- }))
+ let close_btn = button(crate::ui::icons::tinted(
+ crate::ui::icons::CLOSE,
+ 10.0,
+ Color {
+ r: 0.55,
+ g: 0.55,
+ b: 0.55,
+ a: 1.0,
+ },
+ ))
.on_press(Message::TabClose(idx))
.padding([3, 5])
.style(move |_: &Theme, status| button::Style {
@@ -2825,12 +2864,35 @@ fn viewport_context_menu_overlay(
Message::Command("COPY".to_string()),
));
items.push(sep());
- let arrow = if draworder_open {
- "Draw Order \u{25be}"
+ let do_caret = if draworder_open {
+ crate::ui::icons::arrow_down(9.0, TEXT_COL)
} else {
- "Draw Order \u{25b8}"
+ crate::ui::icons::arrow_right(9.0, TEXT_COL)
};
- items.push(item(arrow.to_string(), Message::DrawOrderSubmenuToggle));
+ items.push(
+ button(
+ row![
+ text("Draw Order").size(12).color(TEXT_COL),
+ iced::widget::Space::new().width(Fill),
+ do_caret,
+ ]
+ .align_y(iced::Center),
+ )
+ .on_press(Message::DrawOrderSubmenuToggle)
+ .style(|_: &Theme, status| button::Style {
+ background: Some(Background::Color(match status {
+ button::Status::Hovered | button::Status::Pressed => ITEM_HOVER,
+ _ => Color::TRANSPARENT,
+ })),
+ text_color: TEXT_COL,
+ border: Border::default(),
+ shadow: iced::Shadow::default(),
+ snap: false,
+ })
+ .padding([4, 12])
+ .width(Fill)
+ .into(),
+ );
if draworder_open {
items.push(subitem(
"Bring to Front".to_string(),
@@ -3353,7 +3415,7 @@ fn save_as_dialog_window<'a>(
let path_bar = row![
{
let up_msg = up_path.map(Message::SaveDialogNavigate);
- let b = button(text("↑").size(14).color(TEXT))
+ let b = button(crate::ui::icons::tinted(crate::ui::icons::UP, 14.0, TEXT))
.style(|_: &Theme, st| button::Style {
background: Some(Background::Color(
if matches!(st, button::Status::Hovered | button::Status::Pressed) {
@@ -3398,14 +3460,18 @@ fn save_as_dialog_window<'a>(
let rows: Vec> = entries
.iter()
.map(|(name, is_dir, path)| {
- let icon = if *is_dir { "📁" } else { "📄" };
+ let icon_bytes = if *is_dir {
+ crate::ui::icons::FOLDER
+ } else {
+ crate::ui::icons::DOC
+ };
let color = if *is_dir { DIR_COL } else { FILE_COL };
let p = path.clone();
let d = *is_dir;
mouse_area(
container(
row![
- text(icon).size(13),
+ crate::ui::icons::tinted(icon_bytes, 13.0, color),
Space::new().width(6),
text(crate::ui::text_util::elide(name.as_str(), 48))
.size(13)
@@ -3792,9 +3858,10 @@ pub(super) fn start_page_view<'a>() -> Element<'a, Message> {
let donate_btn = {
button(
row![
- text("♥ ").size(15).color(Color::WHITE),
+ crate::ui::icons::tinted(crate::ui::icons::HEART, 14.0, Color::WHITE),
text("Donate").size(14).color(Color::WHITE),
]
+ .spacing(5)
.align_y(iced::Center),
)
.on_press(Message::RibbonToolClick {
@@ -4097,7 +4164,7 @@ pub(super) fn recent_files_panel<'a>(recents: &'a [std::path::PathBuf]) -> Eleme
});
let path_for_remove = path.clone();
- let remove_btn = button(text("×").size(12).color(MUTED))
+ let remove_btn = button(crate::ui::icons::tinted(crate::ui::icons::CLOSE, 11.0, MUTED))
.on_press(Message::RecentRemove(path_for_remove))
.padding([4, 8])
.style(|_: &Theme, status| button::Style {
diff --git a/src/modules/annotate/mod.rs b/src/modules/annotate/mod.rs
index 0ec5b05b2..e303ff3de 100644
--- a/src/modules/annotate/mod.rs
+++ b/src/modules/annotate/mod.rs
@@ -193,25 +193,33 @@ impl CadModule for AnnotateModule {
RibbonItem::Tool(crate::modules::ToolDef {
id: "ANNOSCALE",
label: "Scale List",
- icon: crate::modules::IconKind::Glyph("⬡"),
+ 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::Glyph("⊕"),
+ 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::Glyph("≡"),
+ 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::Glyph("⇄"),
+ icon: crate::modules::IconKind::Svg(include_bytes!(
+ "../../../assets/icons/sync.svg"
+ )),
event: crate::modules::ModuleEvent::Command("SYNCPVIEWPORTS".to_string()),
}),
],
diff --git a/src/modules/draw/layers/color.rs b/src/modules/draw/layers/color.rs
index 49e3dec9f..6ca5bdd7e 100644
--- a/src/modules/draw/layers/color.rs
+++ b/src/modules/draw/layers/color.rs
@@ -7,7 +7,7 @@ pub fn tool() -> ToolDef {
ToolDef {
id: "COLOR",
label: "Color",
- icon: IconKind::Glyph("🎨"),
+ icon: IconKind::Svg(include_bytes!("../../../../assets/icons/color_palette.svg")),
event: ModuleEvent::Command("COLOR".to_string()),
}
}
diff --git a/src/modules/draw/properties/bylayer.rs b/src/modules/draw/properties/bylayer.rs
index b7f5cf9b9..fe9173d6d 100644
--- a/src/modules/draw/properties/bylayer.rs
+++ b/src/modules/draw/properties/bylayer.rs
@@ -6,7 +6,7 @@ pub fn tool() -> ToolDef {
ToolDef {
id: "BYLAYER",
label: "ByLayer",
- icon: IconKind::Glyph("⊟"),
+ icon: IconKind::Svg(include_bytes!("../../../../assets/icons/bylayer.svg")),
event: ModuleEvent::Command("BYLAYER".to_string()),
}
}
diff --git a/src/modules/insert/box_prim.rs b/src/modules/insert/box_prim.rs
index 5c52306b4..3e88a33d2 100644
--- a/src/modules/insert/box_prim.rs
+++ b/src/modules/insert/box_prim.rs
@@ -3,7 +3,7 @@ pub fn tool() -> ToolDef {
ToolDef {
id: "BOX",
label: "Box",
- icon: IconKind::Glyph("⬜"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/box3d.svg")),
event: ModuleEvent::Command("BOX".to_string()),
}
}
diff --git a/src/modules/insert/clear.rs b/src/modules/insert/clear.rs
index ddd9c7203..27af3f043 100644
--- a/src/modules/insert/clear.rs
+++ b/src/modules/insert/clear.rs
@@ -3,7 +3,7 @@ pub fn tool() -> ToolDef {
ToolDef {
id: "CLEAR",
label: "Clear",
- icon: IconKind::Glyph("🗑"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/clear_tool.svg")),
event: ModuleEvent::ClearModels,
}
}
diff --git a/src/modules/insert/cylinder.rs b/src/modules/insert/cylinder.rs
index 6bd6904b8..dcd3350df 100644
--- a/src/modules/insert/cylinder.rs
+++ b/src/modules/insert/cylinder.rs
@@ -3,7 +3,7 @@ pub fn tool() -> ToolDef {
ToolDef {
id: "CYLINDER",
label: "Cylinder",
- icon: IconKind::Glyph("⬡"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/cylinder3d.svg")),
event: ModuleEvent::Command("CYLINDER".to_string()),
}
}
diff --git a/src/modules/insert/open_obj.rs b/src/modules/insert/open_obj.rs
index b6ec378fa..e82c4a556 100644
--- a/src/modules/insert/open_obj.rs
+++ b/src/modules/insert/open_obj.rs
@@ -3,7 +3,7 @@ pub fn tool() -> ToolDef {
ToolDef {
id: "OPEN",
label: "Open",
- icon: IconKind::Glyph("📂"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/import_obj.svg")),
event: ModuleEvent::OpenFileDialog,
}
}
diff --git a/src/modules/insert/sphere.rs b/src/modules/insert/sphere.rs
index 889ced35b..c17a9420b 100644
--- a/src/modules/insert/sphere.rs
+++ b/src/modules/insert/sphere.rs
@@ -3,7 +3,7 @@ pub fn tool() -> ToolDef {
ToolDef {
id: "SPHERE",
label: "Sphere",
- icon: IconKind::Glyph("⬤"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/sphere3d.svg")),
event: ModuleEvent::Command("SPHERE".to_string()),
}
}
diff --git a/src/modules/layout/mod.rs b/src/modules/layout/mod.rs
index b126ff1bd..78a5a63c9 100644
--- a/src/modules/layout/mod.rs
+++ b/src/modules/layout/mod.rs
@@ -14,13 +14,13 @@ pub fn paper_space_tools() -> Vec {
ToolDef {
id: "PAGESETUP",
label: "Page Setup",
- icon: IconKind::Glyph("📋"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/pagesetup.svg")),
event: ModuleEvent::Command("PAGESETUP".to_string()),
},
ToolDef {
id: "PLOT",
label: "Export PDF",
- icon: IconKind::Glyph("🖨"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/plot.svg")),
event: ModuleEvent::Command("PLOT".to_string()),
},
]
@@ -48,14 +48,14 @@ impl CadModule for LayoutModule {
ToolDef {
id: "PAGESETUP",
label: "Page Setup",
- icon: IconKind::Glyph("📋"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/pagesetup.svg")),
event: ModuleEvent::Command("PAGESETUP".to_string()),
}
.into(),
ToolDef {
id: "PLOT",
label: "Export PDF",
- icon: IconKind::Glyph("🖨"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/plot.svg")),
event: ModuleEvent::Command("PLOT".to_string()),
}
.into(),
diff --git a/src/modules/layout/mview.rs b/src/modules/layout/mview.rs
index 399218a62..ea4c844f0 100644
--- a/src/modules/layout/mview.rs
+++ b/src/modules/layout/mview.rs
@@ -19,7 +19,7 @@ pub fn tool() -> ToolDef {
ToolDef {
id: "MVIEW",
label: "Viewport",
- icon: IconKind::Glyph("⬜"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/viewport.svg")),
event: ModuleEvent::Command("MVIEW".to_string()),
}
}
diff --git a/src/modules/manage/mview.rs b/src/modules/manage/mview.rs
index 559136e91..b7df843bd 100644
--- a/src/modules/manage/mview.rs
+++ b/src/modules/manage/mview.rs
@@ -19,7 +19,7 @@ pub fn tool() -> ToolDef {
ToolDef {
id: "MVIEW",
label: "Viewport",
- icon: IconKind::Glyph("⬜"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/viewport.svg")),
event: ModuleEvent::Command("MVIEW".to_string()),
}
}
diff --git a/src/modules/manage/options.rs b/src/modules/manage/options.rs
index de3e535ad..615f31d0a 100644
--- a/src/modules/manage/options.rs
+++ b/src/modules/manage/options.rs
@@ -4,7 +4,7 @@ pub fn tool() -> ToolDef {
ToolDef {
id: "OPTIONS",
label: "Options",
- icon: IconKind::Glyph("⚙"),
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/options_tool.svg")),
event: ModuleEvent::Command("OPTIONS".to_string()),
}
}
diff --git a/src/modules/model/mod.rs b/src/modules/model/mod.rs
index ab079fbad..b30d464f7 100644
--- a/src/modules/model/mod.rs
+++ b/src/modules/model/mod.rs
@@ -12,12 +12,22 @@ use crate::modules::{CadModule, IconKind, ModuleEvent, RibbonGroup, RibbonItem,
pub struct ModelModule;
+const BOX_ICON: &[u8] = include_bytes!("../../../assets/icons/box3d.svg");
+const CYLINDER_ICON: &[u8] = include_bytes!("../../../assets/icons/cylinder3d.svg");
+const CONE_ICON: &[u8] = include_bytes!("../../../assets/icons/cone3d.svg");
+const SPHERE_ICON: &[u8] = include_bytes!("../../../assets/icons/sphere3d.svg");
+const WEDGE_ICON: &[u8] = include_bytes!("../../../assets/icons/wedge3d.svg");
+const TORUS_ICON: &[u8] = include_bytes!("../../../assets/icons/torus3d.svg");
+const UNION_ICON: &[u8] = include_bytes!("../../../assets/icons/union.svg");
+const SUBTRACT_ICON: &[u8] = include_bytes!("../../../assets/icons/subtract.svg");
+const INTERSECT_ICON: &[u8] = include_bytes!("../../../assets/icons/intersect.svg");
+
/// Helper to declare a ribbon tool that fires a named command.
-fn tool(id: &'static str, label: &'static str, glyph: &'static str) -> ToolDef {
+fn tool(id: &'static str, label: &'static str, icon: &'static [u8]) -> ToolDef {
ToolDef {
id,
label,
- icon: IconKind::Glyph(glyph),
+ icon: IconKind::Svg(icon),
event: ModuleEvent::Command(id.to_string()),
}
}
@@ -35,16 +45,16 @@ impl CadModule for ModelModule {
RibbonGroup {
title: "Model",
tools: vec![
- RibbonItem::LargeTool(tool("BOX", "Box", "▦")),
- RibbonItem::LargeTool(tool("CYLINDER", "Cylinder", "⬭")),
- RibbonItem::LargeTool(tool("CONE", "Cone", "△")),
- RibbonItem::LargeTool(tool("SPHERE", "Sphere", "◯")),
+ 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::Glyph("◰"),
+ icon: IconKind::Svg(WEDGE_ICON),
items: vec![
- ("WEDGE", "Wedge", IconKind::Glyph("◣")),
- ("TORUS", "Torus", IconKind::Glyph("◎")),
+ ("WEDGE", "Wedge", IconKind::Svg(WEDGE_ICON)),
+ ("TORUS", "Torus", IconKind::Svg(TORUS_ICON)),
],
default: "WEDGE",
},
@@ -53,9 +63,9 @@ impl CadModule for ModelModule {
RibbonGroup {
title: "Design",
tools: vec![
- RibbonItem::LargeTool(tool("UNION", "Union", "⊕")),
- RibbonItem::LargeTool(tool("SUBTRACT", "Subtract", "⊖")),
- RibbonItem::LargeTool(tool("INTERSECT", "Intersect", "⊗")),
+ RibbonItem::LargeTool(tool("UNION", "Union", UNION_ICON)),
+ RibbonItem::LargeTool(tool("SUBTRACT", "Subtract", SUBTRACT_ICON)),
+ RibbonItem::LargeTool(tool("INTERSECT", "Intersect", INTERSECT_ICON)),
],
},
]
diff --git a/src/ui/app_menu.rs b/src/ui/app_menu.rs
index 4d9f8e560..22840ae87 100644
--- a/src/ui/app_menu.rs
+++ b/src/ui/app_menu.rs
@@ -18,7 +18,7 @@ use crate::app::Message;
// ── Menu item definitions ─────────────────────────────────────────────────
struct MenuItem {
- icon: &'static str,
+ icon: &'static [u8],
label: &'static str,
desc: &'static str,
command: &'static str,
@@ -26,43 +26,43 @@ struct MenuItem {
const MENU_ITEMS: &[MenuItem] = &[
MenuItem {
- icon: "◻",
+ icon: crate::ui::icons::DOC_NEW,
label: "New",
desc: "Create a new drawing",
command: "NEW",
},
MenuItem {
- icon: "📂",
+ icon: crate::ui::icons::FOLDER,
label: "Open",
desc: "Open an existing file",
command: "OPEN",
},
MenuItem {
- icon: "💾",
+ icon: crate::ui::icons::SAVE,
label: "Save",
desc: "Save the current drawing",
command: "SAVE",
},
MenuItem {
- icon: "📄",
+ icon: crate::ui::icons::DOC,
label: "Save As",
desc: "Save as DWG or DXF",
command: "SAVEAS",
},
MenuItem {
- icon: "🖨",
+ icon: crate::ui::icons::PRINT,
label: "Print",
desc: "Print or plot the drawing",
command: "PRINT",
},
MenuItem {
- icon: "⚙",
+ icon: crate::ui::icons::GEAR,
label: "Options",
desc: "Open application settings",
command: "OPTIONS",
},
MenuItem {
- icon: "❓",
+ icon: crate::ui::icons::HELP,
label: "Help",
desc: "Open help documentation",
command: "HELP",
@@ -336,7 +336,7 @@ impl AppMenu {
fn menu_item_btn<'a>(item: &'a MenuItem) -> Element<'a, Message> {
button(
row![
- container(text(item.icon).size(16).color(ICON_COLOR))
+ container(crate::ui::icons::tinted(item.icon, 16.0, ICON_COLOR))
.width(32)
.padding([0, 4]),
column![
@@ -386,7 +386,8 @@ fn recent_item_btn(path: &PathBuf) -> Element<'_, Message> {
let path_str = path.display().to_string();
button(
row![
- container(text("📄").size(13).color(ICON_COLOR)).width(24),
+ container(crate::ui::icons::tinted(crate::ui::icons::DOC, 13.0, ICON_COLOR))
+ .width(24),
column![
text(name).size(11).color(Color::WHITE),
text(type_label).size(9).color(DIM_COLOR),
diff --git a/src/ui/color_select.rs b/src/ui/color_select.rs
index d0859f45d..973335cca 100644
--- a/src/ui/color_select.rs
+++ b/src/ui/color_select.rs
@@ -125,7 +125,7 @@ pub fn color_selector<'a>(
row![
swatch(cur_bg),
text(cur_name).size(11).color(TEXT),
- text(if open { " ▲" } else { " ▾" }).size(9).color(TEXT),
+ crate::ui::icons::arrow_toggle(open, 9.0, TEXT),
]
.spacing(5)
.align_y(iced::Center),
diff --git a/src/ui/command_line.rs b/src/ui/command_line.rs
index 316e8f2b2..812d5697f 100644
--- a/src/ui/command_line.rs
+++ b/src/ui/command_line.rs
@@ -397,13 +397,13 @@ impl CommandLine {
// full backlog (everything pushed since the app started) so the
// user can recover anything that has already faded off the
// overlay.
- let dropdown_label = if self.history_open { "▾" } else { "▸" };
- let dropdown_btn = button(
- text(dropdown_label)
- .size(11)
- .color(PROMPT_COLOR),
- )
- .on_press(Message::CommandHistoryToggle)
+ let dropdown_icon = if self.history_open {
+ crate::ui::icons::arrow_down(11.0, PROMPT_COLOR)
+ } else {
+ crate::ui::icons::arrow_right(11.0, PROMPT_COLOR)
+ };
+ let dropdown_btn = button(dropdown_icon)
+ .on_press(Message::CommandHistoryToggle)
.style(|_: &Theme, _status| button::Style {
background: Some(Background::Color(INPUT_ROW_BG)),
text_color: Color::WHITE,
diff --git a/src/ui/icons.rs b/src/ui/icons.rs
new file mode 100644
index 000000000..fe57bb2ad
--- /dev/null
+++ b/src/ui/icons.rs
@@ -0,0 +1,142 @@
+//! Shared monochrome UI-chrome icons rendered from bundled SVGs.
+//!
+//! Dropdown carets and the undo/redo controls used to be drawn as Unicode
+//! glyphs (`▾`, `▲`, `↶`, `↷`). Those depend on the active text font carrying
+//! the glyph: on desktop the system fallback fonts supply them, but the web
+//! build bundles only Fira Sans, which lacks them, so they rendered as empty
+//! boxes. Drawing them from SVG instead makes the chrome font-independent.
+
+use iced::widget::{container, svg, Space};
+use iced::{Color, Element, Length, Theme};
+
+const TRI_DOWN: &[u8] = include_bytes!("../../assets/icons/ui/tri_down.svg");
+const TRI_UP: &[u8] = include_bytes!("../../assets/icons/ui/tri_up.svg");
+const TRI_RIGHT: &[u8] = include_bytes!("../../assets/icons/ui/tri_right.svg");
+const UNDO: &[u8] = include_bytes!("../../assets/icons/ui/undo.svg");
+const REDO: &[u8] = include_bytes!("../../assets/icons/ui/redo.svg");
+
+const LAY_ON: &[u8] = include_bytes!("../../assets/icons/layers/layon.svg");
+const LAY_OFF: &[u8] = include_bytes!("../../assets/icons/layers/layoff.svg");
+const LAY_FRZ: &[u8] = include_bytes!("../../assets/icons/layers/layfrz.svg");
+const LAY_THW: &[u8] = include_bytes!("../../assets/icons/layers/laythw.svg");
+const LAY_LCK: &[u8] = include_bytes!("../../assets/icons/layers/laylck.svg");
+const LAY_ULK: &[u8] = include_bytes!("../../assets/icons/layers/layulk.svg");
+
+// Monochrome chrome glyphs (replace Unicode glyphs in buttons / menus / toolbars).
+// All are black-on-transparent; recolour them at the call site with [`tinted`].
+pub const CHECK: &[u8] = include_bytes!("../../assets/icons/ui/check.svg");
+pub const CLOSE: &[u8] = include_bytes!("../../assets/icons/ui/close.svg");
+pub const PLUS: &[u8] = include_bytes!("../../assets/icons/ui/plus.svg");
+pub const TRASH: &[u8] = include_bytes!("../../assets/icons/ui/trash.svg");
+pub const MENU: &[u8] = include_bytes!("../../assets/icons/ui/menu.svg");
+pub const BOLT: &[u8] = include_bytes!("../../assets/icons/ui/bolt.svg");
+pub const MOVE: &[u8] = include_bytes!("../../assets/icons/ui/move.svg");
+pub const SPLIT_V: &[u8] = include_bytes!("../../assets/icons/ui/split_v.svg");
+pub const SPLIT_H: &[u8] = include_bytes!("../../assets/icons/ui/split_h.svg");
+pub const UP: &[u8] = include_bytes!("../../assets/icons/ui/up.svg");
+pub const DOC_NEW: &[u8] = include_bytes!("../../assets/icons/ui/doc_new.svg");
+pub const DOC: &[u8] = include_bytes!("../../assets/icons/ui/doc.svg");
+pub const FOLDER: &[u8] = include_bytes!("../../assets/icons/ui/folder.svg");
+pub const SAVE: &[u8] = include_bytes!("../../assets/icons/ui/save.svg");
+pub const PRINT: &[u8] = include_bytes!("../../assets/icons/ui/print.svg");
+pub const GEAR: &[u8] = include_bytes!("../../assets/icons/ui/gear.svg");
+pub const HELP: &[u8] = include_bytes!("../../assets/icons/ui/help.svg");
+pub const HEART: &[u8] = include_bytes!("../../assets/icons/ui/heart.svg");
+pub const DOT: &[u8] = include_bytes!("../../assets/icons/ui/dot.svg");
+pub const TRI_LEFT_B: &[u8] = include_bytes!("../../assets/icons/ui/tri_left.svg");
+pub const ARROW_LONG_RIGHT: &[u8] = include_bytes!("../../assets/icons/ui/arrow_long_right.svg");
+
+/// Render one of the bundled SVGs tinted to `color` at a square `size`.
+pub fn tinted<'a, M: 'a>(bytes: &'static [u8], size: f32, color: Color) -> Element<'a, M> {
+ svg(svg::Handle::from_memory(bytes))
+ .width(size)
+ .height(size)
+ .style(move |_: &Theme, _| svg::Style { color: Some(color) })
+ .into()
+}
+
+/// Backwards-compatible alias used by the caret/undo/redo helpers below.
+fn icon<'a, M: 'a>(bytes: &'static [u8], size: f32, color: Color) -> Element<'a, M> {
+ tinted(bytes, size, color)
+}
+
+/// A fixed-width (14 px) "current row" check column: a green-tintable check
+/// when `active`, otherwise an empty spacer that preserves alignment. Used by
+/// the many dropdown / popup list rows that mark the selected entry.
+pub fn check_cell<'a, M: 'a>(active: bool, color: Color) -> Element<'a, M> {
+ let inner: Element<'a, M> = if active {
+ tinted(CHECK, 11.0, color)
+ } else {
+ Space::new().width(0).into()
+ };
+ container(inner).width(Length::Fixed(14.0)).into()
+}
+
+/// Render a bundled SVG at its native colours (no tint) at a square `size`.
+pub fn raw<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a, M> {
+ svg(svg::Handle::from_memory(bytes))
+ .width(size)
+ .height(size)
+ .into()
+}
+
+/// Layer visibility icon bytes (on / off).
+pub fn layer_visible(visible: bool) -> &'static [u8] {
+ if visible {
+ LAY_ON
+ } else {
+ LAY_OFF
+ }
+}
+
+/// Layer freeze icon bytes (frozen / thawed).
+pub fn layer_freeze(frozen: bool) -> &'static [u8] {
+ if frozen {
+ LAY_FRZ
+ } else {
+ LAY_THW
+ }
+}
+
+/// Layer lock icon bytes (locked / unlocked).
+pub fn layer_lock(locked: bool) -> &'static [u8] {
+ if locked {
+ LAY_LCK
+ } else {
+ LAY_ULK
+ }
+}
+
+/// Downward dropdown caret (replaces `▾`).
+pub fn arrow_down<'a, M: 'a>(size: f32, color: Color) -> Element<'a, M> {
+ icon(TRI_DOWN, size, color)
+}
+
+/// Upward dropdown caret, shown when a dropdown is open (replaces `▲`).
+pub fn arrow_up<'a, M: 'a>(size: f32, color: Color) -> Element<'a, M> {
+ icon(TRI_UP, size, color)
+}
+
+/// Rightward caret for a collapsed item (replaces `▸`).
+pub fn arrow_right<'a, M: 'a>(size: f32, color: Color) -> Element<'a, M> {
+ icon(TRI_RIGHT, size, color)
+}
+
+/// Caret that flips up/down with `open`.
+pub fn arrow_toggle<'a, M: 'a>(open: bool, size: f32, color: Color) -> Element<'a, M> {
+ if open {
+ arrow_up(size, color)
+ } else {
+ arrow_down(size, color)
+ }
+}
+
+/// Undo curved arrow (replaces `↶`).
+pub fn undo<'a, M: 'a>(size: f32, color: Color) -> Element<'a, M> {
+ icon(UNDO, size, color)
+}
+
+/// Redo curved arrow (replaces `↷`).
+pub fn redo<'a, M: 'a>(size: f32, color: Color) -> Element<'a, M> {
+ icon(REDO, size, color)
+}
diff --git a/src/ui/layers.rs b/src/ui/layers.rs
index 06edb6568..0e03c49e9 100644
--- a/src/ui/layers.rs
+++ b/src/ui/layers.rs
@@ -170,9 +170,19 @@ impl LayerPanel {
// ── Toolbar ───────────────────────────────────────────────────────
let toolbar = container(
row![
- toolbar_btn("⊕ New", Message::LayerNew),
- toolbar_btn_cond("⊗ Delete", Message::LayerDelete, has_sel && !sel_is_zero),
- toolbar_btn_cond("✓ Set Current", Message::LayerSetCurrent, has_sel),
+ toolbar_btn(crate::ui::icons::PLUS, "New", Message::LayerNew),
+ toolbar_btn_cond(
+ crate::ui::icons::TRASH,
+ "Delete",
+ Message::LayerDelete,
+ has_sel && !sel_is_zero,
+ ),
+ toolbar_btn_cond(
+ crate::ui::icons::CHECK,
+ "Set Current",
+ Message::LayerSetCurrent,
+ has_sel,
+ ),
]
.spacing(2),
)
@@ -289,9 +299,16 @@ impl LayerPanel {
// ── Toolbar buttons ───────────────────────────────────────────────────────
-fn toolbar_btn(label: &str, msg: Message) -> Element<'_, Message> {
- button(text(label).size(11).color(Color::WHITE))
- .on_press(msg)
+fn toolbar_btn<'a>(icon: &'static [u8], label: &'a str, msg: Message) -> Element<'a, Message> {
+ button(
+ row![
+ crate::ui::icons::tinted(icon, 12.0, Color::WHITE),
+ text(label).size(11).color(Color::WHITE),
+ ]
+ .spacing(5)
+ .align_y(iced::Center),
+ )
+ .on_press(msg)
.style(|_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
button::Status::Hovered => Color {
@@ -325,8 +342,13 @@ fn toolbar_btn(label: &str, msg: Message) -> Element<'_, Message> {
.into()
}
-fn toolbar_btn_cond(label: &str, msg: Message, enabled: bool) -> Element<'_, Message> {
- let mut b = button(text(label).size(11).color(if enabled {
+fn toolbar_btn_cond<'a>(
+ icon: &'static [u8],
+ label: &'a str,
+ msg: Message,
+ enabled: bool,
+) -> Element<'a, Message> {
+ let fg = if enabled {
Color::WHITE
} else {
Color {
@@ -335,7 +357,15 @@ fn toolbar_btn_cond(label: &str, msg: Message, enabled: bool) -> Element<'_, Mes
b: 0.45,
a: 1.0,
}
- }))
+ };
+ let mut b = button(
+ row![
+ crate::ui::icons::tinted(icon, 12.0, fg),
+ text(label).size(11).color(fg),
+ ]
+ .spacing(5)
+ .align_y(iced::Center),
+ )
.style(|_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
button::Status::Hovered => Color {
@@ -431,42 +461,32 @@ fn layer_row<'a>(
.into()
};
- let vis_svg: &'static [u8] = if layer.visible {
- include_bytes!("../../assets/icons/layers/layon.svg")
- } else {
- include_bytes!("../../assets/icons/layers/layoff.svg")
- };
- let frz_svg: &'static [u8] = if layer.frozen {
- include_bytes!("../../assets/icons/layers/layfrz.svg")
- } else {
- include_bytes!("../../assets/icons/layers/laythw.svg")
- };
- let lck_svg: &'static [u8] = if layer.locked {
- include_bytes!("../../assets/icons/layers/laylck.svg")
- } else {
- include_bytes!("../../assets/icons/layers/layulk.svg")
- };
+ let vis_svg = crate::ui::icons::layer_visible(layer.visible);
+ let frz_svg = crate::ui::icons::layer_freeze(layer.frozen);
+ let lck_svg = crate::ui::icons::layer_lock(layer.locked);
let status_dot: Element<'_, Message> = if is_current {
- text("✓")
- .size(13)
- .color(Color {
+ crate::ui::icons::tinted(
+ crate::ui::icons::CHECK,
+ 13.0,
+ Color {
r: 0.25,
g: 0.85,
b: 0.45,
a: 1.0,
- })
- .into()
+ },
+ )
} else {
- text("▣")
- .size(11)
- .color(Color {
+ crate::ui::icons::tinted(
+ crate::ui::icons::DOT,
+ 9.0,
+ Color {
r: 0.55,
g: 0.55,
b: 0.55,
a: 1.0,
- })
- .into()
+ },
+ )
};
// Name cell
@@ -693,11 +713,7 @@ fn layer_row<'a>(
// Per-viewport freeze columns
for (vp_idx, _vp_col) in vp_cols.iter().enumerate() {
let is_vp_frozen = layer.vp_frozen.get(vp_idx).copied().unwrap_or(false);
- let vp_frz_svg: &'static [u8] = if is_vp_frozen {
- include_bytes!("../../assets/icons/layers/layfrz.svg")
- } else {
- include_bytes!("../../assets/icons/layers/laythw.svg")
- };
+ let vp_frz_svg = crate::ui::icons::layer_freeze(is_vp_frozen);
row_content = row_content.push(
container(svg_btn(
vp_frz_svg,
diff --git a/src/ui/layout_manager.rs b/src/ui/layout_manager.rs
index cde6a8248..1c2316aa2 100644
--- a/src/ui/layout_manager.rs
+++ b/src/ui/layout_manager.rs
@@ -194,14 +194,28 @@ pub fn view_window<'a>(
})
.padding([4, 10]),
Space::new().width(Fill),
- button(text("◀ Move Left").size(11))
- .on_press(Message::LayoutManagerMoveLeft)
- .style(btn_s(false))
- .padding([4, 8]),
- button(text("Move Right ▶").size(11))
- .on_press(Message::LayoutManagerMoveRight)
- .style(btn_s(false))
- .padding([4, 8]),
+ button(
+ row![
+ crate::ui::icons::tinted(crate::ui::icons::TRI_LEFT_B, 9.0, Color::WHITE),
+ text("Move Left").size(11),
+ ]
+ .spacing(4)
+ .align_y(iced::Center),
+ )
+ .on_press(Message::LayoutManagerMoveLeft)
+ .style(btn_s(false))
+ .padding([4, 8]),
+ button(
+ row![
+ text("Move Right").size(11),
+ crate::ui::icons::arrow_right(9.0, Color::WHITE),
+ ]
+ .spacing(4)
+ .align_y(iced::Center),
+ )
+ .on_press(Message::LayoutManagerMoveRight)
+ .style(btn_s(false))
+ .padding([4, 8]),
button(text("Set Current").size(11))
.on_press(Message::LayoutManagerSetCurrent)
.style(btn_s(true))
@@ -223,12 +237,17 @@ pub fn view_window<'a>(
.map(|name| {
let is_sel = name.as_str() == selected;
let is_cur = name.as_str() == current.as_str();
- let label = if is_cur {
- format!("{name} ◀")
- } else {
- name.clone()
- };
- button(text(label).size(12))
+ let mut item_row = row![text(name.clone()).size(12)]
+ .spacing(5)
+ .align_y(iced::Center);
+ if is_cur {
+ item_row = item_row.push(crate::ui::icons::tinted(
+ crate::ui::icons::TRI_LEFT_B,
+ 8.0,
+ Color::WHITE,
+ ));
+ }
+ button(item_row)
.on_press(Message::LayoutManagerSelect(name.clone()))
.style(list_item(is_sel))
.padding([5, 10])
@@ -286,7 +305,7 @@ pub fn view_window<'a>(
row![
text("Status:").size(11).color(DIM).width(80),
text(if selected == current.as_str() {
- "Active ◀"
+ "Active"
} else {
"Inactive"
})
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
index d192bcad7..c33041ce6 100644
--- a/src/ui/mod.rs
+++ b/src/ui/mod.rs
@@ -8,6 +8,7 @@ pub mod color_select;
pub mod command_line;
pub mod cycle_popup;
pub mod dimstyle;
+pub mod icons;
pub mod isolate_popup;
pub mod layers;
pub mod layout_manager;
diff --git a/src/ui/modal.rs b/src/ui/modal.rs
index 1f79915eb..897669529 100644
--- a/src/ui/modal.rs
+++ b/src/ui/modal.rs
@@ -6,7 +6,7 @@
//! so both stack dialogs here — one code path for every platform.
use crate::app::Message;
-use iced::widget::{button, column, container, mouse_area, opaque, row, stack, text};
+use iced::widget::{button, column, container, mouse_area, opaque, row, stack};
use iced::{Background, Border, Color, Element, Length, Padding, Theme, Vector};
const PANEL: Color = Color {
@@ -49,17 +49,26 @@ pub fn modal<'a>(
on_close: Message,
offset: Vector,
) -> Element<'a, Message> {
- let close = button(text("✕").size(15))
- .on_press(on_close)
- .padding([1, 7])
- .style(close_style);
+ let close = button(crate::ui::icons::tinted(
+ crate::ui::icons::CLOSE,
+ 13.0,
+ Color {
+ r: 0.85,
+ g: 0.85,
+ b: 0.85,
+ a: 1.0,
+ },
+ ))
+ .on_press(on_close)
+ .padding([1, 7])
+ .style(close_style);
// Draggable title bar: a grip handle next to the ✕. Kept `Shrink` (no
// `Fill`) so a single Fill child can't blow the dialog out to the full
// screen width — the dialog stays sized to its content. Pressing the grip
// starts a drag (handled in `update`).
let grip = mouse_area(
- container(text("✥").size(15).color(GRIP_C))
+ container(crate::ui::icons::tinted(crate::ui::icons::MOVE, 14.0, GRIP_C))
.padding([1, 7])
.style(|_: &Theme| container::Style {
background: Some(Background::Color(TITLE_C)),
diff --git a/src/ui/page_setup.rs b/src/ui/page_setup.rs
index dde0484c7..1ebc4b69b 100644
--- a/src/ui/page_setup.rs
+++ b/src/ui/page_setup.rs
@@ -329,12 +329,12 @@ pub fn view_window<'a>(
hdivider(),
section_label("Position"),
button(
- text(if center {
- "✓ Center on page"
- } else {
- " Center on page"
- })
- .size(11)
+ row![
+ crate::ui::icons::check_cell(center, Color::WHITE),
+ text("Center on page").size(11),
+ ]
+ .spacing(2)
+ .align_y(iced::Center)
)
.on_press(Message::PageSetupCenterToggle)
.style(pill(center))
diff --git a/src/ui/plugin_manager.rs b/src/ui/plugin_manager.rs
index 2bb724e60..bd8e43d97 100644
--- a/src/ui/plugin_manager.rs
+++ b/src/ui/plugin_manager.rs
@@ -199,6 +199,27 @@ fn pill_button<'a>(label: &str, msg: Message, bg: Color) -> Element<'a, Message>
.into()
}
+/// Square icon variant of [`pill_button`] for glyph-free actions (e.g. remove).
+fn pill_icon_button<'a>(icon: &'static [u8], msg: Message, bg: Color) -> Element<'a, Message> {
+ button(crate::ui::icons::tinted(icon, 11.0, WHITE))
+ .padding([5, 9])
+ .on_press(msg)
+ .style(move |_: &Theme, status| {
+ let c = if matches!(status, button::Status::Hovered | button::Status::Pressed) {
+ Color { r: bg.r + 0.08, g: bg.g + 0.08, b: bg.b + 0.08, a: 1.0 }
+ } else {
+ bg
+ };
+ button::Style {
+ background: Some(Background::Color(c)),
+ text_color: WHITE,
+ border: Border { radius: 4.0.into(), ..Default::default() },
+ ..Default::default()
+ }
+ })
+ .into()
+}
+
const GREEN: Color = Color { r: 0.2, g: 0.45, b: 0.28, a: 1.0 };
const RED: Color = Color { r: 0.4, g: 0.25, b: 0.25, a: 1.0 };
@@ -229,7 +250,11 @@ fn install_controls<'a>(
.spacing(4);
if removable {
controls = controls.push(Space::new().width(6));
- controls = controls.push(pill_button("✕", Message::PluginRepoRemove(repo_s), RED));
+ controls = controls.push(pill_icon_button(
+ crate::ui::icons::CLOSE,
+ Message::PluginRepoRemove(repo_s),
+ RED,
+ ));
}
controls.into()
}
diff --git a/src/ui/properties.rs b/src/ui/properties.rs
index 2ac5a78c1..ff936d0e0 100644
--- a/src/ui/properties.rs
+++ b/src/ui/properties.rs
@@ -676,13 +676,14 @@ pub fn color_picker_dropdown<'a>(
// "More Colors…" toggle button
let more_btn = button(
- text(if palette_open {
- "▲ Less"
- } else {
- "▼ More Colors…"
- })
- .size(10)
- .color(HINT_COLOR),
+ row![
+ crate::ui::icons::arrow_toggle(palette_open, 9.0, HINT_COLOR),
+ text(if palette_open { "Less" } else { "More Colors…" })
+ .size(10)
+ .color(HINT_COLOR),
+ ]
+ .spacing(4)
+ .align_y(iced::Center),
)
.on_press(palette_toggle_msg)
.style(|_: &Theme, _| button::Style {
diff --git a/src/ui/ribbon/mod.rs b/src/ui/ribbon/mod.rs
index 452ad8150..2f211f40d 100644
--- a/src/ui/ribbon/mod.rs
+++ b/src/ui/ribbon/mod.rs
@@ -234,20 +234,8 @@ impl Ribbon {
// ── Tab buttons ───────────────────────────────────────────────────
let history_controls = row![
- render_history_control(
- "↶",
- "Undo",
- UNDO_HISTORY_ID,
- undo_count,
- &self.open_dropdown
- ),
- render_history_control(
- "↷",
- "Redo",
- REDO_HISTORY_ID,
- redo_count,
- &self.open_dropdown
- ),
+ render_history_control("Undo", UNDO_HISTORY_ID, undo_count, &self.open_dropdown),
+ render_history_control("Redo", REDO_HISTORY_ID, redo_count, &self.open_dropdown),
]
.spacing(TOP_HIST_GAP)
.align_y(iced::Center);
@@ -609,14 +597,13 @@ impl Ribbon {
.iter()
.map(|(cmd, label, item_icon)| {
let is_current = *cmd == last_cmd;
- let checkmark = text(if is_current { "✓" } else { " " })
- .size(11)
- .color(if is_current {
- CHECK_COLOR
- } else {
- Color::TRANSPARENT
- })
- .width(Length::Fixed(14.0));
+ let checkmark: Element<'_, Message> = container(if is_current {
+ crate::ui::icons::tinted(crate::ui::icons::CHECK, 11.0, CHECK_COLOR)
+ } else {
+ iced::widget::Space::new().width(0).into()
+ })
+ .width(Length::Fixed(14.0))
+ .into();
let icon_el: Element = match *item_icon {
IconKind::Glyph(s) => text(s)
.size(13)
@@ -717,59 +704,16 @@ impl Ribbon {
.width(12)
.height(12);
- let vis = text(if lv { "●" } else { "○" }).size(10).color(if lv {
- Color {
- r: 0.95,
- g: 0.85,
- b: 0.20,
- a: 1.0,
- }
+ let vis = crate::ui::icons::raw(crate::ui::icons::layer_visible(lv), 14.0);
+ let freeze = crate::ui::icons::raw(crate::ui::icons::layer_freeze(lf), 14.0);
+ let lock = crate::ui::icons::raw(crate::ui::icons::layer_lock(ll), 14.0);
+ let checkmark: Element<'_, Message> = container(if is_active {
+ crate::ui::icons::tinted(crate::ui::icons::CHECK, 11.0, CHECK_COLOR)
} else {
- Color {
- r: 0.45,
- g: 0.45,
- b: 0.45,
- a: 1.0,
- }
- });
- let freeze = text("✱").size(10).color(if lf {
- Color {
- r: 0.40,
- g: 0.80,
- b: 1.00,
- a: 1.0,
- }
- } else {
- Color {
- r: 0.95,
- g: 0.85,
- b: 0.20,
- a: 1.0,
- }
- });
- let lock = text(if ll { "🔒" } else { "🔓" }).size(10).color(if ll {
- Color {
- r: 0.95,
- g: 0.70,
- b: 0.20,
- a: 1.0,
- }
- } else {
- Color {
- r: 0.55,
- g: 0.55,
- b: 0.55,
- a: 1.0,
- }
- });
- let checkmark = text(if is_active { "✓" } else { " " })
- .size(11)
- .color(if is_active {
- CHECK_COLOR
- } else {
- Color::TRANSPARENT
- })
- .width(Length::Fixed(14.0));
+ iced::widget::Space::new().width(0).into()
+ })
+ .width(Length::Fixed(14.0))
+ .into();
let label =
text(&info.name)
.size(11)
@@ -893,14 +837,13 @@ impl Ribbon {
.into_iter()
.map(|lt| {
let is_cur = lt.name == *active_lt;
- let check = text(if is_cur { "✓" } else { " " })
- .size(11)
- .color(if is_cur {
- CHECK_COLOR
- } else {
- Color::TRANSPARENT
- })
- .width(Length::Fixed(14.0));
+ let check: Element<'_, Message> = container(if is_cur {
+ crate::ui::icons::tinted(crate::ui::icons::CHECK, 11.0, CHECK_COLOR)
+ } else {
+ iced::widget::Space::new().width(0).into()
+ })
+ .width(Length::Fixed(14.0))
+ .into();
let name_col = text(lt.name.clone())
.size(11)
.color(if is_cur { LABEL_ON } else { LABEL_OFF })
@@ -971,14 +914,13 @@ impl Ribbon {
.map(|item| {
let is_cur = item.0 == active_lw;
let label = item.to_string();
- let check = text(if is_cur { "✓" } else { " " })
- .size(11)
- .color(if is_cur {
- CHECK_COLOR
- } else {
- Color::TRANSPARENT
- })
- .width(Length::Fixed(14.0));
+ let check: Element<'_, Message> = container(if is_cur {
+ crate::ui::icons::tinted(crate::ui::icons::CHECK, 11.0, CHECK_COLOR)
+ } else {
+ iced::widget::Space::new().width(0).into()
+ })
+ .width(Length::Fixed(14.0))
+ .into();
button(
row![
check,
diff --git a/src/ui/ribbon/widgets.rs b/src/ui/ribbon/widgets.rs
index c46a946b0..8a77f07c9 100644
--- a/src/ui/ribbon/widgets.rs
+++ b/src/ui/ribbon/widgets.rs
@@ -11,6 +11,7 @@ use iced::{Background, Border, Color, Element, Fill, Length, Padding, Theme};
use crate::app::Message;
use crate::modules::{IconKind, ModuleEvent, RibbonGroup, RibbonItem, StyleKey, ToolDef};
+use crate::ui::icons;
use crate::ui::properties::{acad_color_display, LwItem};
use super::LayerInfo;
@@ -361,7 +362,7 @@ pub(super) fn render_small<'a>(
let arr_tip = format!("{} options", cur_label);
let arr_btn = button(
- container(text("▾").size(7).color(ARROW_COLOR))
+ container(icons::arrow_down(8.0, ARROW_COLOR))
.width(Fill)
.height(Fill)
.align_x(iced::Center)
@@ -513,7 +514,7 @@ pub(super) fn render_large<'a>(
});
let arr_btn = button(
- container(text("▾").size(9).color(ARROW_COLOR))
+ container(icons::arrow_down(9.0, ARROW_COLOR))
.width(Fill)
.height(Fill)
.align_x(iced::Center)
@@ -562,51 +563,9 @@ pub(super) fn render_large<'a>(
let ll = info.map(|l| l.locked).unwrap_or(false);
let is_open = open_dd.as_deref() == Some(LAYER_COMBO_ID);
- let vis_icon = text(if lv { "●" } else { "○" }).size(10).color(if lv {
- Color {
- r: 0.95,
- g: 0.85,
- b: 0.20,
- a: 1.0,
- }
- } else {
- Color {
- r: 0.45,
- g: 0.45,
- b: 0.45,
- a: 1.0,
- }
- });
- let freeze_icon = text("✱").size(10).color(if lf {
- Color {
- r: 0.40,
- g: 0.80,
- b: 1.00,
- a: 1.0,
- }
- } else {
- Color {
- r: 0.95,
- g: 0.85,
- b: 0.20,
- a: 1.0,
- }
- });
- let lock_icon = text(if ll { "🔒" } else { "🔓" }).size(10).color(if ll {
- Color {
- r: 0.95,
- g: 0.70,
- b: 0.20,
- a: 1.0,
- }
- } else {
- Color {
- r: 0.65,
- g: 0.65,
- b: 0.65,
- a: 1.0,
- }
- });
+ let vis_icon = icons::raw(icons::layer_visible(lv), 14.0);
+ let freeze_icon = icons::raw(icons::layer_freeze(lf), 14.0);
+ let lock_icon = icons::raw(icons::layer_lock(ll), 14.0);
let swatch = container(text(""))
.style(move |_: &Theme| container::Style {
background: Some(Background::Color(lc)),
@@ -625,7 +584,7 @@ pub(super) fn render_large<'a>(
.width(12)
.height(12);
- const ICONS_USED: f32 = 10.0 + 10.0 + 10.0 + 12.0 + 10.0 + 5.0 * 4.0 + 16.0 + 16.0;
+ const ICONS_USED: f32 = 14.0 + 14.0 + 14.0 + 12.0 + 10.0 + 5.0 * 4.0 + 16.0 + 16.0;
let name_w = (COMBO_W - ICONS_USED).max(40.0);
let combo_btn = button(
@@ -637,12 +596,15 @@ pub(super) fn render_large<'a>(
container(text(active_layer).size(11).color(Color::WHITE))
.width(name_w)
.clip(true),
- text("▾").size(9).color(Color {
- r: 0.7,
- g: 0.7,
- b: 0.7,
- a: 1.0
- }),
+ icons::arrow_down(
+ 9.0,
+ Color {
+ r: 0.7,
+ g: 0.7,
+ b: 0.7,
+ a: 1.0,
+ },
+ ),
]
.spacing(4)
.align_y(iced::Center),
@@ -801,12 +763,16 @@ pub(super) fn render_large<'a>(
container(text(label).size(10).color(Color::WHITE))
.width(Fill)
.clip(true),
- text(if is_open { "▲" } else { "▼" }).size(7).color(Color {
- r: 0.6,
- g: 0.6,
- b: 0.6,
- a: 1.0
- }),
+ icons::arrow_toggle(
+ is_open,
+ 8.0,
+ Color {
+ r: 0.6,
+ g: 0.6,
+ b: 0.6,
+ a: 1.0,
+ },
+ ),
]
.spacing(4)
.align_y(iced::Center),
@@ -905,12 +871,16 @@ pub(super) fn render_large<'a>(
container(text(active.clone()).size(11).color(Color::WHITE))
.width(Fill)
.clip(true),
- text(if is_open { "▲" } else { "▾" }).size(9).color(Color {
- r: 0.7,
- g: 0.7,
- b: 0.7,
- a: 1.0
- }),
+ icons::arrow_toggle(
+ is_open,
+ 9.0,
+ Color {
+ r: 0.7,
+ g: 0.7,
+ b: 0.7,
+ a: 1.0,
+ },
+ ),
]
.spacing(4)
.align_y(iced::Center),
@@ -971,18 +941,21 @@ pub(super) fn render_large<'a>(
let key = style_key;
button(
row![
- text(if is_sel { "✓" } else { " " })
- .size(10)
- .color(if is_sel {
+ container(if is_sel {
+ icons::tinted(
+ icons::CHECK,
+ 10.0,
Color {
r: 0.2,
g: 0.8,
b: 0.4,
a: 1.0,
- }
- } else {
- Color::TRANSPARENT
- }),
+ },
+ )
+ } else {
+ iced::widget::Space::new().width(0).into()
+ })
+ .width(12),
text(name).size(11).color(Color::WHITE),
]
.spacing(6)
@@ -1281,7 +1254,6 @@ pub fn module_event_to_message(event: ModuleEvent) -> Message {
// ── History control ────────────────────────────────────────────────────────
pub(super) fn render_history_control<'a>(
- glyph: &'static str,
label: &'static str,
dropdown_id: &'static str,
count: usize,
@@ -1289,12 +1261,20 @@ pub(super) fn render_history_control<'a>(
) -> Element<'a, Message> {
let dd_open = open_dropdown.as_deref() == Some(dropdown_id);
let active = count > 0;
+ let icon_color = if active { Color::WHITE } else { LABEL_OFF };
let main_btn = {
+ let glyph = if dropdown_id == UNDO_HISTORY_ID {
+ icons::undo(15.0, icon_color)
+ } else {
+ icons::redo(15.0, icon_color)
+ };
let btn = button(
- text(glyph)
- .size(14)
- .color(if active { Color::WHITE } else { LABEL_OFF }),
+ container(glyph)
+ .width(Fill)
+ .height(Fill)
+ .align_x(iced::Center)
+ .align_y(iced::Center),
)
.style(move |_: &Theme, status| top_hist_btn_style(active, dd_open, status))
.width(Length::Fixed(TOP_HIST_W))
@@ -1321,11 +1301,10 @@ pub(super) fn render_history_control<'a>(
let arrow_btn = {
let btn = button(
- container(
- text("▾")
- .size(7)
- .color(if active { ARROW_COLOR } else { LABEL_OFF }),
- )
+ container(icons::arrow_down(
+ 8.0,
+ if active { ARROW_COLOR } else { LABEL_OFF },
+ ))
.width(Fill)
.height(Fill)
.align_x(iced::Center)
diff --git a/src/ui/scale_popup.rs b/src/ui/scale_popup.rs
index bfc964f2b..980cb577f 100644
--- a/src/ui/scale_popup.rs
+++ b/src/ui/scale_popup.rs
@@ -70,14 +70,7 @@ pub fn scale_popup_overlay(
}
fn scale_row(label: String, active: bool, msg: Message) -> Element<'static, Message> {
- let check = text(if active { "✓" } else { " " })
- .size(11)
- .color(if active {
- CHECK_COLOR
- } else {
- Color::TRANSPARENT
- })
- .width(Length::Fixed(14.0));
+ let check = crate::ui::icons::check_cell(active, CHECK_COLOR);
let lbl = text(label)
.size(11)
diff --git a/src/ui/selection_filter_popup.rs b/src/ui/selection_filter_popup.rs
index c71819436..f497619ae 100644
--- a/src/ui/selection_filter_popup.rs
+++ b/src/ui/selection_filter_popup.rs
@@ -60,10 +60,7 @@ pub fn selection_filter_popup_overlay(
}
fn type_row(name: String, included: bool) -> Element<'static, Message> {
- let check = text(if included { "✓" } else { " " })
- .size(11)
- .color(if included { CHECK_COLOR } else { Color::TRANSPARENT })
- .width(Length::Fixed(14.0));
+ let check = crate::ui::icons::check_cell(included, CHECK_COLOR);
let lbl = text(name.clone())
.size(11)
diff --git a/src/ui/snap_popup.rs b/src/ui/snap_popup.rs
index a8aef1af2..d048010fa 100644
--- a/src/ui/snap_popup.rs
+++ b/src/ui/snap_popup.rs
@@ -107,14 +107,7 @@ fn snap_row<'a>(
label: &'a str,
active: bool,
) -> Element<'a, Message> {
- let checkmark = text(if active { "✓" } else { " " })
- .size(11)
- .color(if active {
- CHECK_COLOR
- } else {
- Color::TRANSPARENT
- })
- .width(Length::Fixed(14.0));
+ let checkmark = crate::ui::icons::check_cell(active, CHECK_COLOR);
let icon_el = text(icon)
.size(11)
diff --git a/src/ui/statusbar.rs b/src/ui/statusbar.rs
index 2cb4c163b..378fb3c2c 100644
--- a/src/ui/statusbar.rs
+++ b/src/ui/statusbar.rs
@@ -76,7 +76,7 @@ impl StatusBar {
// Which pills the user has chosen to show on the bar.
config: &'a StatusBarConfig,
) -> Element<'a, Message> {
- let menu_btn = button(text("≡").size(14).color(ICON_COLOR))
+ let menu_btn = button(crate::ui::icons::tinted(crate::ui::icons::MENU, 14.0, ICON_COLOR))
.on_press(Message::Command("MENU".into()))
.style(|_: &Theme, _| button::Style {
background: Some(Background::Color(Color::TRANSPARENT)),
@@ -285,7 +285,7 @@ fn format_coords(p: glam::Vec3) -> String {
// ── Customization handle ──────────────────────────────────────────────────
fn customize_btn() -> Element<'static, Message> {
- button(text("≡").size(14).color(ICON_COLOR))
+ button(crate::ui::icons::tinted(crate::ui::icons::MENU, 14.0, ICON_COLOR))
.on_press(Message::ToggleStatusBarMenu)
.style(|_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
@@ -466,8 +466,15 @@ fn osnap_btn(active: bool, snap_enabled: bool, open: bool) -> Element<'static, M
OSNAP_OFF_TEXT
};
- let left = button(text("⚡ OSNAP").size(10).color(text_color))
- .on_press(Message::ToggleSnapEnabled)
+ let left = button(
+ row![
+ crate::ui::icons::tinted(crate::ui::icons::BOLT, 11.0, text_color),
+ text("OSNAP").size(10).color(text_color),
+ ]
+ .spacing(3)
+ .align_y(iced::Center),
+ )
+ .on_press(Message::ToggleSnapEnabled)
.style(move |_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
button::Status::Hovered => {
@@ -495,7 +502,7 @@ fn osnap_btn(active: bool, snap_enabled: bool, open: bool) -> Element<'static, M
})
.padding([2, 6]);
- let right = button(text("▾").size(9).color(text_color))
+ let right = button(crate::ui::icons::arrow_down(9.0, text_color))
.on_press(Message::ToggleSnapPopup)
.style(move |_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
@@ -607,12 +614,16 @@ fn space_tab<'a>(
.padding([2, 6])
.width(Length::Fixed(90.0));
- let cancel_btn = button(text("✕").size(10).color(Color {
- r: 0.65,
- g: 0.65,
- b: 0.65,
- a: 1.0,
- }))
+ let cancel_btn = button(crate::ui::icons::tinted(
+ crate::ui::icons::CLOSE,
+ 10.0,
+ Color {
+ r: 0.65,
+ g: 0.65,
+ b: 0.65,
+ a: 1.0,
+ },
+ ))
.on_press(Message::LayoutRenameCancel)
.style(|_: &Theme, _| button::Style {
background: Some(Background::Color(Color::TRANSPARENT)),
diff --git a/src/ui/statusbar_menu.rs b/src/ui/statusbar_menu.rs
index 3c092d377..720cf2957 100644
--- a/src/ui/statusbar_menu.rs
+++ b/src/ui/statusbar_menu.rs
@@ -52,10 +52,7 @@ pub fn statusbar_menu_overlay(config: &StatusBarConfig) -> Element<'static, Mess
}
fn menu_row(label: &'static str, checked: bool, msg: Message) -> Element<'static, Message> {
- let check = text(if checked { "✓" } else { " " })
- .size(11)
- .color(if checked { CHECK_COLOR } else { Color::TRANSPARENT })
- .width(Length::Fixed(14.0));
+ let check = crate::ui::icons::check_cell(checked, CHECK_COLOR);
let lbl = text(label)
.size(11)
diff --git a/src/ui/style_list.rs b/src/ui/style_list.rs
index c01c95f02..3e0ee8198 100644
--- a/src/ui/style_list.rs
+++ b/src/ui/style_list.rs
@@ -61,12 +61,8 @@ pub fn item<'a>(
} else {
// Fixed-width ✓ column keeps every name left-aligned whether or not the
// row is current.
- let check = if is_current {
- text("✓").size(11).color(CURRENT_CHECK)
- } else {
- text("").size(11)
- };
- let label = row![check.width(14), text(name.to_string()).size(11).color(TEXT)]
+ let check = crate::ui::icons::check_cell(is_current, CURRENT_CHECK);
+ let label = row![check, text(name.to_string()).size(11).color(TEXT)]
.align_y(iced::Center);
let cell = container(label)
.padding([4, 8])
diff --git a/src/ui/units_popup.rs b/src/ui/units_popup.rs
index ea14dce84..fc23d7aac 100644
--- a/src/ui/units_popup.rs
+++ b/src/ui/units_popup.rs
@@ -73,10 +73,7 @@ pub fn units_popup_overlay(current: i16) -> Element<'static, Message> {
}
fn unit_row(label: &'static str, active: bool, msg: Message) -> Element<'static, Message> {
- let check = text(if active { "✓" } else { " " })
- .size(11)
- .color(if active { CHECK_COLOR } else { Color::TRANSPARENT })
- .width(Length::Fixed(14.0));
+ let check = crate::ui::icons::check_cell(active, CHECK_COLOR);
let lbl = text(label)
.size(11)
diff --git a/src/ui/update_notice.rs b/src/ui/update_notice.rs
index 92b360c03..95e845632 100644
--- a/src/ui/update_notice.rs
+++ b/src/ui/update_notice.rs
@@ -109,7 +109,7 @@ fn render_notes_line<'a>(raw: &str) -> Element<'a, Message> {
}
if let Some(rest) = trimmed.strip_prefix("- ").or_else(|| trimmed.strip_prefix("* ")) {
return row![
- text("•").size(11).color(DIM).width(14),
+ container(crate::ui::icons::tinted(crate::ui::icons::DOT, 5.0, DIM)).width(14),
text(strip_inline_md(rest)).size(11).color(WHITE),
]
.spacing(4)
@@ -165,8 +165,12 @@ pub fn view_window<'a>(latest: &'a str, body: &'a str) -> Element<'a, Message> {
false,
);
let latest_card = version_card("Latest", format!("v{}", latest), true);
- let arrow = container(text("→").size(22).color(DIM))
- .width(iced::Length::Fixed(32.0))
+ let arrow = container(crate::ui::icons::tinted(
+ crate::ui::icons::ARROW_LONG_RIGHT,
+ 20.0,
+ DIM,
+ ))
+ .width(iced::Length::Fixed(32.0))
.height(Fill)
.align_x(iced::Center)
.align_y(iced::Center);