chore: Apply Styled to elements to support flexable style refinement. (#1005)

## Break Changes

- date_picker, dropdown: Removed `width` method, use `w`, `w_full`
instead.

    ```diff
    - .width(px(100.))
    + .w(px(100.))
    ```

- calendar: Removed `bordered` method, use `border` method from GPUI
instead.

    ```diff
    - .bordered(false)
    + .border_0()
    ```
This commit is contained in:
Jason Lee 2025-06-24 18:56:35 +08:00 committed by GitHub
parent c05d63dfbe
commit 9aa593ce0b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 68 additions and 67 deletions

View file

@ -19,7 +19,6 @@ pub struct DatePickerStory {
date_picker_value: Option<String>, date_picker_value: Option<String>,
date_range_picker: Entity<DatePickerState>, date_range_picker: Entity<DatePickerState>,
default_range_mode_picker: Entity<DatePickerState>, default_range_mode_picker: Entity<DatePickerState>,
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
} }
@ -175,33 +174,29 @@ impl Render for DatePickerStory {
v_flex() v_flex()
.gap_3() .gap_3()
.child( .child(
section("Normal").max_w_md().child( section("Normal").max_w_128().child(
DatePicker::new(&self.date_picker) DatePicker::new(&self.date_picker)
.cleanable() .cleanable()
.presets(presets), .presets(presets),
), ),
) )
.child( .child(
section("Small with 180px width").max_w_md().child( section("Small with 180px width")
DatePicker::new(&self.date_picker_small) .max_w_128()
.small() .child(DatePicker::new(&self.date_picker_small).small().w(px(180.))),
.width(px(180.)),
),
) )
.child( .child(
section("Large").max_w_md().child( section("Large")
DatePicker::new(&self.date_picker_large) .max_w_128()
.large() .child(DatePicker::new(&self.date_picker_large).large().w(px(300.))),
.width(px(300.)),
),
) )
.child( .child(
section("Custom (First 5 days of each month disabled)") section("Custom (First 5 days of each month disabled)")
.max_w_md() .max_w_128()
.child(DatePicker::new(&self.data_picker_custom)), .child(DatePicker::new(&self.data_picker_custom)),
) )
.child( .child(
section("Date Range").max_w_md().child( section("Date Range").max_w_128().child(
DatePicker::new(&self.date_range_picker) DatePicker::new(&self.date_range_picker)
.number_of_months(2) .number_of_months(2)
.cleanable() .cleanable()
@ -209,7 +204,7 @@ impl Render for DatePickerStory {
), ),
) )
.child( .child(
section("Default Range Mode").max_w_md().child( section("Default Range Mode").max_w_128().child(
DatePicker::new(&self.default_range_mode_picker) DatePicker::new(&self.default_range_mode_picker)
.placeholder("Range mode picker") .placeholder("Range mode picker")
.cleanable() .cleanable()
@ -217,7 +212,7 @@ impl Render for DatePickerStory {
), ),
) )
.child( .child(
section("Date Picker Value").max_w_md().child( section("Date Picker Value").max_w_128().child(
format!("Date picker value: {:?}", self.date_picker_value).into_element(), format!("Date picker value: {:?}", self.date_picker_value).into_element(),
), ),
) )

View file

@ -236,7 +236,7 @@ impl Render for DropdownStory {
Dropdown::new(&self.fruit_dropdown) Dropdown::new(&self.fruit_dropdown)
.disabled(self.disabled) .disabled(self.disabled)
.icon(IconName::Search) .icon(IconName::Search)
.width(px(320.)) .w(px(320.))
.menu_width(px(400.)), .menu_width(px(400.)),
), ),
) )

View file

@ -2,8 +2,8 @@ use gpui::{
anchored, canvas, deferred, div, prelude::FluentBuilder, px, rems, AnyElement, App, AppContext, anchored, canvas, deferred, div, prelude::FluentBuilder, px, rems, AnyElement, App, AppContext,
Bounds, ClickEvent, Context, DismissEvent, ElementId, Empty, Entity, EventEmitter, FocusHandle, Bounds, ClickEvent, Context, DismissEvent, ElementId, Empty, Entity, EventEmitter, FocusHandle,
Focusable, InteractiveElement, IntoElement, KeyBinding, Length, ParentElement, Pixels, Render, Focusable, InteractiveElement, IntoElement, KeyBinding, Length, ParentElement, Pixels, Render,
RenderOnce, SharedString, StatefulInteractiveElement, Styled, Subscription, Task, WeakEntity, RenderOnce, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, Subscription,
Window, Task, WeakEntity, Window,
}; };
use rust_i18n::t; use rust_i18n::t;
@ -254,6 +254,7 @@ pub struct DropdownState<D: DropdownDelegate + 'static> {
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct Dropdown<D: DropdownDelegate + 'static> { pub struct Dropdown<D: DropdownDelegate + 'static> {
id: ElementId, id: ElementId,
style: StyleRefinement,
state: Entity<DropdownState<D>>, state: Entity<DropdownState<D>>,
size: Size, size: Size,
icon: Option<Icon>, icon: Option<Icon>,
@ -261,7 +262,6 @@ pub struct Dropdown<D: DropdownDelegate + 'static> {
placeholder: Option<SharedString>, placeholder: Option<SharedString>,
title_prefix: Option<SharedString>, title_prefix: Option<SharedString>,
empty: Option<AnyElement>, empty: Option<AnyElement>,
width: Length,
menu_width: Length, menu_width: Length,
disabled: bool, disabled: bool,
} }
@ -523,6 +523,7 @@ where
pub fn new(state: &Entity<DropdownState<D>>) -> Self { pub fn new(state: &Entity<DropdownState<D>>) -> Self {
Self { Self {
id: ("dropdown", state.entity_id()).into(), id: ("dropdown", state.entity_id()).into(),
style: StyleRefinement::default(),
state: state.clone(), state: state.clone(),
placeholder: None, placeholder: None,
size: Size::Medium, size: Size::Medium,
@ -530,18 +531,11 @@ where
cleanable: false, cleanable: false,
title_prefix: None, title_prefix: None,
empty: None, empty: None,
width: Length::Auto,
menu_width: Length::Auto, menu_width: Length::Auto,
disabled: false, disabled: false,
} }
} }
/// Set the width of the dropdown input, default: Length::Auto
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.width = width.into();
self
}
/// Set the width of the dropdown menu, default: Length::Auto /// Set the width of the dropdown menu, default: Length::Auto
pub fn menu_width(mut self, width: impl Into<Length>) -> Self { pub fn menu_width(mut self, width: impl Into<Length>) -> Self {
self.menu_width = width.into(); self.menu_width = width.into();
@ -668,6 +662,15 @@ where
} }
} }
impl<D> Styled for Dropdown<D>
where
D: DropdownDelegate,
{
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl<D> RenderOnce for Dropdown<D> impl<D> RenderOnce for Dropdown<D>
where where
D: DropdownDelegate + 'static, D: DropdownDelegate + 'static,
@ -704,7 +707,6 @@ where
.on_action(window.listener_for(&self.state, DropdownState::escape)) .on_action(window.listener_for(&self.state, DropdownState::escape))
.size_full() .size_full()
.relative() .relative()
.input_text_size(self.size)
.child( .child(
div() div()
.id(ElementId::Name(format!("{}-input", self.id).into())) .id(ElementId::Name(format!("{}-input", self.id).into()))
@ -720,23 +722,24 @@ where
.map(|this| if self.disabled { this } else { this }) .map(|this| if self.disabled { this } else { this })
.overflow_hidden() .overflow_hidden()
.input_text_size(self.size) .input_text_size(self.size)
.map(|this| match self.width { .flex_none()
Length::Definite(l) => this.flex_none().w(l), .w_full()
Length::Auto => this.w_full(),
})
.when(outline_visible, |this| this.focused_border(cx)) .when(outline_visible, |this| this.focused_border(cx))
.input_size(self.size) .input_size(self.size)
.refine_style(&self.style)
.when(allow_open, |this| { .when(allow_open, |this| {
this.on_click(window.listener_for(&self.state, DropdownState::toggle_menu)) this.on_click(window.listener_for(&self.state, DropdownState::toggle_menu))
}) })
.child( .child(
h_flex() h_flex()
.id("inner")
.w_full() .w_full()
.items_center() .items_center()
.justify_between() .justify_between()
.gap_1() .gap_1()
.child( .child(
div() div()
.id("title")
.w_full() .w_full()
.overflow_hidden() .overflow_hidden()
.whitespace_nowrap() .whitespace_nowrap()

View file

@ -4,13 +4,14 @@ use chrono::{Datelike, Local, NaiveDate};
use gpui::{ use gpui::{
prelude::FluentBuilder as _, px, relative, App, ClickEvent, Context, ElementId, Empty, Entity, prelude::FluentBuilder as _, px, relative, App, ClickEvent, Context, ElementId, Empty, Entity,
EventEmitter, FocusHandle, InteractiveElement, IntoElement, ParentElement, Render, RenderOnce, EventEmitter, FocusHandle, InteractiveElement, IntoElement, ParentElement, Render, RenderOnce,
SharedString, StatefulInteractiveElement, Styled, Window, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, Window,
}; };
use rust_i18n::t; use rust_i18n::t;
use crate::{ use crate::{
button::{Button, ButtonVariants as _}, button::{Button, ButtonVariants as _},
h_flex, v_flex, ActiveTheme, Disableable as _, IconName, Selectable, Sizable, Size, h_flex, v_flex, ActiveTheme, Disableable as _, IconName, Selectable, Sizable, Size,
StyledExt as _,
}; };
use super::utils::days_in_month; use super::utils::days_in_month;
@ -245,6 +246,15 @@ impl Matcher {
} }
} }
#[derive(IntoElement)]
pub struct Calendar {
size: Size,
state: Entity<CalendarState>,
style: StyleRefinement,
/// Number of the months view to show.
number_of_months: usize,
}
/// Use to store the state of the calendar. /// Use to store the state of the calendar.
pub struct CalendarState { pub struct CalendarState {
focus_handle: FocusHandle, focus_handle: FocusHandle,
@ -260,15 +270,6 @@ pub struct CalendarState {
disabled: Option<Matcher>, disabled: Option<Matcher>,
} }
#[derive(IntoElement)]
pub struct Calendar {
size: Size,
state: Entity<CalendarState>,
bordered: bool,
/// Number of the months view to show.
number_of_months: usize,
}
impl CalendarState { impl CalendarState {
pub fn new(_: &mut Window, cx: &mut Context<Self>) -> Self { pub fn new(_: &mut Window, cx: &mut Context<Self>) -> Self {
let today = Local::now().naive_local().date(); let today = Local::now().naive_local().date();
@ -498,7 +499,7 @@ impl Calendar {
Self { Self {
size: Size::default(), size: Size::default(),
state: state.clone(), state: state.clone(),
bordered: true, style: StyleRefinement::default(),
number_of_months: 1, number_of_months: 1,
} }
} }
@ -509,12 +510,6 @@ impl Calendar {
self self
} }
/// Set bordered, default: `true`.
pub fn bordered(mut self, bordered: bool) -> Self {
self.bordered = bordered;
self
}
fn render_day( fn render_day(
&self, &self,
d: &NaiveDate, d: &NaiveDate,
@ -907,6 +902,13 @@ impl Sizable for Calendar {
self self
} }
} }
impl Styled for Calendar {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl EventEmitter<CalendarEvent> for CalendarState {} impl EventEmitter<CalendarEvent> for CalendarState {}
impl RenderOnce for Calendar { impl RenderOnce for Calendar {
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
@ -917,10 +919,12 @@ impl RenderOnce for Calendar {
v_flex() v_flex()
.track_focus(&self.state.read(cx).focus_handle) .track_focus(&self.state.read(cx).focus_handle)
.when(self.bordered, |this| { .border_1()
this.border_1().border_color(cx.theme().border).p_3() .border_color(cx.theme().border)
}) .rounded(cx.theme().radius_lg)
.p_3()
.gap_0p5() .gap_0p5()
.refine_style(&self.style)
.child(self.render_header(window, cx)) .child(self.render_header(window, cx))
.child( .child(
v_flex() v_flex()

View file

@ -2,8 +2,8 @@ use chrono::NaiveDate;
use gpui::{ use gpui::{
anchored, deferred, div, prelude::FluentBuilder as _, px, App, AppContext, Context, ElementId, anchored, deferred, div, prelude::FluentBuilder as _, px, App, AppContext, Context, ElementId,
Empty, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement as _, IntoElement, Empty, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement as _, IntoElement,
KeyBinding, Length, MouseButton, ParentElement as _, Render, RenderOnce, SharedString, KeyBinding, MouseButton, ParentElement as _, Render, RenderOnce, SharedString,
StatefulInteractiveElement as _, Styled, Subscription, Window, StatefulInteractiveElement as _, StyleRefinement, Styled, Subscription, Window,
}; };
use rust_i18n::t; use rust_i18n::t;
@ -231,11 +231,11 @@ impl DatePickerState {
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct DatePicker { pub struct DatePicker {
id: ElementId, id: ElementId,
style: StyleRefinement,
state: Entity<DatePickerState>, state: Entity<DatePickerState>,
cleanable: bool, cleanable: bool,
placeholder: Option<SharedString>, placeholder: Option<SharedString>,
size: Size, size: Size,
width: Length,
number_of_months: usize, number_of_months: usize,
presets: Option<Vec<DateRangePreset>>, presets: Option<Vec<DateRangePreset>>,
} }
@ -252,6 +252,12 @@ impl Focusable for DatePicker {
} }
} }
impl Styled for DatePicker {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl Render for DatePickerState { impl Render for DatePickerState {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl gpui::IntoElement { fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl gpui::IntoElement {
Empty Empty
@ -266,7 +272,7 @@ impl DatePicker {
cleanable: true, cleanable: true,
placeholder: None, placeholder: None,
size: Size::default(), size: Size::default(),
width: Length::Auto, style: StyleRefinement::default(),
number_of_months: 2, number_of_months: 2,
presets: None, presets: None,
} }
@ -284,12 +290,6 @@ impl DatePicker {
self self
} }
/// Set width of the date picker input field, default is `Length::Auto`.
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.width = width.into();
self
}
/// Set preset ranges for the date picker. /// Set preset ranges for the date picker.
pub fn presets(mut self, presets: Vec<DateRangePreset>) -> Self { pub fn presets(mut self, presets: Vec<DateRangePreset>) -> Self {
self.presets = Some(presets); self.presets = Some(presets);
@ -325,13 +325,11 @@ impl RenderOnce for DatePicker {
.when(state.open, |this| { .when(state.open, |this| {
this.on_action(window.listener_for(&self.state, DatePickerState::escape)) this.on_action(window.listener_for(&self.state, DatePickerState::escape))
}) })
.flex_none()
.w_full() .w_full()
.relative() .relative()
.map(|this| match self.width {
Length::Definite(l) => this.flex_none().w(l),
Length::Auto => this.w_full(),
})
.input_text_size(self.size) .input_text_size(self.size)
.refine_style(&self.style)
.child( .child(
div() div()
.id("date-picker-input") .id("date-picker-input")
@ -423,7 +421,8 @@ impl RenderOnce for DatePicker {
.child( .child(
Calendar::new(&state.calendar) Calendar::new(&state.calendar)
.number_of_months(self.number_of_months) .number_of_months(self.number_of_months)
.bordered(false) .border_0()
.rounded_none()
.with_size(self.size), .with_size(self.size),
), ),
), ),