Context menu: trailing check mark, faded icons on disabled rows

Checked rows (Properties, grip Stretch/Copy) drew a leading "✓ " that
pushed their label out of line with the other rows. The check mark now
sits in the trailing column where submenu headers draw their caret.

Disabled Undo/Redo rows had grey text but full-strength icons:
themed_disabled faded the icon through the SVG tint colour's alpha, which
iced ignores (the tint only replaces RGB). Fade via the widget's opacity
instead, which fixes every themed_disabled icon.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KeV3kCDBWCrpYaJEs8CRQU
This commit is contained in:
Claude committed 2026-10-08 18:39:00 +00:00
1 parent 1bf584a1c1
commit 7aa456407b
2 files changed
+23 -11

No files matched your search

+18 -9
View File
@@ -1127,6 +1127,8 @@ const MENU_CURSOR_INSET_X: f32 = 24.0;
/// not the row carries a glyph (object snaps, command icons).
const MENU_GUTTER_W: f32 = 18.0;
const MENU_ICON_SIZE: f32 = 14.0;
/// Trailing check mark of checked rows; sits in the submenu caret column.
const MENU_CHECK_SIZE: f32 = 10.0;
/// The gutter cell: the row's glyph, faded with a disabled row, or empty
/// space of the same width.
@@ -1264,16 +1266,13 @@ pub(super) fn viewport_context_menu_overlay(
position_canvas_overlay_clamped(pos, bottom_inset, offset, panel.into())
}
/// One menu row: label (bold for the default row, "✓ "-prefixed when
/// checked), and the keyword hint right-aligned and dimmed. Disabled rows
/// render without a press handler and with faded text.
/// One menu row: label (bold for the default row), the keyword hint
/// right-aligned and dimmed, and a check mark in the trailing column (where
/// submenu headers draw their caret) when checked, so checked labels stay
/// aligned with the others. Disabled rows render without a press handler and
/// with faded text and icons.
fn context_menu_row(item: &MenuItem, indent: f32, highlighted: bool) -> Element<'static, Message> {
let label = if item.checked {
format!("✓ {}", item.label)
} else {
item.label.clone()
};
let mut label_text = text(label).size(12);
let mut label_text = text(item.label.clone()).size(12);
if item.default {
label_text = label_text.font(iced::Font {
weight: iced::font::Weight::Bold,
@@ -1303,6 +1302,16 @@ fn context_menu_row(item: &MenuItem, indent: f32, highlighted: bool) -> Element<
}),
);
}
if item.checked {
if item.hint.is_none() {
content = content.push(iced::widget::Space::new().width(Fill));
}
content = content.push(if enabled {
crate::ui::icons::themed(crate::ui::icons::CHECK, MENU_CHECK_SIZE)
} else {
crate::ui::icons::themed_disabled(crate::ui::icons::CHECK, MENU_CHECK_SIZE)
});
}
let mut btn = button(content)
.padding(iced::Padding {
top: 3.0,
+5 -2
View File
@@ -509,13 +509,16 @@ pub fn themed_secondary<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'
.into()
}
/// Render disabled chrome with the active Iced theme's text color.
/// Render disabled chrome with the active Iced theme's text color, faded
/// like disabled text. The fade goes through the widget's opacity: an SVG
/// tint only replaces RGB, so alpha on the style colour would be ignored.
pub fn themed_disabled<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a, M> {
svg(themed_handle(bytes))
.width(size)
.height(size)
.opacity(0.42_f32)
.style(|theme: &Theme, _| svg::Style {
color: Some(theme.palette().background.base.text.scale_alpha(0.42)),
color: Some(theme.palette().background.base.text),
})
.into()
}