teksilo_core/styles/menu_item_style.rs
1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Tier-3 style protocol for `MenuItem`. See `docs/styling-system.md`.
5
6use std::rc::Rc;
7
8use teksilo_tokens::TextRole;
9
10use crate::build_context::BuildContext;
11use crate::signal::Signal;
12use crate::widget_id::WidgetId;
13
14#[derive(Clone, Debug)]
15pub struct MenuItemStyleConfig {
16 pub label: WidgetId,
17 /// Optional leading slot (icon, checkmark, radio dot).
18 pub leading: Option<WidgetId>,
19 /// Optional trailing slot (shortcut chip, submenu chevron).
20 pub trailing: Option<WidgetId>,
21 pub is_hovered: Signal<bool>,
22 pub is_pressed: Signal<bool>,
23 pub is_focused: Signal<bool>,
24 pub is_disabled: Signal<bool>,
25 /// Bound to keyboard-arrow navigation within the parent menu.
26 pub is_highlighted: Signal<bool>,
27}
28
29pub trait MenuItemStyle: 'static {
30 fn make_body(&self, cfg: &MenuItemStyleConfig, ctx: &mut BuildContext) -> WidgetId;
31
32 /// The text role a menu row's label and shortcut take while the row is
33 /// **highlighted** — hovered, or reached by keyboard navigation.
34 /// `None` (the default) keeps the row's own mapping.
35 ///
36 /// The row builds its label before a style ever sees it, so a style
37 /// that fills the highlight with a saturated colour cannot recolour
38 /// the text on top of it. macOS fills a highlighted menu row with the
39 /// accent and flips its label to `selectedMenuItemTextColor` (white),
40 /// so its style returns [`TextRole::OnAccent`]; IntUI and Fluent both
41 /// use a neutral wash and leave this `None`.
42 ///
43 /// Same shape as
44 /// [`ButtonStyle::label_text_role`](crate::styles::ButtonStyle::label_text_role),
45 /// and defaulted for the same reason: an existing style needs no
46 /// change.
47 fn highlighted_label_role(&self) -> Option<TextRole> {
48 None
49 }
50}
51
52pub type SharedMenuItemStyle = Rc<dyn MenuItemStyle>;