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

View file

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

View file

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

View file

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

View file

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