Skip to main content

teksilo_widgets/
styles.rs

1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Default `Recipe*Style` impls — the IntUI look ships here. Apps that
5//! want a different design language write their own `impl FooStyle`
6//! blocks (per-call via `Foo::style(...)` or theme-wide via
7//! `theme.style_slots`).
8//!
9//! Each impl widget composes a small subtree (or for chrome-dense
10//! widgets like Toggle, a tiny private leaf widget that paints
11//! directly) so the public `Foo` widget itself stays pure
12//! composition over the `FooStyle` trait — no `paint()` method on
13//! any themable widget.
14
15pub mod recipe_avatar_style;
16pub mod recipe_badge_style;
17pub mod recipe_banner_style;
18pub mod recipe_button_style;
19pub mod recipe_calendar_style;
20pub mod recipe_card_style;
21pub mod recipe_checkbox_style;
22pub mod recipe_color_picker_style;
23pub mod recipe_combo_box_style;
24pub mod recipe_date_edit_style;
25pub mod recipe_dialog_style;
26pub mod recipe_drop_target_style;
27pub mod recipe_drop_zone_style;
28pub mod recipe_grid_view_style;
29pub mod recipe_icon_button_style;
30pub mod recipe_link_style;
31pub mod recipe_list_container_style;
32pub mod recipe_menu_item_style;
33pub mod recipe_panel_style;
34pub mod recipe_popover_style;
35pub mod recipe_progress_bar_style;
36pub mod recipe_radio_style;
37pub mod recipe_radio_tile_style;
38pub mod recipe_rich_text_editor_style;
39pub mod recipe_scroll_bar_style;
40pub mod recipe_search_field_style;
41pub mod recipe_segmented_control_style;
42pub mod recipe_slider_style;
43pub mod recipe_snackbar_style;
44pub mod recipe_spin_box_style;
45pub mod recipe_split_button_style;
46pub mod recipe_splitter_style;
47pub mod recipe_standard_item_style;
48pub mod recipe_tab_style;
49pub mod recipe_table_style;
50pub mod recipe_text_input_style;
51pub mod recipe_toast_style;
52pub mod recipe_toggle_style;
53pub mod recipe_tooltip_style;
54
55pub use recipe_avatar_style::{AvatarRecipe, RecipeAvatarStyle};
56pub use recipe_badge_style::{BadgeRecipe, RecipeBadgeStyle};
57pub use recipe_banner_style::{BannerRecipe, RecipeBannerStyle};
58pub use recipe_button_style::RecipeButtonStyle;
59pub use recipe_calendar_style::{CalendarRecipe, RecipeCalendarStyle};
60pub use recipe_card_style::{CardRecipe, RecipeCardStyle};
61pub use recipe_checkbox_style::{CheckboxRecipe, RecipeCheckboxStyle};
62pub use recipe_color_picker_style::{ColorPickerRecipe, RecipeColorPickerStyle};
63pub use recipe_combo_box_style::{ComboBoxRecipe, RecipeComboBoxStyle};
64pub use recipe_date_edit_style::{DateEditRecipe, RecipeDateEditStyle};
65pub use recipe_dialog_style::{DialogRecipe, RecipeDialogStyle};
66pub use recipe_drop_target_style::{DropTargetRecipe, RecipeDropTargetStyle};
67pub use recipe_drop_zone_style::{DropZoneRecipe, RecipeDropZoneStyle};
68pub use recipe_grid_view_style::RecipeGridViewStyle;
69pub use recipe_icon_button_style::{IconButtonRecipe, RecipeIconButtonStyle};
70pub use recipe_link_style::{LinkRecipe, RecipeLinkStyle};
71pub use recipe_list_container_style::RecipeListContainerStyle;
72pub use recipe_menu_item_style::{MenuItemRecipe, RecipeMenuItemStyle};
73pub use recipe_panel_style::{PanelRecipe, RecipePanelStyle};
74pub use recipe_popover_style::{PopoverRecipe, RecipePopoverStyle};
75pub use recipe_progress_bar_style::{ProgressBarRecipe, RecipeProgressBarStyle};
76pub use recipe_radio_style::{RadioRecipe, RecipeRadioStyle};
77pub use recipe_radio_tile_style::{
78    RADIO_TILE_CORNER_RADIUS, RADIO_TILE_VERTICAL_ROW_HEIGHT, RadioTileRecipe, RecipeRadioTileStyle,
79};
80pub use recipe_rich_text_editor_style::RecipeRichTextEditorStyle;
81pub use recipe_scroll_bar_style::{RecipeScrollBarStyle, ScrollBarRecipe};
82pub use recipe_search_field_style::{RecipeSearchFieldStyle, SearchFieldRecipe};
83pub use recipe_segmented_control_style::{RecipeSegmentedControlStyle, SegmentedControlRecipe};
84pub use recipe_slider_style::{RecipeSliderStyle, SliderRecipe};
85pub use recipe_snackbar_style::{RecipeSnackbarStyle, SnackbarRecipe};
86pub use recipe_spin_box_style::RecipeSpinBoxStyle;
87pub use recipe_split_button_style::RecipeSplitButtonStyle;
88pub use recipe_splitter_style::{RecipeSplitterStyle, SplitterRecipe};
89pub use recipe_standard_item_style::{RecipeStandardItemStyle, StandardItemRecipe};
90pub use recipe_tab_style::{RecipeTabStyle, TabRecipe};
91pub use recipe_table_style::{RecipeTableStyle, TableRecipe};
92pub use recipe_text_input_style::{RecipeTextInputStyle, TextInputRecipe};
93pub use recipe_toast_style::{RecipeToastStyle, ToastRecipe};
94pub use recipe_toggle_style::{RecipeToggleStyle, ToggleRecipe};
95pub use recipe_tooltip_style::{RecipeTooltipStyle, TooltipRecipe};
96
97use teksilo_core::build_context::BuildContext;
98use teksilo_core::signal::Signal;
99use teksilo_tokens::ColorTokens;
100
101/// Reactive resolution palette for recipe colours: the live theme palette while
102/// the host window is active, the accent-desaturated projection
103/// ([`ColorTokens::for_inactive_window`]) while it is inactive.
104///
105/// A recipe style that **bakes** a colour into a `ColorProp::Bound(Signal)` /
106/// `PaintProp` at build time (resolving a `RecipeColor` against the theme) must
107/// resolve against this signal rather than a plain `ctx.theme_signal()`. A
108/// `Bound` colour is a concrete value that `ColorProp::resolve` returns verbatim
109/// — it ignores the paint walker's window-inactive theme projection — so accent
110/// chrome baked from `theme_signal` (a Filled button fill, an accent focus
111/// border) would stay vivid in a background window while paint-resolving
112/// controls (Toggle, Checkbox, which read `colors.accent` in `paint()`) greyed
113/// out. Resolving against this greys them out uniformly.
114///
115/// Role-preserving paint paths — passing a `SurfaceRole` / `BorderRole` /
116/// `Signal<Role>` straight to a widget so it resolves against `ctx.theme` at
117/// paint — already follow the projection and need no change.
118pub(crate) fn window_resolution_colors(ctx: &BuildContext) -> Signal<ColorTokens> {
119    let wa = ctx.window_active_signal();
120    ctx.theme_signal().zip(&wa).map(|(theme, active)| {
121        if *active {
122            theme.colors.clone()
123        } else {
124            theme.colors.for_inactive_window()
125        }
126    })
127}