Skip to main content

fission_core/ui/widgets/text_input/
config.rs

1use fission_ir::{op::Color as IrColor, AnyRenderObject};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
5pub enum TextAlignVertical {
6    /// Centres single-line fields and top-aligns multiline fields.
7    #[default]
8    Auto,
9    /// Aligns editable content to the top edge.
10    Top,
11    /// Aligns editable content to the vertical centre.
12    Center,
13    /// Aligns editable content to the bottom edge.
14    Bottom,
15}
16
17impl TextAlignVertical {
18    pub(crate) fn resolve(self, multiline: bool) -> Self {
19        match self {
20            Self::Auto if multiline => Self::Top,
21            Self::Auto => Self::Center,
22            explicit => explicit,
23        }
24    }
25
26    pub(crate) fn justify_content(self, multiline: bool) -> fission_ir::op::JustifyContent {
27        match self.resolve(multiline) {
28            Self::Top => fission_ir::op::JustifyContent::Start,
29            Self::Center => fission_ir::op::JustifyContent::Center,
30            Self::Bottom => fission_ir::op::JustifyContent::End,
31            Self::Auto => unreachable!("automatic text alignment must resolve before lowering"),
32        }
33    }
34
35    pub(crate) fn align_items(self, multiline: bool) -> fission_ir::op::AlignItems {
36        match self.resolve(multiline) {
37            Self::Top => fission_ir::op::AlignItems::Start,
38            Self::Center => fission_ir::op::AlignItems::Center,
39            Self::Bottom => fission_ir::op::AlignItems::End,
40            Self::Auto => unreachable!("automatic text alignment must resolve before lowering"),
41        }
42    }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
46pub enum DragStartBehavior {
47    #[default]
48    Start,
49    Down,
50}
51
52pub use fission_ir::semantics::TextWrapMode;
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
55pub enum TextScrollPolicy {
56    #[default]
57    Auto,
58    Always,
59    Never,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
63pub enum TextScrollPhysics {
64    #[default]
65    Platform,
66    Clamped,
67    NeverScrollable,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71pub struct TextValidationResult {
72    pub state: fission_ir::semantics::TextFieldValidationState,
73    pub message: Option<String>,
74}
75
76impl TextValidationResult {
77    pub fn valid() -> Self {
78        Self {
79            state: fission_ir::semantics::TextFieldValidationState::Valid,
80            message: None,
81        }
82    }
83
84    pub fn invalid(message: impl Into<String>) -> Self {
85        Self {
86            state: fission_ir::semantics::TextFieldValidationState::Invalid,
87            message: Some(message.into()),
88        }
89    }
90}
91
92pub trait TextInputValidator: Send + Sync + std::fmt::Debug {
93    fn validate(&self, value: &crate::TextEditingValue) -> TextValidationResult;
94}
95
96pub type SharedTextInputValidator = std::sync::Arc<dyn TextInputValidator>;
97
98#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
99pub struct TextUndoController {
100    pub capacity: usize,
101}
102
103impl Default for TextUndoController {
104    fn default() -> Self {
105        Self { capacity: 100 }
106    }
107}
108
109#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
110pub struct SpellCheckConfiguration {
111    pub enabled: bool,
112    pub underline_color: Option<IrColor>,
113    pub show_suggestions: bool,
114}
115
116impl Default for SpellCheckConfiguration {
117    fn default() -> Self {
118        Self {
119            enabled: true,
120            underline_color: Some(IrColor {
121                r: 255,
122                g: 59,
123                b: 48,
124                a: 255,
125            }),
126            show_suggestions: true,
127        }
128    }
129}
130
131#[doc(hidden)]
132#[derive(Debug, Clone)]
133pub struct TextInputRuntimeConfig {
134    pub drag_start_behavior: DragStartBehavior,
135    pub undo_controller: Option<TextUndoController>,
136    pub restoration_id: Option<String>,
137    pub spell_check_configuration: Option<SpellCheckConfiguration>,
138    pub custom_input_formatters: Vec<crate::SharedTextInputFormatter>,
139    pub select_all_on_focus: bool,
140    pub scroll_policy: TextScrollPolicy,
141    pub scroll_physics: TextScrollPhysics,
142    pub form_id: Option<String>,
143    pub validator: Option<SharedTextInputValidator>,
144}
145
146#[doc(hidden)]
147pub fn downcast_text_input_runtime_config(
148    any: &AnyRenderObject,
149) -> Option<&TextInputRuntimeConfig> {
150    any.downcast_ref::<TextInputRuntimeConfig>()
151}
152
153pub(crate) fn text_input_scroll_physics_for_node(
154    ir: &fission_ir::CoreIR,
155    node_id: fission_ir::WidgetId,
156) -> Option<TextScrollPhysics> {
157    let mut current = Some(node_id);
158    while let Some(id) = current {
159        if let Some(config) = ir
160            .custom_render_objects
161            .get(&id)
162            .and_then(downcast_text_input_runtime_config)
163        {
164            return Some(config.scroll_physics);
165        }
166        current = ir.nodes.get(&id).and_then(|node| node.parent);
167    }
168    None
169}
170
171#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
172pub struct TextSelectionControls {
173    #[serde(default = "default_selection_controls_enabled")]
174    pub enabled: bool,
175    #[serde(default)]
176    pub show_collapsed_handle: bool,
177    pub handle_radius: f32,
178    pub handle_fill: IrColor,
179    pub handle_stroke: Option<IrColor>,
180    pub handle_stroke_width: f32,
181}
182
183fn default_selection_controls_enabled() -> bool {
184    true
185}
186
187impl Default for TextSelectionControls {
188    fn default() -> Self {
189        Self {
190            enabled: true,
191            show_collapsed_handle: false,
192            handle_radius: 7.0,
193            handle_fill: IrColor {
194                r: 0,
195                g: 122,
196                b: 255,
197                a: 255,
198            },
199            handle_stroke: Some(IrColor {
200                r: 255,
201                g: 255,
202                b: 255,
203                a: 255,
204            }),
205            handle_stroke_width: 1.0,
206        }
207    }
208}
209
210#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
211pub struct TextMagnifierConfiguration {
212    pub enabled: bool,
213    pub diameter: f32,
214    pub scale: f32,
215    pub border_radius: f32,
216    pub border_color: Option<IrColor>,
217    pub border_width: f32,
218}
219
220impl Default for TextMagnifierConfiguration {
221    fn default() -> Self {
222        Self {
223            enabled: true,
224            diameter: 84.0,
225            scale: 1.4,
226            border_radius: 18.0,
227            border_color: Some(IrColor {
228                r: 210,
229                g: 214,
230                b: 224,
231                a: 255,
232            }),
233            border_width: 1.0,
234        }
235    }
236}