add sizable trait
This commit is contained in:
@@ -6,9 +6,9 @@ use gpui::{
|
||||
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled as _,
|
||||
Window, div,
|
||||
};
|
||||
use gpui_native_theme::{ActiveTheme as _, Oklch, ThemeVariant};
|
||||
use gpui_native_theme::{ActiveTheme as _, Oklch, Size, ThemeVariant};
|
||||
|
||||
use crate::common::h_flex;
|
||||
use crate::common::{Sizable, h_flex};
|
||||
|
||||
/// Number of rows and columns in the pixel grid.
|
||||
const GRID_SIZE: usize = 8;
|
||||
@@ -27,6 +27,7 @@ pub struct Avatar {
|
||||
id: SharedString,
|
||||
name: Option<SharedString>,
|
||||
picture: Option<SharedString>,
|
||||
size: Size,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
@@ -42,6 +43,7 @@ impl Avatar {
|
||||
id: id.to_string().into(),
|
||||
name: None,
|
||||
picture: None,
|
||||
size: Size::default(),
|
||||
style: StyleRefinement::default(),
|
||||
}
|
||||
}
|
||||
@@ -65,6 +67,12 @@ impl Avatar {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for Avatar {
|
||||
fn apply_size(&mut self, size: Size) {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
impl gpui::Styled for Avatar {
|
||||
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
||||
&mut self.style
|
||||
@@ -75,11 +83,15 @@ impl RenderOnce for Avatar {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let radius = theme.radius.control;
|
||||
let side = theme.sizes.resolve(self.size).avatar_size;
|
||||
|
||||
let mut base = h_flex()
|
||||
.id(ElementId::from(self.id.clone()))
|
||||
.flex_none()
|
||||
.justify_center()
|
||||
.overflow_hidden()
|
||||
.size(side)
|
||||
.text_size(side * 0.44)
|
||||
.bg(theme.colors.secondary)
|
||||
.rounded(radius)
|
||||
.when_some(
|
||||
@@ -91,13 +103,14 @@ impl RenderOnce for Avatar {
|
||||
base.style().refine(&self.style);
|
||||
|
||||
match self.picture {
|
||||
Some(url) => base.size_8().child(
|
||||
Some(url) => base.child(
|
||||
gpui::img(gpui::SharedUri::from(url))
|
||||
.size_full()
|
||||
.rounded(radius),
|
||||
),
|
||||
None => base.size_8().child(PixelAvatar {
|
||||
None => base.child(PixelAvatar {
|
||||
seed: fnv1a(self.id.as_bytes()),
|
||||
side,
|
||||
style: self.style,
|
||||
}),
|
||||
}
|
||||
@@ -109,6 +122,7 @@ impl RenderOnce for Avatar {
|
||||
#[derive(IntoElement)]
|
||||
struct PixelAvatar {
|
||||
seed: u64,
|
||||
side: gpui::Pixels,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
@@ -164,7 +178,7 @@ impl RenderOnce for PixelAvatar {
|
||||
.grid()
|
||||
.grid_cols(GRID_SIZE as u16)
|
||||
.grid_rows(GRID_SIZE as u16)
|
||||
.size_8()
|
||||
.size(self.side)
|
||||
.flex_none()
|
||||
.rounded(radius)
|
||||
.overflow_hidden()
|
||||
|
||||
@@ -4,12 +4,12 @@ use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
App, BoxShadow, ClickEvent, ElementId, InteractiveElement as _, IntoElement,
|
||||
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
|
||||
Styled as _, Window, px,
|
||||
Styled as _, Window, px, relative,
|
||||
};
|
||||
use gpui_native_icons::{IconRole, icon};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
|
||||
use crate::common::h_flex;
|
||||
use crate::common::{Sizable, h_flex};
|
||||
|
||||
/// The single activation handler, shared by pointer, Enter, and Space.
|
||||
type ClickHandler = Box<dyn Fn(&ClickEvent, &mut Window, &mut App)>;
|
||||
@@ -35,6 +35,7 @@ pub struct Button {
|
||||
label: Option<SharedString>,
|
||||
icon: Option<IconRole>,
|
||||
variant: ButtonVariant,
|
||||
size: Size,
|
||||
disabled: bool,
|
||||
a11y_label: Option<SharedString>,
|
||||
on_click: Option<ClickHandler>,
|
||||
@@ -51,6 +52,7 @@ impl Button {
|
||||
label: None,
|
||||
icon: None,
|
||||
variant: ButtonVariant::default(),
|
||||
size: Size::default(),
|
||||
disabled: false,
|
||||
a11y_label: None,
|
||||
on_click: None,
|
||||
@@ -103,11 +105,18 @@ impl Button {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for Button {
|
||||
fn apply_size(&mut self, size: Size) {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Button {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors;
|
||||
let ramps = theme.ramps;
|
||||
let metrics = theme.sizes.resolve(self.size);
|
||||
|
||||
let on_click = self.on_click;
|
||||
let a11y_label = self.a11y_label.or_else(|| self.label.clone());
|
||||
@@ -143,11 +152,12 @@ impl RenderOnce for Button {
|
||||
.id(self.id)
|
||||
.role(Role::Button)
|
||||
.justify_center()
|
||||
.h_7()
|
||||
.px_4()
|
||||
.h(metrics.control_height)
|
||||
.px(metrics.padding_x)
|
||||
.line_height(relative(1.0))
|
||||
.gap_1p5()
|
||||
.rounded(theme.radius.control)
|
||||
.text_sm()
|
||||
.text_size(metrics.text_size)
|
||||
.when(self.variant == ButtonVariant::Secondary, |button| {
|
||||
button.border_1().border_color(colors.border)
|
||||
})
|
||||
@@ -155,7 +165,11 @@ impl RenderOnce for Button {
|
||||
.text_color(foreground)
|
||||
.when_some(a11y_label, |button, label| button.aria_label(label))
|
||||
.when_some(self.icon, |button, role| {
|
||||
button.child(icon(role).color(foreground))
|
||||
button.child(
|
||||
icon(role)
|
||||
.size(f32::from(metrics.icon_size))
|
||||
.color(foreground),
|
||||
)
|
||||
})
|
||||
.when_some(self.label, |button, label| button.child(label))
|
||||
.when(self.disabled, |button| button.opacity(0.5))
|
||||
|
||||
@@ -7,19 +7,20 @@ use gpui::{
|
||||
px,
|
||||
};
|
||||
use gpui_native_icons::{IconRole, icon};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
|
||||
use crate::common::h_flex;
|
||||
use crate::common::{Sizable, h_flex};
|
||||
|
||||
/// The change reporter, fired with the next value by pointer, Enter, and Space.
|
||||
type ChangeHandler = Box<dyn Fn(bool, &mut Window, &mut App)>;
|
||||
|
||||
/// Checkbox component.
|
||||
/// A controlled checkbox component.
|
||||
#[derive(IntoElement)]
|
||||
pub struct Checkbox {
|
||||
id: ElementId,
|
||||
label: Option<SharedString>,
|
||||
checked: bool,
|
||||
size: Size,
|
||||
disabled: bool,
|
||||
a11y_label: Option<SharedString>,
|
||||
on_change: Option<ChangeHandler>,
|
||||
@@ -35,6 +36,7 @@ impl Checkbox {
|
||||
id: id.into(),
|
||||
label: None,
|
||||
checked: false,
|
||||
size: Size::default(),
|
||||
disabled: false,
|
||||
a11y_label: None,
|
||||
on_change: None,
|
||||
@@ -81,20 +83,27 @@ impl Checkbox {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for Checkbox {
|
||||
fn apply_size(&mut self, size: Size) {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Checkbox {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors;
|
||||
let ramps = theme.ramps;
|
||||
let metrics = theme.sizes.resolve(self.size);
|
||||
let on_change = self.on_change;
|
||||
let next = !self.checked;
|
||||
let a11y_label = self.a11y_label.or_else(|| self.label.clone());
|
||||
|
||||
h_flex()
|
||||
.id(self.id)
|
||||
.role(Role::CheckBox)
|
||||
.gap_1p5()
|
||||
.text_sm()
|
||||
.text_size(metrics.text_size)
|
||||
.role(Role::CheckBox)
|
||||
.when_some(a11y_label, |checkbox, label| checkbox.aria_label(label))
|
||||
.aria_toggled(Toggled::from(self.checked))
|
||||
.when(self.disabled, |checkbox| checkbox.opacity(0.5))
|
||||
@@ -118,7 +127,7 @@ impl RenderOnce for Checkbox {
|
||||
h_flex()
|
||||
.justify_center()
|
||||
.flex_none()
|
||||
.size_4()
|
||||
.size(metrics.icon_size)
|
||||
.rounded(theme.radius.sm)
|
||||
.border_1()
|
||||
.when(self.checked, |indicator| {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
use gpui::{Div, Styled as _, div};
|
||||
|
||||
pub use gpui_native_theme::Size;
|
||||
|
||||
/// A row whose children center on the cross axis.
|
||||
pub fn h_flex() -> Div {
|
||||
div().flex().flex_row().items_center()
|
||||
@@ -11,3 +13,31 @@ pub fn h_flex() -> Div {
|
||||
pub fn v_flex() -> Div {
|
||||
div().flex().flex_col()
|
||||
}
|
||||
|
||||
/// A component that can be told which size to render at.
|
||||
pub trait Sizable: Sized {
|
||||
/// Sets the size.
|
||||
fn with_size(mut self, size: Size) -> Self {
|
||||
self.apply_size(size);
|
||||
self
|
||||
}
|
||||
|
||||
/// Applies the size to the component's fields.
|
||||
fn apply_size(&mut self, size: Size);
|
||||
|
||||
/// Sets `Size::Small`.
|
||||
fn small(self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.with_size(Size::Small)
|
||||
}
|
||||
|
||||
/// Sets `Size::Large`.
|
||||
fn large(self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.with_size(Size::Large)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ use gpui::{
|
||||
SharedString, StatefulInteractiveElement as _, Styled as _, TextAlign, TextRun, UTF16Selection,
|
||||
Window, canvas, div, fill, point, px,
|
||||
};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
|
||||
use crate::common::h_flex;
|
||||
use crate::common::{Sizable, h_flex};
|
||||
|
||||
/// Input layout information for hit testing and caret positioning.
|
||||
struct InputLayout {
|
||||
@@ -374,6 +374,7 @@ pub struct Input {
|
||||
id: ElementId,
|
||||
state: Entity<InputState>,
|
||||
placeholder: SharedString,
|
||||
size: Size,
|
||||
disabled: bool,
|
||||
a11y_label: Option<SharedString>,
|
||||
}
|
||||
@@ -388,6 +389,7 @@ impl Input {
|
||||
id: id.into(),
|
||||
state,
|
||||
placeholder: SharedString::default(),
|
||||
size: Size::default(),
|
||||
disabled: false,
|
||||
a11y_label: None,
|
||||
}
|
||||
@@ -418,11 +420,18 @@ impl Input {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for Input {
|
||||
fn apply_size(&mut self, size: Size) {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Input {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors;
|
||||
let radius = theme.radius;
|
||||
let metrics = theme.sizes.resolve(self.size);
|
||||
let focus_handle = self.state.read(cx).focus_handle.clone();
|
||||
let layout_cell = self.state.read(cx).layout.clone();
|
||||
|
||||
@@ -447,19 +456,19 @@ impl RenderOnce for Input {
|
||||
|
||||
h_flex()
|
||||
.id(self.id.clone())
|
||||
.role(Role::TextInput)
|
||||
.aria_label(fallback_label)
|
||||
.relative()
|
||||
.overflow_hidden()
|
||||
.w_full()
|
||||
.h_7()
|
||||
.px_2()
|
||||
.h(metrics.input_height)
|
||||
.px(metrics.padding_x / 2.0)
|
||||
.rounded(radius.control)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
.bg(colors.input)
|
||||
.text_sm()
|
||||
.text_size(metrics.text_size)
|
||||
.text_color(colors.foreground)
|
||||
.role(Role::TextInput)
|
||||
.aria_label(fallback_label)
|
||||
.when(self.disabled, |input| input.opacity(0.5))
|
||||
.when(!self.disabled, |input| {
|
||||
input.track_focus(&focus_handle).tab_stop(true)
|
||||
@@ -506,6 +515,7 @@ impl RenderOnce for Input {
|
||||
let layout_cell = layout_cell.clone();
|
||||
let focus = focus_handle.clone();
|
||||
let dragging = paint_dragging;
|
||||
let padding = metrics.padding_x / 2.0;
|
||||
let disabled = self.disabled;
|
||||
// `line` comes from the prepaint closure above.
|
||||
move |bounds, line, window, cx| {
|
||||
@@ -519,7 +529,6 @@ impl RenderOnce for Input {
|
||||
)
|
||||
};
|
||||
|
||||
let padding = px(8.);
|
||||
let selection = state.read(cx).selection();
|
||||
let caret_x = line.x_for_index(selection.end);
|
||||
|
||||
|
||||
+13
-3
@@ -4,9 +4,9 @@ use gpui::{
|
||||
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
|
||||
Styled as _, Window, px,
|
||||
};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
|
||||
use crate::common::h_flex;
|
||||
use crate::common::{Sizable, h_flex};
|
||||
|
||||
/// The click handler. When set it replaces the default `href` opening.
|
||||
type ClickHandler = Box<dyn Fn(&ClickEvent, &mut Window, &mut App)>;
|
||||
@@ -17,6 +17,7 @@ pub struct Link {
|
||||
id: ElementId,
|
||||
label: Option<SharedString>,
|
||||
href: Option<SharedString>,
|
||||
size: Size,
|
||||
disabled: bool,
|
||||
a11y_label: Option<SharedString>,
|
||||
on_click: Option<ClickHandler>,
|
||||
@@ -32,6 +33,7 @@ impl Link {
|
||||
id: id.into(),
|
||||
label: None,
|
||||
href: None,
|
||||
size: Size::default(),
|
||||
disabled: false,
|
||||
a11y_label: None,
|
||||
on_click: None,
|
||||
@@ -81,18 +83,26 @@ impl Link {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for Link {
|
||||
fn apply_size(&mut self, size: Size) {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Link {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors;
|
||||
let ramps = theme.ramps;
|
||||
let metrics = theme.sizes.resolve(self.size);
|
||||
|
||||
let on_click = self.on_click;
|
||||
let href = self.href;
|
||||
let a11y_label = self.a11y_label.or_else(|| self.label.clone());
|
||||
|
||||
h_flex()
|
||||
.id(self.id)
|
||||
.text_sm()
|
||||
.text_size(metrics.text_size)
|
||||
.text_color(colors.accent)
|
||||
.underline()
|
||||
.role(Role::Link)
|
||||
|
||||
@@ -8,7 +8,9 @@ use gpui::{
|
||||
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
|
||||
Styled as _, Window, div, relative,
|
||||
};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
|
||||
use crate::common::Sizable;
|
||||
|
||||
/// A progress bar. With a `value` in `0..=1` it paints a determinate fill.
|
||||
#[derive(IntoElement)]
|
||||
@@ -16,6 +18,7 @@ pub struct Progress {
|
||||
id: ElementId,
|
||||
value: f32,
|
||||
indeterminate: bool,
|
||||
size: Size,
|
||||
a11y_label: Option<SharedString>,
|
||||
}
|
||||
|
||||
@@ -29,6 +32,7 @@ impl Progress {
|
||||
id: id.into(),
|
||||
value,
|
||||
indeterminate: false,
|
||||
size: Size::default(),
|
||||
a11y_label: None,
|
||||
}
|
||||
}
|
||||
@@ -49,12 +53,21 @@ impl Progress {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for Progress {
|
||||
fn apply_size(&mut self, size: Size) {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Progress {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors;
|
||||
let ramps = theme.ramps;
|
||||
let radius = theme.radius;
|
||||
let metrics = theme.sizes.resolve(self.size);
|
||||
|
||||
let track_height = metrics.icon_size / 4.0;
|
||||
let value = self.value.clamp(0.0, 1.0);
|
||||
|
||||
div()
|
||||
@@ -73,7 +86,7 @@ impl RenderOnce for Progress {
|
||||
.child(
|
||||
div()
|
||||
.w_full()
|
||||
.h_1p5()
|
||||
.h(track_height)
|
||||
.rounded(radius.full)
|
||||
.bg(colors.border)
|
||||
.child(if self.indeterminate {
|
||||
|
||||
@@ -4,9 +4,9 @@ use gpui::{
|
||||
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Toggled, Window,
|
||||
div, px,
|
||||
};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
|
||||
use crate::common::{h_flex, v_flex};
|
||||
use crate::common::{Sizable, h_flex, v_flex};
|
||||
|
||||
/// The group's change reporter, fired with the clicked radio's value.
|
||||
type ChangeHandler<V> = std::rc::Rc<dyn Fn(V, &mut Window, &mut App)>;
|
||||
@@ -72,6 +72,7 @@ where
|
||||
id: ElementId,
|
||||
selected: Option<V>,
|
||||
children: Vec<Radio<V>>,
|
||||
size: Size,
|
||||
a11y_label: Option<SharedString>,
|
||||
on_change: Option<ChangeHandler<V>>,
|
||||
}
|
||||
@@ -89,6 +90,7 @@ where
|
||||
id: id.into(),
|
||||
selected: None,
|
||||
children: Vec::new(),
|
||||
size: Size::default(),
|
||||
a11y_label: None,
|
||||
on_change: None,
|
||||
}
|
||||
@@ -125,6 +127,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Sizable for RadioGroup<V>
|
||||
where
|
||||
V: Clone + PartialEq + 'static,
|
||||
{
|
||||
fn apply_size(&mut self, size: Size) {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> RenderOnce for RadioGroup<V>
|
||||
where
|
||||
V: Clone + PartialEq + 'static,
|
||||
@@ -134,6 +145,7 @@ where
|
||||
let colors = theme.colors;
|
||||
let ramps = theme.ramps;
|
||||
let radius = theme.radius;
|
||||
let metrics = theme.sizes.resolve(self.size);
|
||||
let selected = self.selected;
|
||||
let on_change = self.on_change;
|
||||
let group_label = self.a11y_label;
|
||||
@@ -141,7 +153,7 @@ where
|
||||
v_flex()
|
||||
.id(self.id)
|
||||
.gap_2()
|
||||
.text_sm()
|
||||
.text_size(metrics.text_size)
|
||||
.role(Role::RadioGroup)
|
||||
.when_some(group_label.clone(), |group, label| group.aria_label(label))
|
||||
.children(self.children.into_iter().map(move |radio| {
|
||||
@@ -182,7 +194,7 @@ where
|
||||
h_flex()
|
||||
.justify_center()
|
||||
.flex_none()
|
||||
.size_4()
|
||||
.size(metrics.icon_size)
|
||||
.rounded(radius.full)
|
||||
.border_1()
|
||||
.when(is_selected, |indicator| {
|
||||
@@ -192,7 +204,7 @@ where
|
||||
.hover(|style| style.bg(ramps.primary.hover))
|
||||
.child(
|
||||
div()
|
||||
.size_2()
|
||||
.size(metrics.icon_size / 2.0)
|
||||
.rounded(radius.full)
|
||||
.bg(colors.primary_foreground),
|
||||
)
|
||||
|
||||
+23
-10
@@ -6,11 +6,11 @@ use gpui::{
|
||||
App, Bounds, BoxShadow, DispatchPhase, ElementId, InteractiveElement as _, IntoElement,
|
||||
KeyDownEvent, MouseButton, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels,
|
||||
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Window, canvas,
|
||||
div, px, relative, rems,
|
||||
div, px, relative,
|
||||
};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
|
||||
use crate::common::h_flex;
|
||||
use crate::common::{Sizable, h_flex};
|
||||
|
||||
/// The change reporter, fired with the next value by click, drag, and arrows.
|
||||
type ChangeHandler = Rc<dyn Fn(f32, &mut Window, &mut App)>;
|
||||
@@ -36,6 +36,7 @@ pub struct Slider {
|
||||
max: f32,
|
||||
step: f32,
|
||||
value: f32,
|
||||
size: Size,
|
||||
disabled: bool,
|
||||
a11y_label: Option<SharedString>,
|
||||
on_change: Option<ChangeHandler>,
|
||||
@@ -53,6 +54,7 @@ impl Slider {
|
||||
max: 1.0,
|
||||
step: 0.0,
|
||||
value,
|
||||
size: Size::default(),
|
||||
disabled: false,
|
||||
a11y_label: None,
|
||||
on_change: None,
|
||||
@@ -108,12 +110,23 @@ impl Slider {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for Slider {
|
||||
fn apply_size(&mut self, size: Size) {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Slider {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors;
|
||||
let ramps = theme.ramps;
|
||||
let radius = theme.radius;
|
||||
let metrics = theme.sizes.resolve(self.size);
|
||||
|
||||
let row_height = metrics.icon_size;
|
||||
let track_height = metrics.icon_size / 4.0;
|
||||
let knob = metrics.icon_size * 1.25;
|
||||
|
||||
let value = self.value.clamp(self.min, self.max);
|
||||
let fraction = if self.max > self.min {
|
||||
@@ -162,7 +175,7 @@ impl RenderOnce for Slider {
|
||||
.id(self.id)
|
||||
.relative()
|
||||
.w_full()
|
||||
.h_5()
|
||||
.h(row_height)
|
||||
.role(Role::Slider)
|
||||
.when_some(self.a11y_label.clone(), |slider, label| {
|
||||
slider.aria_label(label)
|
||||
@@ -251,8 +264,8 @@ impl RenderOnce for Slider {
|
||||
div()
|
||||
.absolute()
|
||||
.left_0()
|
||||
.top_2()
|
||||
.h_1()
|
||||
.top((row_height - track_height) / 2.0)
|
||||
.h(track_height)
|
||||
.w_full()
|
||||
.rounded(radius.full)
|
||||
.bg(colors.border),
|
||||
@@ -261,8 +274,8 @@ impl RenderOnce for Slider {
|
||||
div()
|
||||
.absolute()
|
||||
.left_0()
|
||||
.top_2()
|
||||
.h_1()
|
||||
.top((row_height - track_height) / 2.0)
|
||||
.h(track_height)
|
||||
.w(relative(fraction))
|
||||
.rounded(radius.full)
|
||||
.bg(ramps.primary.base),
|
||||
@@ -278,13 +291,13 @@ impl RenderOnce for Slider {
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.size_5()
|
||||
.size(knob)
|
||||
.flex_none()
|
||||
.rounded(radius.full)
|
||||
.bg(colors.primary_foreground)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
.mr(rems(-0.625)),
|
||||
.mr(-knob / 2.0),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ use gpui::{
|
||||
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Toggled, Window,
|
||||
div, px,
|
||||
};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
|
||||
use crate::common::h_flex;
|
||||
use crate::common::{Sizable, h_flex};
|
||||
|
||||
/// The change reporter, fired with the next value by pointer, Enter, and Space.
|
||||
type ChangeHandler = Box<dyn Fn(bool, &mut Window, &mut App)>;
|
||||
@@ -19,6 +19,7 @@ pub struct Switch {
|
||||
id: ElementId,
|
||||
label: Option<SharedString>,
|
||||
checked: bool,
|
||||
size: Size,
|
||||
disabled: bool,
|
||||
a11y_label: Option<SharedString>,
|
||||
on_change: Option<ChangeHandler>,
|
||||
@@ -34,6 +35,7 @@ impl Switch {
|
||||
id: id.into(),
|
||||
label: None,
|
||||
checked: false,
|
||||
size: Size::default(),
|
||||
disabled: false,
|
||||
a11y_label: None,
|
||||
on_change: None,
|
||||
@@ -80,19 +82,33 @@ impl Switch {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for Switch {
|
||||
fn apply_size(&mut self, size: Size) {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Switch {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors;
|
||||
let ramps = theme.ramps;
|
||||
let on_change = self.on_change;
|
||||
let metrics = theme.sizes.resolve(self.size);
|
||||
|
||||
let track_height = metrics.icon_size * 1.25;
|
||||
let track_width = metrics.icon_size * 2.25;
|
||||
|
||||
let knob = metrics.icon_size;
|
||||
let inset = metrics.icon_size * 0.125;
|
||||
|
||||
let next = !self.checked;
|
||||
let on_change = self.on_change;
|
||||
let a11y_label = self.a11y_label.or_else(|| self.label.clone());
|
||||
|
||||
h_flex()
|
||||
.id(self.id)
|
||||
.gap_1p5()
|
||||
.text_sm()
|
||||
.text_size(metrics.text_size)
|
||||
.role(Role::Switch)
|
||||
.when_some(a11y_label, |switch, label| switch.aria_label(label))
|
||||
.aria_toggled(Toggled::from(self.checked))
|
||||
@@ -114,9 +130,9 @@ impl RenderOnce for Switch {
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_none()
|
||||
.w_9()
|
||||
.h_5()
|
||||
.p_0p5()
|
||||
.w(track_width)
|
||||
.h(track_height)
|
||||
.p(inset)
|
||||
.rounded(theme.radius.full)
|
||||
.when(self.checked, |track| {
|
||||
track
|
||||
@@ -129,7 +145,7 @@ impl RenderOnce for Switch {
|
||||
})
|
||||
.child(
|
||||
div()
|
||||
.size_4()
|
||||
.size(knob)
|
||||
.flex_none()
|
||||
.rounded(theme.radius.full)
|
||||
.bg(colors.primary_foreground)
|
||||
|
||||
@@ -4,9 +4,9 @@ use gpui::{
|
||||
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Toggled, Window,
|
||||
px,
|
||||
};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
|
||||
use crate::common::h_flex;
|
||||
use crate::common::{Sizable, h_flex};
|
||||
|
||||
/// The change handler, fired with the next pressed state.
|
||||
type ChangeHandler = Box<dyn Fn(bool, &mut Window, &mut App)>;
|
||||
@@ -17,6 +17,7 @@ pub struct Toggle {
|
||||
id: ElementId,
|
||||
label: Option<SharedString>,
|
||||
pressed: bool,
|
||||
size: Size,
|
||||
disabled: bool,
|
||||
a11y_label: Option<SharedString>,
|
||||
on_change: Option<ChangeHandler>,
|
||||
@@ -32,6 +33,7 @@ impl Toggle {
|
||||
id: id.into(),
|
||||
label: None,
|
||||
pressed: false,
|
||||
size: Size::default(),
|
||||
disabled: false,
|
||||
a11y_label: None,
|
||||
on_change: None,
|
||||
@@ -78,11 +80,18 @@ impl Toggle {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for Toggle {
|
||||
fn apply_size(&mut self, size: Size) {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Toggle {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors;
|
||||
let ramps = theme.ramps;
|
||||
let metrics = theme.sizes.resolve(self.size);
|
||||
let on_change = self.on_change;
|
||||
let next = !self.pressed;
|
||||
let a11y_label = self.a11y_label.or_else(|| self.label.clone());
|
||||
@@ -90,10 +99,10 @@ impl RenderOnce for Toggle {
|
||||
h_flex()
|
||||
.id(self.id)
|
||||
.justify_center()
|
||||
.h_7()
|
||||
.px_3()
|
||||
.h(metrics.control_height)
|
||||
.px(metrics.padding_x * 0.75)
|
||||
.rounded(theme.radius.control)
|
||||
.text_sm()
|
||||
.text_size(metrics.text_size)
|
||||
.text_color(colors.foreground)
|
||||
.role(Role::Button)
|
||||
.when_some(a11y_label, |toggle, label| toggle.aria_label(label))
|
||||
|
||||
Reference in New Issue
Block a user