Add Indicator (#25)
https://github.com/user-attachments/assets/cd6410ca-e1c7-459e-8455-d76506b6d305
This commit is contained in:
parent
ff8d39d4a9
commit
7c99584862
18 changed files with 219 additions and 109 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -5427,7 +5427,6 @@ dependencies = [
|
|||
"taffy",
|
||||
"unicode-segmentation",
|
||||
"usvg",
|
||||
"uuid",
|
||||
"windows",
|
||||
"wry",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ This is an example of build app by using GPUI.
|
|||
- [x] Button with Icon
|
||||
- [x] IconButton
|
||||
- [x] Glost / Outline Button
|
||||
- [x] Loading
|
||||
- [x] Label
|
||||
- [x] Icon
|
||||
- [x] Checkbox
|
||||
|
|
@ -58,8 +59,8 @@ This is an example of build app by using GPUI.
|
|||
- [x] Dockpanel
|
||||
- [x] Resizable
|
||||
- [x] Progress
|
||||
- [ ] ProgressBar
|
||||
- [ ] Loading
|
||||
- [x] ProgressBar
|
||||
- [x] Indicator
|
||||
- [ ] Skeleton
|
||||
- [ ] DatePicker
|
||||
- [ ] DateTimePicker
|
||||
|
|
|
|||
3
assets/icons/loader-circle.svg
Normal file
3
assets/icons/loader-circle.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-circle">
|
||||
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 266 B |
10
assets/icons/loader.svg
Normal file
10
assets/icons/loader.svg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader">
|
||||
<path d="M12 2v4"/>
|
||||
<path d="m16.2 7.8 2.9-2.9"/>
|
||||
<path d="M18 12h4"/>
|
||||
<path d="m16.2 16.2 2.9 2.9"/>
|
||||
<path d="M12 18v4"/>
|
||||
<path d="m4.9 19.1 2.9-2.9"/>
|
||||
<path d="M2 12h4"/>
|
||||
<path d="m4.9 4.9 2.9 2.9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 435 B |
|
|
@ -9,9 +9,9 @@ use workspace::{dock::DockPosition, TitleBar, Workspace};
|
|||
|
||||
use std::sync::Arc;
|
||||
use ui::{
|
||||
button::ButtonSize,
|
||||
switch::{LabelSide, Switch},
|
||||
theme::{ActiveTheme, Theme},
|
||||
Size,
|
||||
};
|
||||
|
||||
use crate::app_state::AppState;
|
||||
|
|
@ -173,7 +173,7 @@ impl StoryWorkspace {
|
|||
appears_transparent: true,
|
||||
traffic_light_position: Some(point(px(9.0), px(9.0))),
|
||||
}),
|
||||
window_min_size: Some(Size {
|
||||
window_min_size: Some(gpui::Size {
|
||||
width: px(640.),
|
||||
height: px(480.),
|
||||
}),
|
||||
|
|
@ -255,7 +255,7 @@ impl Render for StoryWorkspace {
|
|||
.mr_3()
|
||||
.child(
|
||||
Switch::new("theme-mode")
|
||||
.size(ButtonSize::Small)
|
||||
.size(Size::Small)
|
||||
.checked(cx.theme().mode.is_dark())
|
||||
.label_side(LabelSide::Left)
|
||||
.label("Dark Mode")
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use gpui::{
|
|||
};
|
||||
|
||||
use ui::{
|
||||
button::{Button, ButtonSize, ButtonStyle},
|
||||
h_flex, v_flex, Clickable, Disableable as _, Icon, IconName, Selectable,
|
||||
button::{Button, ButtonStyle},
|
||||
h_flex, v_flex, Clickable, Disableable as _, Icon, IconName, Selectable, Size,
|
||||
};
|
||||
|
||||
use crate::section;
|
||||
|
|
@ -96,21 +96,22 @@ impl Render for ButtonStory {
|
|||
Button::new("button-6", cx)
|
||||
.label("Primary Button")
|
||||
.style(ButtonStyle::Primary)
|
||||
.size(ButtonSize::Small)
|
||||
.size(Size::Small)
|
||||
.loading(true)
|
||||
.on_click(Self::on_click),
|
||||
)
|
||||
.child(
|
||||
Button::new("button-7", cx)
|
||||
.label("Secondary Button")
|
||||
.style(ButtonStyle::Secondary)
|
||||
.size(ButtonSize::Small)
|
||||
.size(Size::Small)
|
||||
.on_click(Self::on_click),
|
||||
)
|
||||
.child(
|
||||
Button::new("button-8", cx)
|
||||
.label("Danger Button")
|
||||
.style(ButtonStyle::Danger)
|
||||
.size(ButtonSize::Small)
|
||||
.size(Size::Small)
|
||||
.on_click(Self::on_click),
|
||||
),
|
||||
)
|
||||
|
|
@ -120,21 +121,22 @@ impl Render for ButtonStory {
|
|||
Button::new("button-xs-1", cx)
|
||||
.label("Primary Button")
|
||||
.style(ButtonStyle::Primary)
|
||||
.size(ButtonSize::XSmall)
|
||||
.size(Size::XSmall)
|
||||
.on_click(Self::on_click),
|
||||
)
|
||||
.child(
|
||||
Button::new("button-xs-2", cx)
|
||||
.label("Secondary Button")
|
||||
.style(ButtonStyle::Secondary)
|
||||
.size(ButtonSize::XSmall)
|
||||
.size(Size::XSmall)
|
||||
.loading(true)
|
||||
.on_click(Self::on_click),
|
||||
)
|
||||
.child(
|
||||
Button::new("button-xs-3", cx)
|
||||
.label("Danger Button")
|
||||
.style(ButtonStyle::Danger)
|
||||
.size(ButtonSize::XSmall)
|
||||
.size(Size::XSmall)
|
||||
.on_click(Self::on_click),
|
||||
),
|
||||
),
|
||||
|
|
@ -163,7 +165,8 @@ impl Render for ButtonStory {
|
|||
.label("Disabled Button")
|
||||
.style(ButtonStyle::Danger)
|
||||
.on_click(Self::on_click)
|
||||
.disabled(true),
|
||||
.disabled(true)
|
||||
.loading(true),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
|
|
@ -195,7 +198,11 @@ impl Render for ButtonStory {
|
|||
.icon(IconName::Search)
|
||||
.style(ButtonStyle::Primary),
|
||||
)
|
||||
.child(Button::new("icon-button-1", cx).icon(IconName::Info))
|
||||
.child(
|
||||
Button::new("icon-button-1", cx)
|
||||
.icon(IconName::Info)
|
||||
.loading(true),
|
||||
)
|
||||
.child(
|
||||
Button::new("icon-button-2", cx)
|
||||
.icon(IconName::Close)
|
||||
|
|
@ -204,7 +211,7 @@ impl Render for ButtonStory {
|
|||
.child(
|
||||
Button::new("icon-button-3", cx)
|
||||
.icon(IconName::Search)
|
||||
.size(ButtonSize::Small)
|
||||
.size(Size::Small)
|
||||
.style(ButtonStyle::Primary),
|
||||
)
|
||||
.child(
|
||||
|
|
@ -223,29 +230,29 @@ impl Render for ButtonStory {
|
|||
.child(
|
||||
Button::new("icon-button-4", cx)
|
||||
.icon(IconName::Info)
|
||||
.size(ButtonSize::Small),
|
||||
.size(Size::Small),
|
||||
)
|
||||
.child(
|
||||
Button::new("icon-button-5", cx)
|
||||
.icon(IconName::Close)
|
||||
.size(ButtonSize::Small)
|
||||
.size(Size::Small)
|
||||
.style(ButtonStyle::Danger),
|
||||
)
|
||||
.child(
|
||||
Button::new("icon-button-6", cx)
|
||||
.icon(IconName::Search)
|
||||
.size(ButtonSize::XSmall)
|
||||
.size(Size::XSmall)
|
||||
.style(ButtonStyle::Primary),
|
||||
)
|
||||
.child(
|
||||
Button::new("icon-button-7", cx)
|
||||
.icon(IconName::Info)
|
||||
.size(ButtonSize::XSmall),
|
||||
.size(Size::XSmall),
|
||||
)
|
||||
.child(
|
||||
Button::new("icon-button-8", cx)
|
||||
.icon(IconName::Close)
|
||||
.size(ButtonSize::XSmall)
|
||||
.size(Size::XSmall)
|
||||
.style(ButtonStyle::Danger),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use gpui::{
|
|||
ParentElement as _, Render, Styled as _, View, ViewContext, VisualContext, WindowContext,
|
||||
};
|
||||
use ui::{
|
||||
button::{Button, ButtonSize},
|
||||
button::Button,
|
||||
divider::Divider,
|
||||
h_flex,
|
||||
input::TextInput,
|
||||
|
|
@ -12,7 +12,7 @@ use ui::{
|
|||
popup_menu::PopupMenu,
|
||||
prelude::FluentBuilder,
|
||||
switch::Switch,
|
||||
v_flex, Clickable, IconName,
|
||||
v_flex, Clickable, IconName, Size,
|
||||
};
|
||||
|
||||
actions!(popover_story, [Copy, Paste, Cut, SearchAll]);
|
||||
|
|
@ -141,7 +141,7 @@ impl Render for PopoverStory {
|
|||
Button::new("info1", cx)
|
||||
.label("Yes")
|
||||
.width(px(80.))
|
||||
.size(ButtonSize::Small),
|
||||
.size(Size::Small),
|
||||
)
|
||||
.into_any()
|
||||
})
|
||||
|
|
@ -164,7 +164,7 @@ impl Render for PopoverStory {
|
|||
Button::new("info1", cx)
|
||||
.label("Yes")
|
||||
.width(px(80.))
|
||||
.size(ButtonSize::Small),
|
||||
.size(Size::Small),
|
||||
)
|
||||
.into_any()
|
||||
})
|
||||
|
|
@ -234,7 +234,7 @@ impl Render for PopoverStory {
|
|||
Button::new("info1", cx)
|
||||
.label("Yes")
|
||||
.width(px(80.))
|
||||
.size(ButtonSize::Small),
|
||||
.size(Size::Small),
|
||||
)
|
||||
.into_any()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
use gpui::{
|
||||
div, IntoElement, ParentElement, Render, Styled, View, ViewContext, VisualContext,
|
||||
div, px, IntoElement, ParentElement, Render, Styled, View, ViewContext, VisualContext,
|
||||
WindowContext,
|
||||
};
|
||||
use ui::{button::Button, h_flex, progress::Progress, v_flex, Clickable, IconName};
|
||||
use ui::{
|
||||
button::Button, h_flex, indicator::Indicator, progress::Progress, v_flex, Clickable, IconName,
|
||||
Size,
|
||||
};
|
||||
|
||||
pub struct ProgressStory {
|
||||
value: f32,
|
||||
|
|
@ -74,5 +77,19 @@ impl Render for ProgressStory {
|
|||
})),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_x_2()
|
||||
.child(Indicator::new().size(Size::XSmall))
|
||||
.child(Indicator::new().size(Size::Small))
|
||||
.child(Indicator::new())
|
||||
.child(
|
||||
Indicator::new()
|
||||
.size(Size::Large)
|
||||
.icon(IconName::LoaderCircle)
|
||||
.color(ui::blue_500()),
|
||||
)
|
||||
.child(Indicator::new().size(px(64.))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,11 @@ use gpui::{
|
|||
};
|
||||
|
||||
use ui::{
|
||||
button::ButtonSize,
|
||||
h_flex,
|
||||
label::Label,
|
||||
switch::{LabelSide, Switch},
|
||||
theme::ActiveTheme,
|
||||
v_flex, Disableable as _, StyledExt,
|
||||
v_flex, Disableable as _, Size, StyledExt,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -116,7 +115,7 @@ impl Render for SwitchStory {
|
|||
.items_start().child(title("Disabled Switchs")).child(
|
||||
h_flex().items_center()
|
||||
.gap_6()
|
||||
.child(Switch::new("switch3").checked(self.switch3).label("Small Size").size(ButtonSize::Small).on_click(cx.listener(move |view, checked, cx| {
|
||||
.child(Switch::new("switch3").checked(self.switch3).label("Small Size").size(Size::Small).on_click(cx.listener(move |view, checked, cx| {
|
||||
view.switch3 = *checked;
|
||||
cx.notify();
|
||||
})),
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ usvg = { version = "0.41.0", default-features = false }
|
|||
taffy = "0.4.3"
|
||||
paste = "1"
|
||||
once_cell = "1.19.0"
|
||||
uuid = "1.10.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
h_flex,
|
||||
indicator::Indicator,
|
||||
theme::{ActiveTheme, Colorize as _},
|
||||
Clickable, Disableable, Icon, Selectable, StyledExt,
|
||||
Clickable, Disableable, Icon, Selectable, Size, StyledExt,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, ClickEvent, DefiniteLength, Div, ElementId, FocusHandle,
|
||||
|
|
@ -16,13 +17,6 @@ pub enum ButtonRounded {
|
|||
Large,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ButtonSize {
|
||||
XSmall,
|
||||
Small,
|
||||
Medium,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ButtonStyle {
|
||||
Primary,
|
||||
|
|
@ -45,9 +39,10 @@ pub struct Button {
|
|||
height: Option<DefiniteLength>,
|
||||
style: ButtonStyle,
|
||||
rounded: ButtonRounded,
|
||||
size: ButtonSize,
|
||||
size: Size,
|
||||
tooltip: Option<SharedString>,
|
||||
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
||||
loading: bool,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
|
|
@ -64,9 +59,10 @@ impl Button {
|
|||
width: None,
|
||||
height: None,
|
||||
rounded: ButtonRounded::Medium,
|
||||
size: ButtonSize::Medium,
|
||||
size: Size::Medium,
|
||||
tooltip: None,
|
||||
on_click: None,
|
||||
loading: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +87,7 @@ impl Button {
|
|||
label: impl Into<SharedString>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Self {
|
||||
Self::new(id, cx).label(label).size(ButtonSize::Small)
|
||||
Self::new(id, cx).label(label).size(Size::Small)
|
||||
}
|
||||
|
||||
pub fn width(mut self, width: impl Into<DefiniteLength>) -> Self {
|
||||
|
|
@ -109,8 +105,8 @@ impl Button {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: ButtonSize) -> Self {
|
||||
self.size = size;
|
||||
pub fn size(mut self, size: impl Into<Size>) -> Self {
|
||||
self.size = size.into();
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -134,6 +130,11 @@ impl Button {
|
|||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn loading(mut self, loading: bool) -> Self {
|
||||
self.loading = loading;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for Button {
|
||||
|
|
@ -180,15 +181,16 @@ impl RenderOnce for Button {
|
|||
.map(|this| {
|
||||
if self.label.is_none() {
|
||||
match self.size {
|
||||
ButtonSize::XSmall => this.size_5(),
|
||||
ButtonSize::Small => this.size_6(),
|
||||
ButtonSize::Medium => this.size_8(),
|
||||
Size::Size(px) => this.size(px),
|
||||
Size::XSmall => this.size_5(),
|
||||
Size::Small => this.size_6(),
|
||||
Size::Large | Size::Medium => this.size_8(),
|
||||
}
|
||||
} else {
|
||||
match self.size {
|
||||
ButtonSize::XSmall => this.px_1().py_1().h_5(),
|
||||
ButtonSize::Small => this.px_3().py_2().h_6(),
|
||||
ButtonSize::Medium => this.px_4().py_2().h_8(),
|
||||
Size::XSmall => this.px_1().py_1().h_5(),
|
||||
Size::Small => this.px_3().py_2().h_6(),
|
||||
_ => this.px_4().py_2().h_8(),
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -247,14 +249,19 @@ impl RenderOnce for Button {
|
|||
.justify_center()
|
||||
.gap_2()
|
||||
.text_color(text_color)
|
||||
.when_some(self.icon, |this, icon| {
|
||||
this.child(icon.size(self.size).text_color(text_color))
|
||||
.when(!self.loading, |this| {
|
||||
this.when_some(self.icon, |this, icon| {
|
||||
this.child(icon.size(self.size).text_color(text_color))
|
||||
})
|
||||
})
|
||||
.when(self.loading, |this| {
|
||||
this.child(Indicator::new().size(self.size).color(text_color))
|
||||
})
|
||||
.when_some(self.label, |this, label| this.child(label))
|
||||
.map(|this| match self.size {
|
||||
ButtonSize::XSmall => this.text_xs(),
|
||||
ButtonSize::Small => this.text_sm(),
|
||||
ButtonSize::Medium => this.text_base(),
|
||||
Size::XSmall => this.text_xs(),
|
||||
Size::Small => this.text_sm(),
|
||||
_ => this.text_base(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{button::ButtonSize, theme::ActiveTheme};
|
||||
use crate::{theme::ActiveTheme, Size};
|
||||
use gpui::{
|
||||
prelude::FluentBuilder as _, svg, AnyElement, Hsla, IntoElement, Pixels, Render, RenderOnce,
|
||||
prelude::FluentBuilder as _, svg, AnyElement, Hsla, IntoElement, Render, RenderOnce,
|
||||
SharedString, StyleRefinement, Styled, Svg, View, VisualContext, WindowContext,
|
||||
};
|
||||
|
||||
|
|
@ -21,6 +21,8 @@ pub enum IconName {
|
|||
Search,
|
||||
Delete,
|
||||
CircleX,
|
||||
Loader,
|
||||
LoaderCircle,
|
||||
}
|
||||
|
||||
impl IconName {
|
||||
|
|
@ -41,6 +43,8 @@ impl IconName {
|
|||
IconName::Search => "icons/search.svg",
|
||||
IconName::Delete => "icons/delete.svg",
|
||||
IconName::CircleX => "icons/circle-x.svg",
|
||||
IconName::Loader => "icons/loader.svg",
|
||||
IconName::LoaderCircle => "icons/loader-circle.svg",
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
|
@ -69,36 +73,12 @@ impl RenderOnce for IconName {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum IconSize {
|
||||
XSmall,
|
||||
Small,
|
||||
Medium,
|
||||
Size(Pixels),
|
||||
}
|
||||
|
||||
impl From<ButtonSize> for IconSize {
|
||||
fn from(size: ButtonSize) -> Self {
|
||||
match size {
|
||||
ButtonSize::XSmall => IconSize::XSmall,
|
||||
ButtonSize::Small => IconSize::Small,
|
||||
ButtonSize::Medium => IconSize::Medium,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pixels> for IconSize {
|
||||
fn from(size: Pixels) -> Self {
|
||||
IconSize::Size(size)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Icon {
|
||||
base: Svg,
|
||||
path: SharedString,
|
||||
text_color: Option<Hsla>,
|
||||
size: IconSize,
|
||||
size: Size,
|
||||
}
|
||||
|
||||
impl Default for Icon {
|
||||
|
|
@ -107,7 +87,7 @@ impl Default for Icon {
|
|||
base: svg().flex_none().size_4(),
|
||||
path: "".into(),
|
||||
text_color: None,
|
||||
size: IconSize::Medium,
|
||||
size: Size::Medium,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -135,7 +115,7 @@ impl Icon {
|
|||
///
|
||||
/// Also can receive a `ButtonSize` to convert to `IconSize`,
|
||||
/// Or a `Pixels` to set a custom size: `px(30.)`
|
||||
pub fn size(mut self, size: impl Into<IconSize>) -> Self {
|
||||
pub fn size(mut self, size: impl Into<Size>) -> Self {
|
||||
self.size = size.into();
|
||||
|
||||
self
|
||||
|
|
@ -145,6 +125,11 @@ impl Icon {
|
|||
pub fn view(self, cx: &mut WindowContext) -> View<Icon> {
|
||||
cx.new_view(|_| self)
|
||||
}
|
||||
|
||||
pub fn transform(mut self, transformation: gpui::Transformation) -> Self {
|
||||
self.base = self.base.with_transformation(transformation);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Styled for Icon {
|
||||
|
|
@ -165,10 +150,11 @@ impl RenderOnce for Icon {
|
|||
self.base
|
||||
.text_color(text_color)
|
||||
.map(|this| match self.size {
|
||||
IconSize::Size(px) => this.size(px),
|
||||
IconSize::XSmall => this.size_3(),
|
||||
IconSize::Small => this.size_3p5(),
|
||||
IconSize::Medium => this.size_4(),
|
||||
Size::Size(px) => this.size(px),
|
||||
Size::XSmall => this.size_3(),
|
||||
Size::Small => this.size_3p5(),
|
||||
Size::Medium => this.size_4(),
|
||||
Size::Large => this.size_6(),
|
||||
})
|
||||
.path(self.path)
|
||||
}
|
||||
|
|
@ -189,10 +175,11 @@ impl Render for Icon {
|
|||
.size_4()
|
||||
.text_color(text_color)
|
||||
.map(|this| match self.size {
|
||||
IconSize::Size(px) => this.size(px),
|
||||
IconSize::XSmall => this.size_3(),
|
||||
IconSize::Small => this.size_3p5(),
|
||||
IconSize::Medium => this.size_4(),
|
||||
Size::Size(px) => this.size(px),
|
||||
Size::XSmall => this.size_3(),
|
||||
Size::Small => this.size_3p5(),
|
||||
Size::Medium => this.size_4(),
|
||||
Size::Large => this.size_6(),
|
||||
})
|
||||
.path(self.path.clone())
|
||||
}
|
||||
|
|
|
|||
59
crates/ui/src/indicator.rs
Normal file
59
crates/ui/src/indicator.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use crate::{theme::ActiveTheme, Icon, IconName, Size};
|
||||
use gpui::{
|
||||
div, ease_in_out, percentage, Animation, AnimationExt as _, Hsla, IntoElement, ParentElement,
|
||||
RenderOnce, Styled as _, Transformation,
|
||||
};
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Indicator {
|
||||
size: Size,
|
||||
icon: IconName,
|
||||
speed: Duration,
|
||||
color: Option<Hsla>,
|
||||
}
|
||||
|
||||
impl Indicator {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
size: Size::Medium,
|
||||
speed: Duration::from_secs_f64(0.8),
|
||||
icon: IconName::Loader,
|
||||
color: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: impl Into<Size>) -> Self {
|
||||
self.size = size.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon(mut self, icon: IconName) -> Self {
|
||||
self.icon = icon;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: Hsla) -> Self {
|
||||
self.color = Some(color);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Indicator {
|
||||
fn render(self, cx: &mut gpui::WindowContext) -> impl IntoElement {
|
||||
let color = self.color.unwrap_or_else(|| cx.theme().indicator);
|
||||
div()
|
||||
.child(
|
||||
Icon::new(self.icon)
|
||||
.size(self.size)
|
||||
.text_color(color)
|
||||
.with_animation(
|
||||
"circle",
|
||||
Animation::new(self.speed).repeat().with_easing(ease_in_out),
|
||||
|this, delta| this.transform(Transformation::rotate(percentage(delta))),
|
||||
),
|
||||
)
|
||||
.into_element()
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ mod disableable;
|
|||
mod event;
|
||||
mod focusable;
|
||||
mod icon;
|
||||
|
||||
mod scroll;
|
||||
mod scrollbar;
|
||||
mod selectable;
|
||||
|
|
@ -15,6 +16,7 @@ pub mod button;
|
|||
pub mod checkbox;
|
||||
pub mod divider;
|
||||
pub mod dropdown;
|
||||
pub mod indicator;
|
||||
pub mod input;
|
||||
pub mod label;
|
||||
pub mod list;
|
||||
|
|
@ -35,7 +37,7 @@ pub use disableable::Disableable;
|
|||
pub use event::InterativeElementExt;
|
||||
pub use focusable::FocusableCycle;
|
||||
pub use selectable::{Selectable, Selection};
|
||||
pub use styled_ext::StyledExt;
|
||||
pub use styled_ext::{Size, StyledExt};
|
||||
|
||||
pub use colors::*;
|
||||
pub use icon::*;
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ impl RenderOnce for Progress {
|
|||
})
|
||||
.h_full()
|
||||
.w(relative_w)
|
||||
.bg(cx.theme().primary),
|
||||
.bg(cx.theme().progress_bar),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
|
|
@ -57,7 +57,7 @@ impl RenderOnce for Progress {
|
|||
})
|
||||
.h_full()
|
||||
.flex_1()
|
||||
.bg(cx.theme().primary.opacity(0.2)),
|
||||
.bg(cx.theme().progress_bar.opacity(0.2)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::theme::ActiveTheme;
|
||||
use gpui::{hsla, point, px, BoxShadow, FocusHandle, Styled, WindowContext};
|
||||
use gpui::{hsla, point, px, BoxShadow, FocusHandle, Pixels, Styled, WindowContext};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
pub enum ElevationIndex {
|
||||
|
|
@ -133,3 +133,19 @@ pub trait StyledExt: Styled + Sized {
|
|||
}
|
||||
|
||||
impl<E: Styled> StyledExt for E {}
|
||||
|
||||
/// A size for elements.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Size {
|
||||
Size(Pixels),
|
||||
XSmall,
|
||||
Small,
|
||||
Medium,
|
||||
Large,
|
||||
}
|
||||
|
||||
impl From<Pixels> for Size {
|
||||
fn from(size: Pixels) -> Self {
|
||||
Size::Size(size)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use crate::{
|
||||
button::ButtonSize,
|
||||
stack::h_flex,
|
||||
theme::{ActiveTheme, Colorize},
|
||||
Disableable,
|
||||
Disableable, Size,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, Div, InteractiveElement, IntoElement, ParentElement as _,
|
||||
|
|
@ -31,7 +30,7 @@ pub struct Switch {
|
|||
label: Option<SharedString>,
|
||||
label_side: LabelSide,
|
||||
on_click: Option<OnClick>,
|
||||
size: ButtonSize,
|
||||
size: Size,
|
||||
}
|
||||
|
||||
impl Switch {
|
||||
|
|
@ -46,7 +45,7 @@ impl Switch {
|
|||
label: None,
|
||||
on_click: None,
|
||||
label_side: LabelSide::Right,
|
||||
size: ButtonSize::Medium,
|
||||
size: Size::Medium,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +59,8 @@ impl Switch {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: ButtonSize) -> Self {
|
||||
/// Only supported XSmall, Small, Medium
|
||||
pub fn size(mut self, size: Size) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
|
@ -107,8 +107,8 @@ impl RenderOnce for Switch {
|
|||
.child(
|
||||
self.base
|
||||
.map(|this| match self.size {
|
||||
ButtonSize::Medium => this.w_11().h_6().rounded_xl(),
|
||||
ButtonSize::XSmall | ButtonSize::Small => this.w_8().h_4().rounded_lg(),
|
||||
Size::XSmall | Size::Small => this.w_8().h_4().rounded_lg(),
|
||||
_ => this.w_11().h_6().rounded_xl(),
|
||||
})
|
||||
.flex()
|
||||
.items_center()
|
||||
|
|
@ -125,15 +125,15 @@ impl RenderOnce for Switch {
|
|||
.rounded_full()
|
||||
.bg(toggle_bg)
|
||||
.map(|this| match self.size {
|
||||
ButtonSize::Medium => this.w_5().h_5(),
|
||||
ButtonSize::XSmall | ButtonSize::Small => this.w_3().h_3(),
|
||||
Size::XSmall | Size::Small => this.w_3().h_3(),
|
||||
_ => this.w_5().h_5(),
|
||||
}),
|
||||
),
|
||||
)
|
||||
.when_some(self.label, |this, label| {
|
||||
this.child(div().child(label).map(|this| match self.size {
|
||||
ButtonSize::Medium => this.text_base(),
|
||||
ButtonSize::XSmall | ButtonSize::Small => this.text_sm(),
|
||||
Size::XSmall | Size::Small => this.text_sm(),
|
||||
_ => this.text_base(),
|
||||
}))
|
||||
})
|
||||
.when_some(
|
||||
|
|
|
|||
|
|
@ -301,6 +301,8 @@ pub struct Theme {
|
|||
pub tab_active: Hsla,
|
||||
pub tab_foreground: Hsla,
|
||||
pub tab_active_foreground: Hsla,
|
||||
pub indicator: Hsla,
|
||||
pub progress_bar: Hsla,
|
||||
}
|
||||
|
||||
impl Global for Theme {}
|
||||
|
|
@ -355,6 +357,8 @@ impl From<Colors> for Theme {
|
|||
tab_active: colors.background,
|
||||
tab_foreground: colors.foreground,
|
||||
tab_active_foreground: colors.foreground,
|
||||
indicator: colors.secondary_foreground,
|
||||
progress_bar: colors.primary,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue