feat(ui): add configurable Iced themes

This commit is contained in:
Hakan Seven committed 2026-07-28 09:33:12 +03:00
1 parent 73545520c8
commit 4bf14baea4
52 files changed
+3063 -5670

No files matched your search

+136 -408
View File
@@ -120,22 +120,13 @@ impl StatusBar {
// Leftmost hamburger: opens a dropdown listing Model + every layout, so
// a layout can be picked directly even when the tab strip is scrolled.
let menu_button = button(crate::ui::icons::tinted(
crate::ui::icons::MENU,
16.0,
if is_start { DISABLED_COLOR } else { ICON_COLOR },
))
.style(|_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
button::Status::Hovered => PILL_BG,
_ => Color::TRANSPARENT,
})),
border: Border {
radius: 3.0.into(),
..Default::default()
},
..Default::default()
})
let menu_icon = if is_start {
crate::ui::icons::themed_disabled(crate::ui::icons::MENU, 16.0)
} else {
crate::ui::icons::themed_secondary(crate::ui::icons::MENU, 16.0)
};
let menu_button = button(menu_icon)
.style(button::subtle)
.padding([4, 8]);
let menu_btn = if is_start {
tip(
@@ -156,15 +147,8 @@ impl StatusBar {
)
};
let add_button = button(text("+").size(12).color(if is_start {
DISABLED_COLOR
} else {
ICON_COLOR
}))
.style(|_: &Theme, _| button::Style {
background: Some(Background::Color(Color::TRANSPARENT)),
..Default::default()
})
let add_button = button(text("+").size(12))
.style(button::subtle)
.padding([4, 8]);
let add_btn = if is_start {
tip(
@@ -453,14 +437,17 @@ impl StatusBar {
.justify_end(true);
container(wrap)
.style(|_: &Theme| container::Style {
background: Some(Background::Color(BAR_BG)),
.style(|theme: &Theme| {
let palette = theme.extended_palette();
container::Style {
background: Some(Background::Color(palette.background.base.color)),
border: Border {
color: BORDER_COLOR,
color: palette.background.neutral.color,
width: 1.0,
radius: 0.0.into(),
},
..Default::default()
}
})
.width(Length::Fill)
// One row matches the drawing (document) tab bar height so the three
@@ -501,19 +488,9 @@ fn format_coords(cursor: glam::DVec3, last: Option<glam::DVec3>, mode: i16, pick
// ── Customization handle ──────────────────────────────────────────────────
fn customize_btn() -> Element<'static, Message> {
button(crate::ui::icons::tinted(crate::ui::icons::MENU, 16.0, ICON_COLOR))
button(crate::ui::icons::themed_secondary(crate::ui::icons::MENU, 16.0))
.on_press(Message::StatusMenuTooltipHidden(true))
.style(|_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
button::Status::Hovered => PILL_BG,
_ => Color::TRANSPARENT,
})),
border: Border {
radius: 3.0.into(),
..Default::default()
},
..Default::default()
})
.style(button::subtle)
.padding([4, 8])
.into()
}
@@ -521,7 +498,7 @@ fn customize_btn() -> Element<'static, Message> {
// ── Tooltip helper ────────────────────────────────────────────────────────
fn tip<'a>(content: Element<'a, Message>, label: &'static str) -> Element<'a, Message> {
tip_node(content, text(label).size(11).color(Color::WHITE).into())
tip_node(content, text(label).size(11).into())
}
/// A menu root shows its tooltip until clicked. Moving from the root into the
@@ -549,26 +526,7 @@ fn tip_node<'a>(content: Element<'a, Message>, body: Element<'a, Message>) -> El
tooltip(
content,
container(body)
.style(|_: &Theme| container::Style {
background: Some(Background::Color(Color {
r: 0.13,
g: 0.13,
b: 0.13,
a: 0.97,
})),
border: Border {
color: Color {
r: 0.35,
g: 0.35,
b: 0.35,
a: 1.0,
},
width: 1.0,
radius: 3.0.into(),
},
text_color: Some(Color::WHITE),
..Default::default()
})
.style(container::bordered_box)
.padding([4, 8]),
TipPos::Top,
)
@@ -581,24 +539,26 @@ fn tip_node<'a>(content: Element<'a, Message>, body: Element<'a, Message>) -> El
/// text labels were too small to read). The name lives in the tooltip each
/// call site already wraps it with.
fn toggle_pill(icon: &'static [u8], active: bool, msg: Message) -> Element<'static, Message> {
let color = if active { OSNAP_ON_TEXT } else { OSNAP_OFF_TEXT };
button(crate::ui::icons::tinted(icon, 17.0, color))
let icon = if active {
crate::ui::icons::themed_primary(icon, 17.0)
} else {
crate::ui::icons::themed_secondary(icon, 17.0)
};
button(icon)
.on_press(msg)
.style(move |_: &Theme, status| button::Style {
background: Some(Background::Color(match (active, status) {
(true, button::Status::Hovered) => SNAP_ON_HOVER,
(true, _) => SNAP_ON_BG,
(false, button::Status::Hovered) => SNAP_OFF_HOVER,
(false, _) => SNAP_OFF_BG,
})),
border: Border {
color: if active { SNAP_BORDER_ON } else { BORDER_COLOR },
width: 1.0,
radius: 2.0.into(),
},
text_color: color,
shadow: iced::Shadow::default(),
snap: false,
.style(move |theme: &Theme, status| {
let mut style = button::subtle(theme, status);
if active {
let palette = theme.extended_palette();
style.background = Some(Background::Color(match status {
button::Status::Hovered => palette.primary.base.color,
_ => palette.primary.weak.color,
}));
style.text_color = palette.primary.weak.text;
style.border.color = palette.primary.base.color;
style.border.width = 1.0;
}
style
})
.padding([4, 7])
.into()
@@ -611,16 +571,6 @@ fn toggle_pill(icon: &'static [u8], active: bool, msg: Message) -> Element<'stat
// and a dropdown caret, so both halves read as a single integrated control and
// the border / background / caret / sizing live in exactly ONE place.
/// Pill text/icon tint for the lit vs. dim state — shared so `main` (built by
/// the caller) and the caret always match.
fn pill_text_color(active: bool) -> Color {
if active {
OSNAP_ON_TEXT
} else {
OSNAP_OFF_TEXT
}
}
/// Wrap a caller-built, already-click-wired `main` element together with its
/// menu-bearing dropdown caret.
fn split_pill<'a>(
@@ -628,22 +578,26 @@ fn split_pill<'a>(
caret: Element<'a, Message>,
active: bool,
) -> Element<'a, Message> {
let bg = if active { SNAP_ON_BG } else { SNAP_OFF_BG };
let border_color = if active {
SNAP_BORDER_ON
} else {
BORDER_COLOR
};
container(row![main, caret].spacing(3).align_y(iced::Center))
.style(move |_: &Theme| container::Style {
background: Some(Background::Color(bg)),
.style(move |theme: &Theme| {
let palette = theme.extended_palette();
container::Style {
background: Some(Background::Color(if active {
palette.primary.weak.color
} else {
palette.background.weakest.color
})),
border: Border {
color: border_color,
color: if active {
palette.primary.base.color
} else {
palette.background.neutral.color
},
width: 1.0,
radius: 2.0.into(),
},
..Default::default()
}
})
.padding([4, 6])
.into()
@@ -664,7 +618,6 @@ fn polar_pill<'a>(
let tooltip_text = format!(
"Polar Tracking ({angle})\nF10 — left-click on/off\nRight-click cycles · ▾ picks angle",
);
let color = pill_text_color(active);
// Right-click quick-cycles through the same increments the picker lists.
const CYCLE: &[f32] = &[90.0, 45.0, 30.0, 22.5, 18.0, 15.0, 10.0, 5.0, 1.0];
@@ -673,10 +626,24 @@ fn polar_pill<'a>(
None => 45.0,
};
let polar_icon = if active {
crate::ui::icons::themed_primary(crate::ui::icons::ST_POLAR, 17.0)
} else {
crate::ui::icons::themed_secondary(crate::ui::icons::ST_POLAR, 17.0)
};
let main = mouse_area(
row![
crate::ui::icons::tinted(crate::ui::icons::ST_POLAR, 17.0, color),
text(angle).size(11).color(color),
polar_icon,
text(angle).size(11).style(move |theme: &Theme| {
let palette = theme.extended_palette();
text::Style {
color: Some(if active {
palette.primary.base.color
} else {
palette.background.base.text.scale_alpha(0.72)
}),
}
}),
]
.spacing(2)
.align_y(iced::Center),
@@ -685,26 +652,8 @@ fn polar_pill<'a>(
.on_right_press(Message::SetPolarAngle(next_angle));
let main = tooltip(
main,
container(text(tooltip_text).size(11).color(Color::WHITE))
.style(|_: &Theme| container::Style {
background: Some(Background::Color(Color {
r: 0.13,
g: 0.13,
b: 0.13,
a: 0.95,
})),
border: Border {
color: Color {
r: 0.35,
g: 0.35,
b: 0.35,
a: 1.0,
},
width: 1.0,
radius: 3.0.into(),
},
..Default::default()
})
container(text(tooltip_text).size(11))
.style(container::bordered_box)
.padding([4, 8]),
TipPos::Top,
);
@@ -712,7 +661,12 @@ fn polar_pill<'a>(
let caret = status_menu::menu_bar(
menu_tip(
mouse_area(
container(crate::ui::icons::arrow_down(9.0, color)).padding([4, 7]),
container(if active {
crate::ui::icons::themed_primary_arrow_down(9.0)
} else {
crate::ui::icons::themed_secondary_arrow_down(9.0)
})
.padding([4, 7]),
)
.on_press(Message::StatusMenuTooltipHidden(true))
.into(),
@@ -737,12 +691,13 @@ fn osnap_btn<'a>(
entries: Vec<StatusMenuEntry<'a>>,
) -> Element<'a, Message> {
let on = active || snap_enabled;
let snap_icon = if on {
crate::ui::icons::themed_primary(crate::ui::icons::ST_OSNAP, 17.0)
} else {
crate::ui::icons::themed_secondary(crate::ui::icons::ST_OSNAP, 17.0)
};
let main = tip(
mouse_area(crate::ui::icons::tinted(
crate::ui::icons::ST_OSNAP,
17.0,
pill_text_color(on),
))
mouse_area(snap_icon)
.on_press(Message::ToggleSnapEnabled)
.into(),
"Object Snap: toggle on/off\nF3",
@@ -751,7 +706,12 @@ fn osnap_btn<'a>(
let caret = status_menu::menu_bar(
menu_tip(
mouse_area(
container(crate::ui::icons::arrow_down(9.0, pill_text_color(on))).padding([4, 7]),
container(if on {
crate::ui::icons::themed_primary_arrow_down(9.0)
} else {
crate::ui::icons::themed_secondary_arrow_down(9.0)
})
.padding([4, 7]),
)
.on_press(Message::StatusMenuTooltipHidden(true))
.into(),
@@ -768,44 +728,10 @@ fn osnap_btn<'a>(
// ── Helpers ───────────────────────────────────────────────────────────────
fn layout_tab_context_menu(name: String) -> Element<'static, Message> {
const MENU_BG: Color = Color {
r: 0.17,
g: 0.17,
b: 0.17,
a: 1.0,
};
const MENU_BORDER: Color = Color {
r: 0.35,
g: 0.35,
b: 0.35,
a: 1.0,
};
const ITEM_HOVER: Color = Color {
r: 0.25,
g: 0.45,
b: 0.70,
a: 1.0,
};
const TEXT_COLOR: Color = Color {
r: 0.88,
g: 0.88,
b: 0.88,
a: 1.0,
};
let item = |label: &'static str, msg: Message| {
button(text(label).size(12).color(TEXT_COLOR))
button(text(label).size(12))
.on_press(msg)
.style(|_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
button::Status::Hovered | button::Status::Pressed => ITEM_HOVER,
_ => Color::TRANSPARENT,
})),
text_color: TEXT_COLOR,
border: Border::default(),
shadow: iced::Shadow::default(),
snap: false,
})
.style(button::subtle)
.padding([4, 12])
.width(Length::Fill)
};
@@ -818,15 +744,7 @@ fn layout_tab_context_menu(name: String) -> Element<'static, Message> {
.spacing(0)
.width(160),
)
.style(move |_: &Theme| container::Style {
background: Some(Background::Color(MENU_BG)),
border: Border {
color: MENU_BORDER,
width: 1.0,
radius: 4.0.into(),
},
..Default::default()
})
.style(container::bordered_box)
.padding([4, 0])
.into()
}
@@ -843,46 +761,34 @@ fn space_tab<'a>(
enabled: bool,
reorderable_layouts: Arc<[String]>,
) -> Element<'a, Message> {
let bg = move |is_active: bool, hovered: bool| {
if is_active {
TAB_ACTIVE
} else if hovered {
TAB_HOVER
let tab_style = move |theme: &Theme| {
let palette = theme.extended_palette();
let text_color = if !enabled {
palette.background.base.text.scale_alpha(0.42)
} else if is_active {
palette.primary.weak.text
} else {
Color::TRANSPARENT
}
};
let border = Border {
color: if is_active {
ACCENT
} else {
Color::TRANSPARENT
},
width: if is_active { 1.0 } else { 0.0 },
radius: 2.0.into(),
};
let text_color = if !enabled {
DISABLED_COLOR
} else if is_active {
Color::WHITE
} else {
Color {
r: 0.65,
g: 0.65,
b: 0.65,
a: 1.0,
palette.background.base.text.scale_alpha(0.72)
};
container::Style {
background: is_active.then_some(Background::Color(palette.primary.weak.color)),
border: Border {
color: if is_active {
palette.primary.base.color
} else {
Color::TRANSPARENT
},
width: if is_active { 1.0 } else { 0.0 },
radius: 2.0.into(),
},
text_color: Some(text_color),
..Default::default()
}
};
if !enabled {
let display = container(text(label.clone()).size(12).color(text_color))
.style(move |_: &Theme| container::Style {
background: Some(Background::Color(bg(is_active, false))),
border,
..Default::default()
})
let display = container(text(label.clone()).size(12))
.style(tab_style)
.padding([4, 10]);
crate::ui::wrap_bar::PosReport::owned(
format!("SB_LAYOUT_TAB:{label}"),
@@ -899,49 +805,15 @@ fn space_tab<'a>(
.on_input(Message::LayoutRenameEdit)
.on_submit(Message::LayoutRenameCommit)
.size(12)
.style(|_: &Theme, _| text_input::Style {
background: Background::Color(TAB_ACTIVE),
border: Border {
color: ACCENT,
width: 1.0,
radius: 2.0.into(),
},
icon: Color::WHITE,
placeholder: Color {
r: 0.5,
g: 0.5,
b: 0.5,
a: 1.0,
},
value: Color::WHITE,
selection: Color {
r: 0.20,
g: 0.55,
b: 0.90,
a: 0.4,
},
})
.padding([3, 6])
.width(Length::Fixed(90.0));
let cancel_btn = button(crate::ui::icons::tinted(
let cancel_btn = button(crate::ui::icons::themed_secondary(
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)),
border: Border::default(),
shadow: iced::Shadow::default(),
snap: false,
..Default::default()
})
.style(button::subtle)
.padding([4, 4]);
row![input, cancel_btn]
@@ -951,12 +823,8 @@ fn space_tab<'a>(
} else {
// Normal clickable tab — left click switches. Paper-layout tabs are
// wrapped in `ContextMenu`, which owns right-click handling.
let display = container(text(label.clone()).size(12).color(text_color))
.style(move |_: &Theme| container::Style {
background: Some(Background::Color(bg(is_active, false))),
border,
..Default::default()
})
let display = container(text(label.clone()).size(12))
.style(tab_style)
.padding([4, 10]);
let switch_msg = Message::LayoutSwitch(label.clone());
@@ -1006,34 +874,21 @@ fn space_mode_btn(current_layout: &str, in_mspace: bool) -> Element<'static, Mes
("PAPER", false, Some(Message::MspaceCommand))
};
let text_color = if active {
SNAP_BORDER_ON
} else {
OSNAP_OFF_TEXT
};
let bg_normal = if active { SNAP_ON_BG } else { SNAP_OFF_BG };
let bg_hover = if active {
SNAP_ON_HOVER
} else {
SNAP_OFF_HOVER
};
let border_color = if active { SNAP_BORDER_ON } else { BORDER_COLOR };
let clickable = on_press.is_some();
let mut btn = button(text(label).size(12).color(text_color))
.style(move |_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
button::Status::Hovered if clickable => bg_hover,
_ => bg_normal,
})),
border: Border {
color: border_color,
width: 1.0,
radius: 2.0.into(),
},
text_color,
shadow: iced::Shadow::default(),
snap: false,
let mut btn = button(text(label).size(12))
.style(move |theme: &Theme, status| {
let mut style = button::subtle(theme, status);
if active {
let palette = theme.extended_palette();
style.background = Some(Background::Color(match status {
button::Status::Hovered if clickable => palette.primary.base.color,
_ => palette.primary.weak.color,
}));
style.text_color = palette.primary.weak.text;
style.border.color = palette.primary.base.color;
style.border.width = 1.0;
}
style
})
.padding([4, 7]);
@@ -1045,119 +900,12 @@ fn space_mode_btn(current_layout: &str, in_mspace: bool) -> Element<'static, Mes
}
fn status_pill(label: impl Into<String>) -> Element<'static, Message> {
container(text(label.into()).size(12).color(Color {
r: 0.65,
g: 0.65,
b: 0.65,
a: 1.0,
}))
.style(|_: &Theme| container::Style {
background: Some(Background::Color(PILL_BG)),
border: Border {
color: BORDER_COLOR,
width: 1.0,
radius: 2.0.into(),
},
..Default::default()
})
container(text(label.into()).size(12))
.style(container::bordered_box)
.padding([4, 8])
.into()
}
// ── Colours ───────────────────────────────────────────────────────────────
const BAR_BG: Color = Color {
r: 0.14,
g: 0.14,
b: 0.14,
a: 1.0,
};
const TAB_ACTIVE: Color = Color {
r: 0.25,
g: 0.25,
b: 0.25,
a: 1.0,
};
const TAB_HOVER: Color = Color {
r: 0.20,
g: 0.20,
b: 0.20,
a: 1.0,
};
const PILL_BG: Color = Color {
r: 0.19,
g: 0.19,
b: 0.19,
a: 1.0,
};
const BORDER_COLOR: Color = Color {
r: 0.28,
g: 0.28,
b: 0.28,
a: 1.0,
};
const ICON_COLOR: Color = Color {
r: 0.70,
g: 0.70,
b: 0.70,
a: 1.0,
};
const DISABLED_COLOR: Color = Color {
r: 0.35,
g: 0.35,
b: 0.35,
a: 1.0,
};
const ACCENT: Color = Color {
r: 0.20,
g: 0.55,
b: 0.90,
a: 1.0,
};
const OSNAP_ON_TEXT: Color = Color {
r: 0.35,
g: 0.75,
b: 1.00,
a: 1.0,
};
const OSNAP_OFF_TEXT: Color = Color {
r: 0.42,
g: 0.42,
b: 0.42,
a: 1.0,
};
const SNAP_ON_BG: Color = Color {
r: 0.10,
g: 0.20,
b: 0.32,
a: 1.0,
};
const SNAP_ON_HOVER: Color = Color {
r: 0.14,
g: 0.27,
b: 0.42,
a: 1.0,
};
const SNAP_BORDER_ON: Color = Color {
r: 0.20,
g: 0.50,
b: 0.85,
a: 1.0,
};
const SNAP_OFF_BG: Color = Color {
r: 0.17,
g: 0.17,
b: 0.17,
a: 1.0,
};
const SNAP_OFF_HOVER: Color = Color {
r: 0.22,
g: 0.22,
b: 0.22,
a: 1.0,
};
// ── Scale popup button ────────────────────────────────────────────────────
/// Shared visual root for labelled status-bar menus (units, scale, …).
@@ -1167,29 +915,9 @@ fn popup_pill(label: &str) -> Element<'static, Message> {
fn action_pill(label: &str, msg: Message) -> Element<'static, Message> {
let label = label.to_string();
button(
text(label)
.size(12)
.color(OSNAP_OFF_TEXT),
)
button(text(label).size(12))
.on_press(msg)
.style(|_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
button::Status::Hovered => SNAP_ON_HOVER,
_ => SNAP_OFF_BG,
})),
border: Border {
color: match status {
button::Status::Hovered => SNAP_BORDER_ON,
_ => BORDER_COLOR,
},
width: 1.0,
radius: 2.0.into(),
},
text_color: OSNAP_OFF_TEXT,
shadow: iced::Shadow::default(),
snap: false,
})
.style(button::subtle)
.padding([4, 7])
.into()
}
+23 -50
View File
@@ -51,57 +51,30 @@ pub fn menu_bar<'a>(
.safe_bounds_margin(0.0)
.close_on_background_click_global(true)
.draw_path(DrawPath::Backdrop)
.style(|_: &Theme, _| iced_aw::style::menu_bar::Style {
bar_background: Background::Color(Color::TRANSPARENT),
bar_border: Border::default(),
bar_shadow: Shadow::default(),
menu_background: Background::Color(MENU_BG),
menu_border: Border {
color: MENU_BORDER,
width: 1.0,
radius: 3.0.into(),
},
menu_shadow: Shadow {
color: Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.35,
.style(|theme: &Theme, _| {
let palette = theme.extended_palette();
iced_aw::style::menu_bar::Style {
bar_background: Background::Color(Color::TRANSPARENT),
bar_border: Border::default(),
bar_shadow: Shadow::default(),
menu_background: Background::Color(palette.background.weakest.color),
menu_border: Border {
color: palette.background.neutral.color,
width: 1.0,
radius: 3.0.into(),
},
offset: iced::Vector::new(0.0, -2.0),
blur_radius: 6.0,
},
path: Background::Color(ACTIVE_BG),
path_border: Border {
color: ACTIVE_BORDER,
width: 1.0,
radius: 2.0.into(),
},
menu_shadow: Shadow {
color: palette.background.strongest.color.scale_alpha(0.35),
offset: iced::Vector::new(0.0, -2.0),
blur_radius: 6.0,
},
path: Background::Color(palette.primary.weak.color),
path_border: Border {
color: palette.primary.base.color,
width: 1.0,
radius: 2.0.into(),
},
}
})
.into()
}
const MENU_BG: Color = Color {
r: 0.15,
g: 0.15,
b: 0.15,
a: 1.0,
};
const MENU_BORDER: Color = Color {
r: 0.32,
g: 0.32,
b: 0.32,
a: 1.0,
};
const ACTIVE_BG: Color = Color {
r: 0.10,
g: 0.20,
b: 0.32,
a: 1.0,
};
const ACTIVE_BORDER: Color = Color {
r: 0.20,
g: 0.50,
b: 0.85,
a: 1.0,
};
+13 -54
View File
@@ -1,7 +1,7 @@
//! Status-bar customization and layout-list menu entries.
use iced::widget::{button, row, text};
use iced::{Background, Color, Element, Fill, Theme};
use iced::{Background, Element, Fill, Theme};
use crate::app::Message;
use crate::ui::statusbar::statusbar_config::{StatusBarConfig, StatusPill};
@@ -31,23 +31,17 @@ pub fn layout_entries<'a>(
}
fn layout_row<'a>(name: String, is_current: bool) -> Element<'a, Message> {
let lbl = text(name.clone())
.size(11)
.color(if is_current { LABEL_ON } else { LABEL_OFF });
let lbl = text(name.clone()).size(11);
button(row![lbl].align_y(iced::Center))
.on_press(Message::LayoutSwitch(name))
.style(move |_: &Theme, status| button::Style {
background: Some(Background::Color(match (is_current, status) {
(_, button::Status::Hovered) => ROW_HOVER,
(true, _) => Color {
r: 0.18,
g: 0.26,
b: 0.36,
a: 1.0,
},
_ => Color::TRANSPARENT,
})),
..Default::default()
.style(move |theme: &Theme, status| {
let mut style = button::subtle(theme, status);
if is_current && status == button::Status::Active {
let palette = theme.extended_palette();
style.background = Some(Background::Color(palette.primary.weak.color));
style.text_color = palette.primary.weak.text;
}
style
})
.width(Fill)
.padding([4, 12])
@@ -55,51 +49,16 @@ fn layout_row<'a>(name: String, is_current: bool) -> Element<'a, Message> {
}
fn menu_row(label: &'static str, checked: bool, msg: Message) -> Element<'static, Message> {
let check = crate::ui::icons::check_cell(checked, CHECK_COLOR);
let check = crate::ui::icons::themed_check_cell(checked);
let lbl = text(label)
.size(11)
.color(if checked { LABEL_ON } else { LABEL_OFF });
let lbl = text(label).size(11);
let content = row![check, lbl].spacing(6).align_y(iced::Center);
button(content)
.on_press(msg)
.style(|_: &Theme, status| button::Style {
background: Some(Background::Color(match status {
button::Status::Hovered => ROW_HOVER,
_ => Color::TRANSPARENT,
})),
..Default::default()
})
.style(button::subtle)
.width(Fill)
.padding([4, 10])
.into()
}
// ── Colours ───────────────────────────────────────────────────────────────
const ROW_HOVER: Color = Color {
r: 0.22,
g: 0.22,
b: 0.22,
a: 1.0,
};
const CHECK_COLOR: Color = Color {
r: 0.35,
g: 0.75,
b: 1.00,
a: 1.0,
};
const LABEL_ON: Color = Color {
r: 0.92,
g: 0.92,
b: 0.92,
a: 1.0,
};
const LABEL_OFF: Color = Color {
r: 0.65,
g: 0.65,
b: 0.65,
a: 1.0,
};