gpui_component/setting/fields/
mod.rs

1mod bool;
2mod dropdown;
3mod element;
4mod number;
5mod string;
6
7pub(crate) use bool::*;
8pub(crate) use dropdown::*;
9pub(crate) use element::*;
10pub(crate) use number::*;
11pub(crate) use string::*;
12
13pub use element::SettingFieldElement;
14pub use number::NumberFieldOptions;
15
16use gpui::{AnyElement, App, IntoElement, SharedString, StyleRefinement, Styled, Window};
17use std::{any::Any, rc::Rc};
18
19use crate::setting::RenderOptions;
20
21pub(crate) trait SettingFieldRender {
22    #[allow(clippy::too_many_arguments)]
23    fn render(
24        &self,
25        field: Rc<dyn AnySettingField>,
26        options: &RenderOptions,
27        style: &StyleRefinement,
28        window: &mut Window,
29        cx: &mut App,
30    ) -> AnyElement;
31}
32
33pub(crate) fn get_value<T: Clone + 'static>(field: &Rc<dyn AnySettingField>, cx: &mut App) -> T {
34    let setting_field = field
35        .as_any()
36        .downcast_ref::<SettingField<T>>()
37        .expect("Failed to downcast setting field");
38    (setting_field.value)(cx)
39}
40
41pub(crate) fn set_value<T: Clone + 'static>(
42    field: &Rc<dyn AnySettingField>,
43    _cx: &mut App,
44) -> Rc<dyn Fn(T, &mut App)> {
45    let setting_field = field
46        .as_any()
47        .downcast_ref::<SettingField<T>>()
48        .expect("Failed to downcast setting field");
49    setting_field.set_value.clone()
50}
51
52/// The type of setting field to render.
53#[derive(Clone)]
54pub enum SettingFieldType {
55    Switch,
56    Checkbox,
57    NumberInput {
58        options: NumberFieldOptions,
59    },
60    Input,
61    Dropdown {
62        options: Vec<(SharedString, SharedString)>,
63    },
64    Element {
65        element: Rc<dyn SettingFieldElement<Element = AnyElement>>,
66    },
67}
68
69impl SettingFieldType {
70    #[inline]
71    pub(crate) fn is_switch(&self) -> bool {
72        matches!(self, SettingFieldType::Switch)
73    }
74
75    #[inline]
76    pub(crate) fn is_number_input(&self) -> bool {
77        matches!(self, SettingFieldType::NumberInput { .. })
78    }
79
80    #[inline]
81    pub(crate) fn is_input(&self) -> bool {
82        matches!(self, SettingFieldType::Input)
83    }
84
85    #[inline]
86    pub(crate) fn is_dropdown(&self) -> bool {
87        matches!(self, SettingFieldType::Dropdown { .. })
88    }
89
90    #[inline]
91    pub(crate) fn is_element(&self) -> bool {
92        matches!(self, SettingFieldType::Element { .. })
93    }
94
95    #[inline]
96    pub(super) fn dropdown_options(&self) -> Option<&Vec<(SharedString, SharedString)>> {
97        match self {
98            SettingFieldType::Dropdown { options } => Some(options),
99            _ => None,
100        }
101    }
102
103    #[inline]
104    pub(super) fn number_input_options(&self) -> Option<&NumberFieldOptions> {
105        match self {
106            SettingFieldType::NumberInput { options } => Some(options),
107            _ => None,
108        }
109    }
110
111    #[inline]
112    pub(super) fn element(&self) -> Rc<dyn SettingFieldElement<Element = AnyElement>> {
113        match self {
114            SettingFieldType::Element { element } => element.clone(),
115            _ => unreachable!("element_render called on non-element field"),
116        }
117    }
118}
119
120/// A setting field that can get and set a value of type T in the App.
121pub struct SettingField<T> {
122    pub(crate) field_type: SettingFieldType,
123    pub(crate) style: StyleRefinement,
124    /// Function to get the value for this field.
125    pub(crate) value: Rc<dyn Fn(&App) -> T>,
126    /// Function to set the value for this field.
127    pub(crate) set_value: Rc<dyn Fn(T, &mut App)>,
128    pub(crate) default_value: Option<T>,
129}
130
131impl SettingField<bool> {
132    /// Create a new Switch field.
133    pub fn switch<V, S>(value: V, set_value: S) -> Self
134    where
135        V: Fn(&App) -> bool + 'static,
136        S: Fn(bool, &mut App) + 'static,
137    {
138        Self::new(SettingFieldType::Switch, value, set_value)
139    }
140
141    /// Create a new Checkbox field.
142    pub fn checkbox<V, S>(value: V, set_value: S) -> Self
143    where
144        V: Fn(&App) -> bool + 'static,
145        S: Fn(bool, &mut App) + 'static,
146    {
147        Self::new(SettingFieldType::Checkbox, value, set_value)
148    }
149}
150
151impl SettingField<SharedString> {
152    /// Create a new Input field.
153    pub fn input<V, S>(value: V, set_value: S) -> Self
154    where
155        V: Fn(&App) -> SharedString + 'static,
156        S: Fn(SharedString, &mut App) + 'static,
157    {
158        Self::new(SettingFieldType::Input, value, set_value)
159    }
160
161    /// Create a new Dropdown field with the given options.
162    pub fn dropdown<V, S>(
163        options: Vec<(SharedString, SharedString)>,
164        value: V,
165        set_value: S,
166    ) -> Self
167    where
168        V: Fn(&App) -> SharedString + 'static,
169        S: Fn(SharedString, &mut App) + 'static,
170    {
171        Self::new(SettingFieldType::Dropdown { options }, value, set_value)
172    }
173
174    /// Create a new setting field with the given custom element that implements [`SettingFieldElement`] trait.
175    ///
176    /// See also [`SettingField::render`] for simply building with a render closure.
177    pub fn element<E>(element: E) -> Self
178    where
179        E: SettingFieldElement + 'static,
180    {
181        Self::new(
182            SettingFieldType::Element {
183                element: Rc::new(AnySettingFieldElement(element)),
184            },
185            |_| SharedString::default(),
186            |_, _| {},
187        )
188    }
189
190    /// Create a new setting field with the given element render closure.
191    ///
192    /// See also [`SettingField::element`] for building with a custom field for more complex scenarios.
193    pub fn render<E, R>(element_render: R) -> Self
194    where
195        E: IntoElement + 'static,
196        R: Fn(&RenderOptions, &mut Window, &mut App) -> E + 'static,
197    {
198        Self::element(
199            move |options: &RenderOptions, window: &mut Window, cx: &mut App| {
200                (element_render)(options, window, cx).into_any_element()
201            },
202        )
203    }
204}
205
206impl SettingField<f64> {
207    /// Create a new Number Input field with the given options.
208    pub fn number_input<V, S>(options: NumberFieldOptions, value: V, set_value: S) -> Self
209    where
210        V: Fn(&App) -> f64 + 'static,
211        S: Fn(f64, &mut App) + 'static,
212    {
213        Self::new(SettingFieldType::NumberInput { options }, value, set_value)
214    }
215}
216
217impl<T> SettingField<T> {
218    /// Create a new setting field with the given get and set functions.
219    fn new<V, S>(field_type: SettingFieldType, value: V, set_value: S) -> Self
220    where
221        V: Fn(&App) -> T + 'static,
222        S: Fn(T, &mut App) + 'static,
223    {
224        Self {
225            field_type,
226            style: StyleRefinement::default(),
227            value: Rc::new(value),
228            set_value: Rc::new(set_value),
229            default_value: None,
230        }
231    }
232
233    /// Set the default value for this setting field, default is None.
234    ///
235    /// If set, this value can be used to reset the setting to its default state.
236    /// If not set, the setting cannot be reset.
237    pub fn default_value(mut self, default_value: impl Into<T>) -> Self {
238        self.default_value = Some(default_value.into());
239        self
240    }
241}
242
243impl<T> Styled for SettingField<T> {
244    fn style(&mut self) -> &mut StyleRefinement {
245        &mut self.style
246    }
247}
248
249/// A trait for setting fields that allows for dynamic typing.
250pub trait AnySettingField {
251    fn as_any(&self) -> &dyn std::any::Any;
252    fn type_name(&self) -> &'static str;
253    fn type_id(&self) -> std::any::TypeId;
254    fn field_type(&self) -> &SettingFieldType;
255    fn style(&self) -> &StyleRefinement;
256    fn is_resettable(&self, cx: &App) -> bool;
257    fn reset(&self, window: &mut Window, cx: &mut App);
258}
259
260impl<T: Clone + PartialEq + Send + Sync + 'static> AnySettingField for SettingField<T> {
261    fn as_any(&self) -> &dyn Any {
262        self
263    }
264
265    fn type_name(&self) -> &'static str {
266        std::any::type_name::<T>()
267    }
268
269    fn type_id(&self) -> std::any::TypeId {
270        std::any::TypeId::of::<T>()
271    }
272
273    fn field_type(&self) -> &SettingFieldType {
274        &self.field_type
275    }
276
277    fn style(&self) -> &StyleRefinement {
278        &self.style
279    }
280
281    fn is_resettable(&self, cx: &App) -> bool {
282        let Some(default_value) = self.default_value.as_ref() else {
283            return false;
284        };
285
286        &(self.value)(cx) != default_value
287    }
288
289    fn reset(&self, _: &mut Window, cx: &mut App) {
290        let Some(default_value) = self.default_value.as_ref() else {
291            return;
292        };
293
294        (self.set_value)(default_value.clone(), cx)
295    }
296}