Skip to main content

teksilo_widgets/styles/
recipe_search_field_style.rs

1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Default `SearchFieldStyle` impl + design tokens.
5//!
6//! Design tokens for `SearchField` live as `pub const`s on this module.
7//! `SearchField` and `SuggestionPanel` read them directly when
8//! building the magnifier glyph, the suggestion-list row chrome, and
9//! the popup padding. The suggestion popup *surface* is routed through
10//! `PopoverStyle::Menu`; only the row chrome + panel padding remain
11//! SearchField-specific.
12
13use teksilo_core::build_context::BuildContext;
14use teksilo_core::styles::{SearchFieldStyle, SearchFieldStyleConfig, SharedSearchFieldStyle};
15use teksilo_core::widget_id::WidgetId;
16
17// ─── IntUI design tokens for SearchField ───────────────────────────
18
19/// Visual size of the magnifier glyph drawn inside the leading slot.
20pub const GLYPH_SIZE: f32 = 14.0;
21/// Reserved width of the leading slot — wider than the glyph so it
22/// doesn't sit flush against the field's leading edge.
23pub const GLYPH_SLOT_WIDTH: f32 = 22.0;
24/// Vertical gap between the input field and the suggestions popup
25/// rendered below it.
26pub const INPUT_PANEL_GAP: f32 = 2.0;
27/// Padding between the popup surface border and the row column.
28pub const PANEL_PADDING: f32 = 4.0;
29/// Suggestion popup outer corner radius.
30pub const PANEL_CORNER_RADIUS: f32 = 6.0;
31/// Per-row hover-highlight corner radius.
32pub const ROW_CORNER_RADIUS: f32 = 2.0;
33pub const ROW_PADDING_HORIZONTAL: f32 = 10.0;
34pub const ROW_PADDING_VERTICAL: f32 = 4.0;
35pub const ROW_HEIGHT: f32 = 26.0;
36
37/// Dimension tokens for `SearchField` bundled into a copyable struct.
38///
39/// `RecipeSearchFieldStyle::default()` fills every field from the
40/// corresponding `pub const` above.  Apps that want a custom size can
41/// construct `SearchFieldRecipe { row_height: 32.0, ..Default::default() }`
42/// and pass it to `RecipeSearchFieldStyle::new(recipe)`.
43#[derive(Debug, Clone, Copy, PartialEq)]
44pub struct SearchFieldRecipe {
45    pub glyph_size: f32,
46    pub glyph_slot_width: f32,
47    pub input_panel_gap: f32,
48    pub panel_padding: f32,
49    pub panel_corner_radius: f32,
50    pub row_corner_radius: f32,
51    pub row_padding_horizontal: f32,
52    pub row_padding_vertical: f32,
53    pub row_height: f32,
54}
55
56impl Default for SearchFieldRecipe {
57    fn default() -> Self {
58        Self {
59            glyph_size: GLYPH_SIZE,
60            glyph_slot_width: GLYPH_SLOT_WIDTH,
61            input_panel_gap: INPUT_PANEL_GAP,
62            panel_padding: PANEL_PADDING,
63            panel_corner_radius: PANEL_CORNER_RADIUS,
64            row_corner_radius: ROW_CORNER_RADIUS,
65            row_padding_horizontal: ROW_PADDING_HORIZONTAL,
66            row_padding_vertical: ROW_PADDING_VERTICAL,
67            row_height: ROW_HEIGHT,
68        }
69    }
70}
71
72/// Default `SearchFieldStyle` shipped with Teksilo. Passthrough —
73/// IntUI's search-field chrome lives inside `TextInput`'s leading
74/// slot + clear button, which are already themed.
75#[derive(Debug, Default, Clone, Copy)]
76pub struct RecipeSearchFieldStyle {
77    pub recipe: SearchFieldRecipe,
78}
79
80impl RecipeSearchFieldStyle {
81    pub fn new(recipe: SearchFieldRecipe) -> Self {
82        Self { recipe }
83    }
84}
85
86impl SearchFieldStyle for RecipeSearchFieldStyle {
87    fn make_body(&self, cfg: &SearchFieldStyleConfig, _ctx: &mut BuildContext) -> WidgetId {
88        cfg.body
89    }
90}
91
92pub fn resolve_search_field_style(
93    override_: &Option<SharedSearchFieldStyle>,
94    ctx: &BuildContext,
95) -> SharedSearchFieldStyle {
96    if let Some(s) = override_.clone() {
97        return s;
98    }
99    ctx.theme_signal()
100        .get()
101        .style_slots
102        .search_field
103        .clone()
104        .unwrap_or_else(|| {
105            std::rc::Rc::new(RecipeSearchFieldStyle::default()) as SharedSearchFieldStyle
106        })
107}