mod bool; mod dropdown; mod element; mod number; mod string; pub(crate) use bool::*; pub(crate) use dropdown::*; pub(crate) use element::*; pub(crate) use number::*; pub(crate) use string::*; pub use element::SettingFieldElement; pub use number::NumberFieldOptions; use gpui::{AnyElement, App, IntoElement, SharedString, StyleRefinement, Styled, Window}; use std::{any::Any, rc::Rc}; use crate::setting::RenderOptions; pub(crate) trait SettingFieldRender { #[allow(clippy::too_many_arguments)] fn render( &self, field: Rc, options: &RenderOptions, style: &StyleRefinement, window: &mut Window, cx: &mut App, ) -> AnyElement; } pub(crate) fn get_value(field: &Rc, cx: &mut App) -> T { let setting_field = field .as_any() .downcast_ref::>() .expect("Failed to downcast setting field"); (setting_field.value)(cx) } pub(crate) fn set_value( field: &Rc, _cx: &mut App, ) -> Rc { let setting_field = field .as_any() .downcast_ref::>() .expect("Failed to downcast setting field"); setting_field.set_value.clone() } /// The type of setting field to render. #[derive(Clone)] pub enum SettingFieldType { Switch, Checkbox, NumberInput { options: NumberFieldOptions, }, Input, Dropdown { options: Vec<(SharedString, SharedString)>, }, Element { element: Rc>, }, } impl SettingFieldType { #[inline] pub(crate) fn is_switch(&self) -> bool { matches!(self, SettingFieldType::Switch) } #[inline] pub(crate) fn is_number_input(&self) -> bool { matches!(self, SettingFieldType::NumberInput { .. }) } #[inline] pub(crate) fn is_input(&self) -> bool { matches!(self, SettingFieldType::Input) } #[inline] pub(crate) fn is_dropdown(&self) -> bool { matches!(self, SettingFieldType::Dropdown { .. }) } #[inline] pub(crate) fn is_element(&self) -> bool { matches!(self, SettingFieldType::Element { .. }) } #[inline] pub(super) fn dropdown_options(&self) -> Option<&Vec<(SharedString, SharedString)>> { match self { SettingFieldType::Dropdown { options } => Some(options), _ => None, } } #[inline] pub(super) fn number_input_options(&self) -> Option<&NumberFieldOptions> { match self { SettingFieldType::NumberInput { options } => Some(options), _ => None, } } #[inline] pub(super) fn element(&self) -> Rc> { match self { SettingFieldType::Element { element } => element.clone(), _ => unreachable!("element_render called on non-element field"), } } } /// A setting field that can get and set a value of type T in the App. pub struct SettingField { pub(crate) field_type: SettingFieldType, pub(crate) style: StyleRefinement, /// Function to get the value for this field. pub(crate) value: Rc T>, /// Function to set the value for this field. pub(crate) set_value: Rc, pub(crate) default_value: Option, } impl SettingField { /// Create a new Switch field. pub fn switch(value: V, set_value: S) -> Self where V: Fn(&App) -> bool + 'static, S: Fn(bool, &mut App) + 'static, { Self::new(SettingFieldType::Switch, value, set_value) } /// Create a new Checkbox field. pub fn checkbox(value: V, set_value: S) -> Self where V: Fn(&App) -> bool + 'static, S: Fn(bool, &mut App) + 'static, { Self::new(SettingFieldType::Checkbox, value, set_value) } } impl SettingField { /// Create a new Input field. pub fn input(value: V, set_value: S) -> Self where V: Fn(&App) -> SharedString + 'static, S: Fn(SharedString, &mut App) + 'static, { Self::new(SettingFieldType::Input, value, set_value) } /// Create a new Dropdown field with the given options. pub fn dropdown( options: Vec<(SharedString, SharedString)>, value: V, set_value: S, ) -> Self where V: Fn(&App) -> SharedString + 'static, S: Fn(SharedString, &mut App) + 'static, { Self::new(SettingFieldType::Dropdown { options }, value, set_value) } /// Create a new setting field with the given custom element that implements [`SettingFieldElement`] trait. /// /// See also [`SettingField::render`] for simply building with a render closure. pub fn element(element: E) -> Self where E: SettingFieldElement + 'static, { Self::new( SettingFieldType::Element { element: Rc::new(AnySettingFieldElement(element)), }, |_| SharedString::default(), |_, _| {}, ) } /// Create a new setting field with the given element render closure. /// /// See also [`SettingField::element`] for building with a custom field for more complex scenarios. pub fn render(element_render: R) -> Self where E: IntoElement + 'static, R: Fn(&RenderOptions, &mut Window, &mut App) -> E + 'static, { Self::element( move |options: &RenderOptions, window: &mut Window, cx: &mut App| { (element_render)(options, window, cx).into_any_element() }, ) } } impl SettingField { /// Create a new Number Input field with the given options. pub fn number_input(options: NumberFieldOptions, value: V, set_value: S) -> Self where V: Fn(&App) -> f64 + 'static, S: Fn(f64, &mut App) + 'static, { Self::new(SettingFieldType::NumberInput { options }, value, set_value) } } impl SettingField { /// Create a new setting field with the given get and set functions. fn new(field_type: SettingFieldType, value: V, set_value: S) -> Self where V: Fn(&App) -> T + 'static, S: Fn(T, &mut App) + 'static, { Self { field_type, style: StyleRefinement::default(), value: Rc::new(value), set_value: Rc::new(set_value), default_value: None, } } /// Set the default value for this setting field, default is None. /// /// If set, this value can be used to reset the setting to its default state. /// If not set, the setting cannot be reset. pub fn default_value(mut self, default_value: impl Into) -> Self { self.default_value = Some(default_value.into()); self } } impl Styled for SettingField { fn style(&mut self) -> &mut StyleRefinement { &mut self.style } } /// A trait for setting fields that allows for dynamic typing. pub trait AnySettingField { fn as_any(&self) -> &dyn std::any::Any; fn type_name(&self) -> &'static str; fn type_id(&self) -> std::any::TypeId; fn field_type(&self) -> &SettingFieldType; fn style(&self) -> &StyleRefinement; fn is_resettable(&self, cx: &App) -> bool; fn reset(&self, window: &mut Window, cx: &mut App); } impl AnySettingField for SettingField { fn as_any(&self) -> &dyn Any { self } fn type_name(&self) -> &'static str { std::any::type_name::() } fn type_id(&self) -> std::any::TypeId { std::any::TypeId::of::() } fn field_type(&self) -> &SettingFieldType { &self.field_type } fn style(&self) -> &StyleRefinement { &self.style } fn is_resettable(&self, cx: &App) -> bool { let Some(default_value) = self.default_value.as_ref() else { return false; }; &(self.value)(cx) != default_value } fn reset(&self, _: &mut Window, cx: &mut App) { let Some(default_value) = self.default_value.as_ref() else { return; }; (self.set_value)(default_value.clone(), cx) } }