Skip to main content

urushi/theme/
role.rs

1//! Typed roles that map semantic meaning to styles.
2
3use crate::{BlockStyle, TextStyle};
4
5use super::Theme;
6
7/// A reusable text component understood by all urushi consumers.
8///
9/// Every `ComponentRole` resolves to a [`TextStyle`]. Roles whose value is a
10/// rectangle — a panel — are [`PanelRole`] values instead, because geometry
11/// lives on [`BlockStyle`].
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ComponentRole {
14    Body,
15    Muted,
16    Accent,
17    Success,
18    Error,
19    PromptQuestion,
20    PromptAnswer,
21    PromptPlaceholder,
22    PromptCursor,
23    PromptOption,
24    PromptOptionSelected,
25    PromptButton,
26    PromptButtonFocused,
27    PromptHelp,
28    PromptError,
29}
30
31impl ComponentRole {
32    pub(super) const fn index(self) -> usize {
33        match self {
34            Self::Body => 0,
35            Self::Muted => 1,
36            Self::Accent => 2,
37            Self::Success => 3,
38            Self::Error => 4,
39            Self::PromptQuestion => 5,
40            Self::PromptAnswer => 6,
41            Self::PromptPlaceholder => 7,
42            Self::PromptCursor => 8,
43            Self::PromptOption => 9,
44            Self::PromptOptionSelected => 10,
45            Self::PromptButton => 11,
46            Self::PromptButtonFocused => 12,
47            Self::PromptHelp => 13,
48            Self::PromptError => 14,
49        }
50    }
51}
52
53/// A framed surface: a rectangle, not a run of text.
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum PanelRole {
56    Panel,
57    PanelFocused,
58}
59
60impl BlockThemeRole for PanelRole {
61    fn resolve(self, theme: &Theme) -> BlockStyle {
62        match self {
63            Self::Panel => theme.components().get_panel().clone(),
64            Self::PanelFocused => theme.components().get_panel_focused().clone(),
65        }
66    }
67}
68
69/// A semantic style role used by the List component.
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum ListRole {
72    Item,
73    Enumerator,
74}
75
76impl TextThemeRole for ListRole {
77    fn resolve(self, theme: &Theme) -> TextStyle {
78        theme.components().get_list_style(self).clone()
79    }
80}
81
82/// A semantic style role used by the Tree component.
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub enum TreeRole {
85    Root,
86    Item,
87    Connector,
88}
89
90impl TextThemeRole for TreeRole {
91    fn resolve(self, theme: &Theme) -> TextStyle {
92        theme.components().get_tree_style(self).clone()
93    }
94}
95
96/// A semantic style role used by the Scrollbar component.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
98pub enum ScrollbarRole {
99    Thumb,
100    Track,
101    Begin,
102    End,
103}
104
105impl ScrollbarRole {
106    pub(crate) const fn index(self) -> usize {
107        match self {
108            Self::Thumb => 0,
109            Self::Track => 1,
110            Self::Begin => 2,
111            Self::End => 3,
112        }
113    }
114}
115
116impl TextThemeRole for ScrollbarRole {
117    fn resolve(self, theme: &Theme) -> TextStyle {
118        theme.components().get_scrollbar_style(self).clone()
119    }
120}
121
122/// A semantic cell role used by the Table component.
123///
124/// A table cell is a block: it aligns its content inside a column width, which
125/// is geometry. The glyph style of the table's rules is a plain [`TextStyle`],
126/// reachable through
127/// [`TablePresentation::get_border_style`](crate::TablePresentation::get_border_style).
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub enum TableRole {
130    Header,
131    Cell,
132}
133
134impl BlockThemeRole for TableRole {
135    fn resolve(self, theme: &Theme) -> BlockStyle {
136        theme.components().get_table_style(self).clone()
137    }
138}
139
140/// A typed role that resolves a [`TextStyle`] from a [`Theme`].
141///
142/// This is the extension point of the theme system: an application defines its
143/// own role type and derives the style here, so the result keeps following
144/// theme overrides and light/dark selection. A role that needs a parameter
145/// carries it in the role value, and a role that needs data the theme cannot
146/// provide carries a reference to it.
147pub trait TextThemeRole: Copy {
148    fn resolve(self, theme: &Theme) -> TextStyle;
149}
150
151/// A typed role that resolves a [`BlockStyle`] from a [`Theme`].
152///
153/// The geometry-bearing counterpart of [`TextThemeRole`], for roles whose value is
154/// a rectangle rather than a run of text.
155pub trait BlockThemeRole: Copy {
156    fn resolve(self, theme: &Theme) -> BlockStyle;
157}
158
159impl TextThemeRole for ComponentRole {
160    fn resolve(self, theme: &Theme) -> TextStyle {
161        theme.components().get_text_style(self).clone()
162    }
163}