settings ui: Add numeric steppers to settings UI (#39491)

This PR adds the numeric stepper component to the settings ui and
implements some settings that rely on this component as well.

I also switched {buffer/ui}_font_weight to the `gpui::FontWeight` type
and added a manual implementation of the Schemars trait. This allows Zed
to send min, max, and default information to the JSON LSP when a user is
manually editing the settings file.

The numeric stepper elements added to the settings ui are below:
- ui font size
- ui font weight
- Buffer font size
- Buffer font weight 
- Scroll sensitivity
- Fast scroll sensitivity
- Vertical scroll margin
- Horizontal scroll margin
- Inline blame padding 
- Inline blame delay
- Inline blame min column
- Unnecessary code fade
- Tab Size
- Hover popover delay

Release Notes:

- N/A
This commit is contained in:
Anthony Eid
2025-10-06 10:06:33 +00:00
committed by GitHub
parent c26937a848
commit d2b91eb2bc
11 changed files with 501 additions and 137 deletions
+112 -17
View File
@@ -1,13 +1,14 @@
use std::{
fmt::Display,
ops::{Add, Sub},
num::{NonZeroU32, NonZeroU64},
rc::Rc,
str::FromStr,
};
use editor::{Editor, EditorStyle};
use gpui::{ClickEvent, Entity, FocusHandle, Focusable, Modifiers};
use gpui::{ClickEvent, Entity, FocusHandle, Focusable, FontWeight, Modifiers};
use settings::CodeFade;
use ui::{IconButtonShape, prelude::*};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
@@ -25,15 +26,7 @@ pub enum NumericStepperMode {
}
pub trait NumericStepperType:
Display
+ Add<Output = Self>
+ Sub<Output = Self>
+ Copy
+ Clone
+ Sized
+ PartialOrd
+ FromStr
+ 'static
Display + Copy + Clone + Sized + PartialOrd + FromStr + 'static
{
fn default_format(value: &Self) -> String {
format!("{}", value)
@@ -43,6 +36,56 @@ pub trait NumericStepperType:
fn small_step() -> Self;
fn min_value() -> Self;
fn max_value() -> Self;
fn saturating_add(self, rhs: Self) -> Self;
fn saturating_sub(self, rhs: Self) -> Self;
}
impl NumericStepperType for gpui::FontWeight {
fn default_step() -> Self {
FontWeight(10.0)
}
fn large_step() -> Self {
FontWeight(50.0)
}
fn small_step() -> Self {
FontWeight(5.0)
}
fn min_value() -> Self {
gpui::FontWeight::THIN
}
fn max_value() -> Self {
gpui::FontWeight::BLACK
}
fn saturating_add(self, rhs: Self) -> Self {
FontWeight((self.0 + rhs.0).min(Self::max_value().0))
}
fn saturating_sub(self, rhs: Self) -> Self {
FontWeight((self.0 - rhs.0).max(Self::min_value().0))
}
}
impl NumericStepperType for settings::CodeFade {
fn default_step() -> Self {
CodeFade(0.10)
}
fn large_step() -> Self {
CodeFade(0.20)
}
fn small_step() -> Self {
CodeFade(0.05)
}
fn min_value() -> Self {
CodeFade(0.0)
}
fn max_value() -> Self {
CodeFade(0.9)
}
fn saturating_add(self, rhs: Self) -> Self {
CodeFade((self.0 + rhs.0).min(Self::max_value().0))
}
fn saturating_sub(self, rhs: Self) -> Self {
CodeFade((self.0 - rhs.0).max(Self::min_value().0))
}
}
macro_rules! impl_numeric_stepper_int {
@@ -67,6 +110,50 @@ macro_rules! impl_numeric_stepper_int {
fn max_value() -> Self {
<$type>::MAX
}
fn saturating_add(self, rhs: Self) -> Self {
self.saturating_add(rhs)
}
fn saturating_sub(self, rhs: Self) -> Self {
self.saturating_sub(rhs)
}
}
};
}
macro_rules! impl_numeric_stepper_nonzero_int {
($nonzero:ty, $inner:ty) => {
impl NumericStepperType for $nonzero {
fn default_step() -> Self {
<$nonzero>::new(1).unwrap()
}
fn large_step() -> Self {
<$nonzero>::new(10).unwrap()
}
fn small_step() -> Self {
<$nonzero>::new(1).unwrap()
}
fn min_value() -> Self {
<$nonzero>::MIN
}
fn max_value() -> Self {
<$nonzero>::MAX
}
fn saturating_add(self, rhs: Self) -> Self {
let result = self.get().saturating_add(rhs.get());
<$nonzero>::new(result.max(1)).unwrap()
}
fn saturating_sub(self, rhs: Self) -> Self {
let result = self.get().saturating_sub(rhs.get()).max(1);
<$nonzero>::new(result).unwrap()
}
}
};
}
@@ -75,10 +162,7 @@ macro_rules! impl_numeric_stepper_float {
($type:ident) => {
impl NumericStepperType for $type {
fn default_format(value: &Self) -> String {
format!("{:^4}", value)
.trim_end_matches('0')
.trim_end_matches('.')
.to_string()
format!("{:.2}", value)
}
fn default_step() -> Self {
@@ -100,6 +184,14 @@ macro_rules! impl_numeric_stepper_float {
fn max_value() -> Self {
<$type>::MAX
}
fn saturating_add(self, rhs: Self) -> Self {
(self + rhs).clamp(Self::min_value(), Self::max_value())
}
fn saturating_sub(self, rhs: Self) -> Self {
(self - rhs).clamp(Self::min_value(), Self::max_value())
}
}
};
}
@@ -113,6 +205,9 @@ impl_numeric_stepper_int!(u32);
impl_numeric_stepper_int!(i64);
impl_numeric_stepper_int!(u64);
impl_numeric_stepper_nonzero_int!(NonZeroU32, u32);
impl_numeric_stepper_nonzero_int!(NonZeroU64, u64);
#[derive(RegisterComponent)]
pub struct NumericStepper<T = usize> {
id: ElementId,
@@ -281,7 +376,7 @@ impl<T: NumericStepperType> RenderOnce for NumericStepper<T> {
let min = self.min_value;
move |click: &ClickEvent, window: &mut Window, cx: &mut App| {
let step = get_step(click.modifiers());
let new_value = value - step;
let new_value = value.saturating_sub(step);
let new_value = if new_value < min { min } else { new_value };
on_change(&new_value, window, cx);
window.focus_prev();
@@ -410,7 +505,7 @@ impl<T: NumericStepperType> RenderOnce for NumericStepper<T> {
let max = self.max_value;
move |click: &ClickEvent, window: &mut Window, cx: &mut App| {
let step = get_step(click.modifiers());
let new_value = value + step;
let new_value = value.saturating_add(step);
let new_value = if new_value > max { max } else { new_value };
on_change(&new_value, window, cx);
}