divider: Add support for dashed divider (#1730)
## Description 1. Add support for dashed divider. 2. Add divider story. ## Screenshot <img width="1139" height="859" alt="SCR-20251203-nueg" src="https://github.com/user-attachments/assets/3f2dafd9-f808-4315-bf58-c0380798eb66" />
This commit is contained in:
parent
ec097ed406
commit
e2f022cb59
4 changed files with 174 additions and 11 deletions
93
crates/story/src/divider_story.rs
Normal file
93
crates/story/src/divider_story.rs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
use crate::section;
|
||||
use gpui::{
|
||||
App, AppContext, Context, Entity, Focusable, IntoElement, ParentElement, Render, Styled,
|
||||
Window, px,
|
||||
};
|
||||
use gpui_component::{ActiveTheme, divider::Divider, h_flex, label::Label, v_flex};
|
||||
|
||||
const DESCRIPTION: &str = "GPUI Component is a Rust GUI components for building fantastic cross-platform desktop application by using GPUI.";
|
||||
|
||||
pub struct DividerStory {
|
||||
focus_handle: gpui::FocusHandle,
|
||||
}
|
||||
|
||||
impl super::Story for DividerStory {
|
||||
fn title() -> &'static str {
|
||||
"Divider"
|
||||
}
|
||||
|
||||
fn description() -> &'static str {
|
||||
"A divider that can be either vertical or horizontal."
|
||||
}
|
||||
|
||||
fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render> {
|
||||
Self::view(window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl DividerStory {
|
||||
pub fn view(_window: &mut Window, cx: &mut App) -> Entity<Self> {
|
||||
cx.new(|cx| Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Focusable for DividerStory {
|
||||
fn focus_handle(&self, _: &gpui::App) -> gpui::FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for DividerStory {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.child(
|
||||
section("Horizontal Dividers").child(
|
||||
v_flex()
|
||||
.gap_4()
|
||||
.w_full()
|
||||
.mt_4()
|
||||
.child(Divider::horizontal())
|
||||
.child(Divider::horizontal().label("With Label"))
|
||||
.child(Divider::horizontal_dashed())
|
||||
.child(Divider::horizontal_dashed().label("Dashed With Label")),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
section("Vertical Dividers").child(
|
||||
h_flex()
|
||||
.gap_4()
|
||||
.h(px(100.))
|
||||
.child(Divider::vertical())
|
||||
.child(Divider::vertical().label("Solid"))
|
||||
.child(Divider::vertical_dashed())
|
||||
.child(Divider::vertical_dashed().label("Dashed")),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
section("Combination Dividers").child(
|
||||
v_flex()
|
||||
.gap_y_4()
|
||||
.child(
|
||||
v_flex().gap_y_2().child("Hello GPUI Component").child(
|
||||
Label::new(DESCRIPTION)
|
||||
.text_color(cx.theme().muted_foreground)
|
||||
.text_sm(),
|
||||
),
|
||||
)
|
||||
.child(Divider::horizontal())
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_x_4()
|
||||
.child("Docs")
|
||||
.child(Divider::vertical().dashed())
|
||||
.child("Github")
|
||||
.child(Divider::vertical().dashed())
|
||||
.child("Source"),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ mod color_picker_story;
|
|||
mod date_picker_story;
|
||||
mod description_list_story;
|
||||
mod dialog_story;
|
||||
mod divider_story;
|
||||
mod dropdown_button_story;
|
||||
mod form_story;
|
||||
mod group_box_story;
|
||||
|
|
@ -73,6 +74,7 @@ pub use color_picker_story::ColorPickerStory;
|
|||
pub use date_picker_story::DatePickerStory;
|
||||
pub use description_list_story::DescriptionListStory;
|
||||
pub use dialog_story::DialogStory;
|
||||
pub use divider_story::DividerStory;
|
||||
pub use dropdown_button_story::DropdownButtonStory;
|
||||
pub use form_story::FormStory;
|
||||
pub use group_box_story::GroupBoxStory;
|
||||
|
|
@ -667,6 +669,7 @@ impl StoryState {
|
|||
"InputStory" => story!(InputStory),
|
||||
"ListStory" => story!(ListStory),
|
||||
"DialogStory" => story!(DialogStory),
|
||||
"DividerStory" => story!(DividerStory),
|
||||
"PopoverStory" => story!(PopoverStory),
|
||||
"ProgressStory" => story!(ProgressStory),
|
||||
"ResizableStory" => story!(ResizableStory),
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ impl Gallery {
|
|||
StoryContainer::panel::<DatePickerStory>(window, cx),
|
||||
StoryContainer::panel::<DescriptionListStory>(window, cx),
|
||||
StoryContainer::panel::<DialogStory>(window, cx),
|
||||
StoryContainer::panel::<DividerStory>(window, cx),
|
||||
StoryContainer::panel::<DropdownButtonStory>(window, cx),
|
||||
StoryContainer::panel::<FormStory>(window, cx),
|
||||
StoryContainer::panel::<GroupBoxStory>(window, cx),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
use crate::{ActiveTheme, StyledExt};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, App, Axis, Div, Hsla, IntoElement, ParentElement,
|
||||
RenderOnce, SharedString, StyleRefinement, Styled, Window,
|
||||
App, Axis, Div, Hsla, IntoElement, ParentElement, PathBuilder, RenderOnce, SharedString,
|
||||
StyleRefinement, Styled, Window, canvas, div, point, prelude::FluentBuilder as _, px,
|
||||
};
|
||||
|
||||
/// The style of the divider line.
|
||||
#[derive(Clone, Copy, PartialEq, Default)]
|
||||
pub enum DividerStyle {
|
||||
#[default]
|
||||
Solid,
|
||||
Dashed,
|
||||
}
|
||||
|
||||
/// A divider that can be either vertical or horizontal.
|
||||
#[derive(IntoElement)]
|
||||
pub struct Divider {
|
||||
|
|
@ -12,6 +20,7 @@ pub struct Divider {
|
|||
label: Option<SharedString>,
|
||||
axis: Axis,
|
||||
color: Option<Hsla>,
|
||||
line_style: DividerStyle,
|
||||
}
|
||||
|
||||
impl Divider {
|
||||
|
|
@ -23,6 +32,7 @@ impl Divider {
|
|||
label: None,
|
||||
color: None,
|
||||
style: StyleRefinement::default(),
|
||||
line_style: DividerStyle::Solid,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,9 +44,20 @@ impl Divider {
|
|||
label: None,
|
||||
color: None,
|
||||
style: StyleRefinement::default(),
|
||||
line_style: DividerStyle::Solid,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a vertical dashed divider.
|
||||
pub fn vertical_dashed() -> Self {
|
||||
Self::vertical().dashed()
|
||||
}
|
||||
|
||||
/// Creates a horizontal dashed divider.
|
||||
pub fn horizontal_dashed() -> Self {
|
||||
Self::horizontal().dashed()
|
||||
}
|
||||
|
||||
/// Sets the label for the divider.
|
||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||
self.label = Some(label.into());
|
||||
|
|
@ -48,6 +69,52 @@ impl Divider {
|
|||
self.color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the style of the divider to dashed.
|
||||
pub fn dashed(mut self) -> Self {
|
||||
self.line_style = DividerStyle::Dashed;
|
||||
self
|
||||
}
|
||||
|
||||
fn render_base(axis: Axis) -> Div {
|
||||
div().absolute().map(|this| match axis {
|
||||
Axis::Vertical => this.w(px(1.)).h_full(),
|
||||
Axis::Horizontal => this.h(px(1.)).w_full(),
|
||||
})
|
||||
}
|
||||
|
||||
fn render_solid(axis: Axis, color: Hsla) -> impl IntoElement {
|
||||
Self::render_base(axis).bg(color)
|
||||
}
|
||||
|
||||
fn render_dashed(axis: Axis, color: Hsla) -> impl IntoElement {
|
||||
Self::render_base(axis).child(
|
||||
canvas(
|
||||
move |_, _, _| {},
|
||||
move |bounds, _, window, _| {
|
||||
let mut builder = PathBuilder::stroke(px(1.)).dash_array(&[px(4.), px(2.)]);
|
||||
let (start, end) = match axis {
|
||||
Axis::Horizontal => {
|
||||
let x = bounds.origin.x;
|
||||
let y = bounds.origin.y + px(0.5);
|
||||
(point(x, y), point(x + bounds.size.width, y))
|
||||
}
|
||||
Axis::Vertical => {
|
||||
let x = bounds.origin.x + px(0.5);
|
||||
let y = bounds.origin.y;
|
||||
(point(x, y), point(x, y + bounds.size.height))
|
||||
}
|
||||
};
|
||||
builder.move_to(start);
|
||||
builder.line_to(end);
|
||||
if let Ok(line) = builder.build() {
|
||||
window.paint_path(line, color);
|
||||
}
|
||||
},
|
||||
)
|
||||
.size_full(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Styled for Divider {
|
||||
|
|
@ -58,21 +125,20 @@ impl Styled for Divider {
|
|||
|
||||
impl RenderOnce for Divider {
|
||||
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let color = self.color.unwrap_or(cx.theme().border);
|
||||
let axis = self.axis;
|
||||
let line_style = self.line_style;
|
||||
|
||||
self.base
|
||||
.flex()
|
||||
.flex_shrink_0()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.refine_style(&self.style)
|
||||
.child(
|
||||
div()
|
||||
.absolute()
|
||||
.map(|this| match self.axis {
|
||||
Axis::Vertical => this.w(px(1.)).h_full(),
|
||||
Axis::Horizontal => this.h(px(1.)).w_full(),
|
||||
})
|
||||
.bg(self.color.unwrap_or(cx.theme().border)),
|
||||
)
|
||||
.child(match line_style {
|
||||
DividerStyle::Solid => Self::render_solid(axis, color).into_any_element(),
|
||||
DividerStyle::Dashed => Self::render_dashed(axis, color).into_any_element(),
|
||||
})
|
||||
.when_some(self.label, |this, label| {
|
||||
this.child(
|
||||
div()
|
||||
|
|
|
|||
Loading…
Reference in a new issue