119 lines
3.8 KiB
Rust
119 lines
3.8 KiB
Rust
use std::rc::Rc;
|
|
|
|
use gpui::{
|
|
AnyElement, App, AppContext as _, Entity, IntoElement, SharedString, StyleRefinement, Styled,
|
|
Window, prelude::FluentBuilder as _,
|
|
};
|
|
|
|
use crate::{
|
|
AxisExt, Sizable, StyledExt,
|
|
input::{InputState, NumberInput, NumberInputEvent},
|
|
setting::{
|
|
AnySettingField, RenderOptions,
|
|
fields::{SettingFieldRender, get_value, set_value},
|
|
},
|
|
};
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct NumberFieldOptions {
|
|
/// The minimum value for the number input, default is `f64::MIN`.
|
|
pub min: f64,
|
|
/// The maximum value for the number input, default is `f64::MAX`.
|
|
pub max: f64,
|
|
/// The step value for the number input, default is `1.0`.
|
|
pub step: f64,
|
|
}
|
|
|
|
impl Default for NumberFieldOptions {
|
|
fn default() -> Self {
|
|
Self {
|
|
min: f64::MIN,
|
|
max: f64::MAX,
|
|
step: 1.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) struct NumberField {
|
|
options: NumberFieldOptions,
|
|
}
|
|
|
|
impl NumberField {
|
|
pub(crate) fn new(options: Option<&NumberFieldOptions>) -> Self {
|
|
Self {
|
|
options: options.cloned().unwrap_or_default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
struct State {
|
|
input: Entity<InputState>,
|
|
_subscription: gpui::Subscription,
|
|
}
|
|
|
|
impl SettingFieldRender for NumberField {
|
|
fn render(
|
|
&self,
|
|
field: Rc<dyn AnySettingField>,
|
|
options: &RenderOptions,
|
|
style: &StyleRefinement,
|
|
window: &mut Window,
|
|
cx: &mut App,
|
|
) -> AnyElement {
|
|
let value = get_value::<f64>(&field, cx);
|
|
let set_value = set_value::<f64>(&field, cx);
|
|
let num_options = self.options.clone();
|
|
|
|
let state = window
|
|
.use_keyed_state(
|
|
SharedString::from(format!(
|
|
"number-state-{}-{}-{}",
|
|
options.page_ix, options.group_ix, options.item_ix
|
|
)),
|
|
cx,
|
|
|window, cx| {
|
|
let input =
|
|
cx.new(|cx| InputState::new(window, cx).default_value(value.to_string()));
|
|
let _subscription = cx.subscribe_in(&input, window, {
|
|
move |_, input, event: &NumberInputEvent, window, cx| match event {
|
|
NumberInputEvent::Step(action) => input.update(cx, |input, cx| {
|
|
let value = input.value();
|
|
if let Ok(value) = value.parse::<f64>() {
|
|
let new_value =
|
|
if *action == crate::input::StepAction::Increment {
|
|
(value + num_options.step).min(num_options.max)
|
|
} else {
|
|
(value - num_options.step).max(num_options.min)
|
|
};
|
|
set_value(new_value, cx);
|
|
input.set_value(
|
|
SharedString::from(new_value.to_string()),
|
|
window,
|
|
cx,
|
|
);
|
|
}
|
|
}),
|
|
}
|
|
});
|
|
|
|
State {
|
|
input,
|
|
_subscription,
|
|
}
|
|
},
|
|
)
|
|
.read(cx);
|
|
|
|
NumberInput::new(&state.input)
|
|
.with_size(options.size)
|
|
.map(|this| {
|
|
if options.layout.is_horizontal() {
|
|
this.w_32()
|
|
} else {
|
|
this.w_full()
|
|
}
|
|
})
|
|
.refine_style(style)
|
|
.into_any_element()
|
|
}
|
|
}
|