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",
|
"taffy",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"usvg",
|
"usvg",
|
||||||
"uuid",
|
|
||||||
"windows",
|
"windows",
|
||||||
"wry",
|
"wry",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ This is an example of build app by using GPUI.
|
||||||
- [x] Button with Icon
|
- [x] Button with Icon
|
||||||
- [x] IconButton
|
- [x] IconButton
|
||||||
- [x] Glost / Outline Button
|
- [x] Glost / Outline Button
|
||||||
|
- [x] Loading
|
||||||
- [x] Label
|
- [x] Label
|
||||||
- [x] Icon
|
- [x] Icon
|
||||||
- [x] Checkbox
|
- [x] Checkbox
|
||||||
|
|
@ -58,8 +59,8 @@ This is an example of build app by using GPUI.
|
||||||
- [x] Dockpanel
|
- [x] Dockpanel
|
||||||
- [x] Resizable
|
- [x] Resizable
|
||||||
- [x] Progress
|
- [x] Progress
|
||||||
- [ ] ProgressBar
|
- [x] ProgressBar
|
||||||
- [ ] Loading
|
- [x] Indicator
|
||||||
- [ ] Skeleton
|
- [ ] Skeleton
|
||||||
- [ ] DatePicker
|
- [ ] DatePicker
|
||||||
- [ ] DateTimePicker
|
- [ ] 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 std::sync::Arc;
|
||||||
use ui::{
|
use ui::{
|
||||||
button::ButtonSize,
|
|
||||||
switch::{LabelSide, Switch},
|
switch::{LabelSide, Switch},
|
||||||
theme::{ActiveTheme, Theme},
|
theme::{ActiveTheme, Theme},
|
||||||
|
Size,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::app_state::AppState;
|
use crate::app_state::AppState;
|
||||||
|
|
@ -173,7 +173,7 @@ impl StoryWorkspace {
|
||||||
appears_transparent: true,
|
appears_transparent: true,
|
||||||
traffic_light_position: Some(point(px(9.0), px(9.0))),
|
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.),
|
width: px(640.),
|
||||||
height: px(480.),
|
height: px(480.),
|
||||||
}),
|
}),
|
||||||
|
|
@ -255,7 +255,7 @@ impl Render for StoryWorkspace {
|
||||||
.mr_3()
|
.mr_3()
|
||||||
.child(
|
.child(
|
||||||
Switch::new("theme-mode")
|
Switch::new("theme-mode")
|
||||||
.size(ButtonSize::Small)
|
.size(Size::Small)
|
||||||
.checked(cx.theme().mode.is_dark())
|
.checked(cx.theme().mode.is_dark())
|
||||||
.label_side(LabelSide::Left)
|
.label_side(LabelSide::Left)
|
||||||
.label("Dark Mode")
|
.label("Dark Mode")
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ use gpui::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use ui::{
|
use ui::{
|
||||||
button::{Button, ButtonSize, ButtonStyle},
|
button::{Button, ButtonStyle},
|
||||||
h_flex, v_flex, Clickable, Disableable as _, Icon, IconName, Selectable,
|
h_flex, v_flex, Clickable, Disableable as _, Icon, IconName, Selectable, Size,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::section;
|
use crate::section;
|
||||||
|
|
@ -96,21 +96,22 @@ impl Render for ButtonStory {
|
||||||
Button::new("button-6", cx)
|
Button::new("button-6", cx)
|
||||||
.label("Primary Button")
|
.label("Primary Button")
|
||||||
.style(ButtonStyle::Primary)
|
.style(ButtonStyle::Primary)
|
||||||
.size(ButtonSize::Small)
|
.size(Size::Small)
|
||||||
|
.loading(true)
|
||||||
.on_click(Self::on_click),
|
.on_click(Self::on_click),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("button-7", cx)
|
Button::new("button-7", cx)
|
||||||
.label("Secondary Button")
|
.label("Secondary Button")
|
||||||
.style(ButtonStyle::Secondary)
|
.style(ButtonStyle::Secondary)
|
||||||
.size(ButtonSize::Small)
|
.size(Size::Small)
|
||||||
.on_click(Self::on_click),
|
.on_click(Self::on_click),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("button-8", cx)
|
Button::new("button-8", cx)
|
||||||
.label("Danger Button")
|
.label("Danger Button")
|
||||||
.style(ButtonStyle::Danger)
|
.style(ButtonStyle::Danger)
|
||||||
.size(ButtonSize::Small)
|
.size(Size::Small)
|
||||||
.on_click(Self::on_click),
|
.on_click(Self::on_click),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -120,21 +121,22 @@ impl Render for ButtonStory {
|
||||||
Button::new("button-xs-1", cx)
|
Button::new("button-xs-1", cx)
|
||||||
.label("Primary Button")
|
.label("Primary Button")
|
||||||
.style(ButtonStyle::Primary)
|
.style(ButtonStyle::Primary)
|
||||||
.size(ButtonSize::XSmall)
|
.size(Size::XSmall)
|
||||||
.on_click(Self::on_click),
|
.on_click(Self::on_click),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("button-xs-2", cx)
|
Button::new("button-xs-2", cx)
|
||||||
.label("Secondary Button")
|
.label("Secondary Button")
|
||||||
.style(ButtonStyle::Secondary)
|
.style(ButtonStyle::Secondary)
|
||||||
.size(ButtonSize::XSmall)
|
.size(Size::XSmall)
|
||||||
|
.loading(true)
|
||||||
.on_click(Self::on_click),
|
.on_click(Self::on_click),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("button-xs-3", cx)
|
Button::new("button-xs-3", cx)
|
||||||
.label("Danger Button")
|
.label("Danger Button")
|
||||||
.style(ButtonStyle::Danger)
|
.style(ButtonStyle::Danger)
|
||||||
.size(ButtonSize::XSmall)
|
.size(Size::XSmall)
|
||||||
.on_click(Self::on_click),
|
.on_click(Self::on_click),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -163,7 +165,8 @@ impl Render for ButtonStory {
|
||||||
.label("Disabled Button")
|
.label("Disabled Button")
|
||||||
.style(ButtonStyle::Danger)
|
.style(ButtonStyle::Danger)
|
||||||
.on_click(Self::on_click)
|
.on_click(Self::on_click)
|
||||||
.disabled(true),
|
.disabled(true)
|
||||||
|
.loading(true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
|
@ -195,7 +198,11 @@ impl Render for ButtonStory {
|
||||||
.icon(IconName::Search)
|
.icon(IconName::Search)
|
||||||
.style(ButtonStyle::Primary),
|
.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(
|
.child(
|
||||||
Button::new("icon-button-2", cx)
|
Button::new("icon-button-2", cx)
|
||||||
.icon(IconName::Close)
|
.icon(IconName::Close)
|
||||||
|
|
@ -204,7 +211,7 @@ impl Render for ButtonStory {
|
||||||
.child(
|
.child(
|
||||||
Button::new("icon-button-3", cx)
|
Button::new("icon-button-3", cx)
|
||||||
.icon(IconName::Search)
|
.icon(IconName::Search)
|
||||||
.size(ButtonSize::Small)
|
.size(Size::Small)
|
||||||
.style(ButtonStyle::Primary),
|
.style(ButtonStyle::Primary),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
|
@ -223,29 +230,29 @@ impl Render for ButtonStory {
|
||||||
.child(
|
.child(
|
||||||
Button::new("icon-button-4", cx)
|
Button::new("icon-button-4", cx)
|
||||||
.icon(IconName::Info)
|
.icon(IconName::Info)
|
||||||
.size(ButtonSize::Small),
|
.size(Size::Small),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("icon-button-5", cx)
|
Button::new("icon-button-5", cx)
|
||||||
.icon(IconName::Close)
|
.icon(IconName::Close)
|
||||||
.size(ButtonSize::Small)
|
.size(Size::Small)
|
||||||
.style(ButtonStyle::Danger),
|
.style(ButtonStyle::Danger),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("icon-button-6", cx)
|
Button::new("icon-button-6", cx)
|
||||||
.icon(IconName::Search)
|
.icon(IconName::Search)
|
||||||
.size(ButtonSize::XSmall)
|
.size(Size::XSmall)
|
||||||
.style(ButtonStyle::Primary),
|
.style(ButtonStyle::Primary),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("icon-button-7", cx)
|
Button::new("icon-button-7", cx)
|
||||||
.icon(IconName::Info)
|
.icon(IconName::Info)
|
||||||
.size(ButtonSize::XSmall),
|
.size(Size::XSmall),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("icon-button-8", cx)
|
Button::new("icon-button-8", cx)
|
||||||
.icon(IconName::Close)
|
.icon(IconName::Close)
|
||||||
.size(ButtonSize::XSmall)
|
.size(Size::XSmall)
|
||||||
.style(ButtonStyle::Danger),
|
.style(ButtonStyle::Danger),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use gpui::{
|
||||||
ParentElement as _, Render, Styled as _, View, ViewContext, VisualContext, WindowContext,
|
ParentElement as _, Render, Styled as _, View, ViewContext, VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
use ui::{
|
use ui::{
|
||||||
button::{Button, ButtonSize},
|
button::Button,
|
||||||
divider::Divider,
|
divider::Divider,
|
||||||
h_flex,
|
h_flex,
|
||||||
input::TextInput,
|
input::TextInput,
|
||||||
|
|
@ -12,7 +12,7 @@ use ui::{
|
||||||
popup_menu::PopupMenu,
|
popup_menu::PopupMenu,
|
||||||
prelude::FluentBuilder,
|
prelude::FluentBuilder,
|
||||||
switch::Switch,
|
switch::Switch,
|
||||||
v_flex, Clickable, IconName,
|
v_flex, Clickable, IconName, Size,
|
||||||
};
|
};
|
||||||
|
|
||||||
actions!(popover_story, [Copy, Paste, Cut, SearchAll]);
|
actions!(popover_story, [Copy, Paste, Cut, SearchAll]);
|
||||||
|
|
@ -141,7 +141,7 @@ impl Render for PopoverStory {
|
||||||
Button::new("info1", cx)
|
Button::new("info1", cx)
|
||||||
.label("Yes")
|
.label("Yes")
|
||||||
.width(px(80.))
|
.width(px(80.))
|
||||||
.size(ButtonSize::Small),
|
.size(Size::Small),
|
||||||
)
|
)
|
||||||
.into_any()
|
.into_any()
|
||||||
})
|
})
|
||||||
|
|
@ -164,7 +164,7 @@ impl Render for PopoverStory {
|
||||||
Button::new("info1", cx)
|
Button::new("info1", cx)
|
||||||
.label("Yes")
|
.label("Yes")
|
||||||
.width(px(80.))
|
.width(px(80.))
|
||||||
.size(ButtonSize::Small),
|
.size(Size::Small),
|
||||||
)
|
)
|
||||||
.into_any()
|
.into_any()
|
||||||
})
|
})
|
||||||
|
|
@ -234,7 +234,7 @@ impl Render for PopoverStory {
|
||||||
Button::new("info1", cx)
|
Button::new("info1", cx)
|
||||||
.label("Yes")
|
.label("Yes")
|
||||||
.width(px(80.))
|
.width(px(80.))
|
||||||
.size(ButtonSize::Small),
|
.size(Size::Small),
|
||||||
)
|
)
|
||||||
.into_any()
|
.into_any()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, IntoElement, ParentElement, Render, Styled, View, ViewContext, VisualContext,
|
div, px, IntoElement, ParentElement, Render, Styled, View, ViewContext, VisualContext,
|
||||||
WindowContext,
|
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 {
|
pub struct ProgressStory {
|
||||||
value: f32,
|
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::{
|
use ui::{
|
||||||
button::ButtonSize,
|
|
||||||
h_flex,
|
h_flex,
|
||||||
label::Label,
|
label::Label,
|
||||||
switch::{LabelSide, Switch},
|
switch::{LabelSide, Switch},
|
||||||
theme::ActiveTheme,
|
theme::ActiveTheme,
|
||||||
v_flex, Disableable as _, StyledExt,
|
v_flex, Disableable as _, Size, StyledExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -116,7 +115,7 @@ impl Render for SwitchStory {
|
||||||
.items_start().child(title("Disabled Switchs")).child(
|
.items_start().child(title("Disabled Switchs")).child(
|
||||||
h_flex().items_center()
|
h_flex().items_center()
|
||||||
.gap_6()
|
.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;
|
view.switch3 = *checked;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})),
|
})),
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ usvg = { version = "0.41.0", default-features = false }
|
||||||
taffy = "0.4.3"
|
taffy = "0.4.3"
|
||||||
paste = "1"
|
paste = "1"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
uuid = "1.10.0"
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
h_flex,
|
h_flex,
|
||||||
|
indicator::Indicator,
|
||||||
theme::{ActiveTheme, Colorize as _},
|
theme::{ActiveTheme, Colorize as _},
|
||||||
Clickable, Disableable, Icon, Selectable, StyledExt,
|
Clickable, Disableable, Icon, Selectable, Size, StyledExt,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, prelude::FluentBuilder as _, px, ClickEvent, DefiniteLength, Div, ElementId, FocusHandle,
|
div, prelude::FluentBuilder as _, px, ClickEvent, DefiniteLength, Div, ElementId, FocusHandle,
|
||||||
|
|
@ -16,13 +17,6 @@ pub enum ButtonRounded {
|
||||||
Large,
|
Large,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ButtonSize {
|
|
||||||
XSmall,
|
|
||||||
Small,
|
|
||||||
Medium,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum ButtonStyle {
|
pub enum ButtonStyle {
|
||||||
Primary,
|
Primary,
|
||||||
|
|
@ -45,9 +39,10 @@ pub struct Button {
|
||||||
height: Option<DefiniteLength>,
|
height: Option<DefiniteLength>,
|
||||||
style: ButtonStyle,
|
style: ButtonStyle,
|
||||||
rounded: ButtonRounded,
|
rounded: ButtonRounded,
|
||||||
size: ButtonSize,
|
size: Size,
|
||||||
tooltip: Option<SharedString>,
|
tooltip: Option<SharedString>,
|
||||||
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
||||||
|
loading: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Button {
|
impl Button {
|
||||||
|
|
@ -64,9 +59,10 @@ impl Button {
|
||||||
width: None,
|
width: None,
|
||||||
height: None,
|
height: None,
|
||||||
rounded: ButtonRounded::Medium,
|
rounded: ButtonRounded::Medium,
|
||||||
size: ButtonSize::Medium,
|
size: Size::Medium,
|
||||||
tooltip: None,
|
tooltip: None,
|
||||||
on_click: None,
|
on_click: None,
|
||||||
|
loading: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,7 +87,7 @@ impl Button {
|
||||||
label: impl Into<SharedString>,
|
label: impl Into<SharedString>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Self {
|
) -> 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 {
|
pub fn width(mut self, width: impl Into<DefiniteLength>) -> Self {
|
||||||
|
|
@ -109,8 +105,8 @@ impl Button {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(mut self, size: ButtonSize) -> Self {
|
pub fn size(mut self, size: impl Into<Size>) -> Self {
|
||||||
self.size = size;
|
self.size = size.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,6 +130,11 @@ impl Button {
|
||||||
self.style = style;
|
self.style = style;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn loading(mut self, loading: bool) -> Self {
|
||||||
|
self.loading = loading;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Disableable for Button {
|
impl Disableable for Button {
|
||||||
|
|
@ -180,15 +181,16 @@ impl RenderOnce for Button {
|
||||||
.map(|this| {
|
.map(|this| {
|
||||||
if self.label.is_none() {
|
if self.label.is_none() {
|
||||||
match self.size {
|
match self.size {
|
||||||
ButtonSize::XSmall => this.size_5(),
|
Size::Size(px) => this.size(px),
|
||||||
ButtonSize::Small => this.size_6(),
|
Size::XSmall => this.size_5(),
|
||||||
ButtonSize::Medium => this.size_8(),
|
Size::Small => this.size_6(),
|
||||||
|
Size::Large | Size::Medium => this.size_8(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match self.size {
|
match self.size {
|
||||||
ButtonSize::XSmall => this.px_1().py_1().h_5(),
|
Size::XSmall => this.px_1().py_1().h_5(),
|
||||||
ButtonSize::Small => this.px_3().py_2().h_6(),
|
Size::Small => this.px_3().py_2().h_6(),
|
||||||
ButtonSize::Medium => this.px_4().py_2().h_8(),
|
_ => this.px_4().py_2().h_8(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -247,14 +249,19 @@ impl RenderOnce for Button {
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.text_color(text_color)
|
.text_color(text_color)
|
||||||
.when_some(self.icon, |this, icon| {
|
.when(!self.loading, |this| {
|
||||||
this.child(icon.size(self.size).text_color(text_color))
|
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))
|
.when_some(self.label, |this, label| this.child(label))
|
||||||
.map(|this| match self.size {
|
.map(|this| match self.size {
|
||||||
ButtonSize::XSmall => this.text_xs(),
|
Size::XSmall => this.text_xs(),
|
||||||
ButtonSize::Small => this.text_sm(),
|
Size::Small => this.text_sm(),
|
||||||
ButtonSize::Medium => this.text_base(),
|
_ => this.text_base(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{button::ButtonSize, theme::ActiveTheme};
|
use crate::{theme::ActiveTheme, Size};
|
||||||
use gpui::{
|
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,
|
SharedString, StyleRefinement, Styled, Svg, View, VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -21,6 +21,8 @@ pub enum IconName {
|
||||||
Search,
|
Search,
|
||||||
Delete,
|
Delete,
|
||||||
CircleX,
|
CircleX,
|
||||||
|
Loader,
|
||||||
|
LoaderCircle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IconName {
|
impl IconName {
|
||||||
|
|
@ -41,6 +43,8 @@ impl IconName {
|
||||||
IconName::Search => "icons/search.svg",
|
IconName::Search => "icons/search.svg",
|
||||||
IconName::Delete => "icons/delete.svg",
|
IconName::Delete => "icons/delete.svg",
|
||||||
IconName::CircleX => "icons/circle-x.svg",
|
IconName::CircleX => "icons/circle-x.svg",
|
||||||
|
IconName::Loader => "icons/loader.svg",
|
||||||
|
IconName::LoaderCircle => "icons/loader-circle.svg",
|
||||||
}
|
}
|
||||||
.into()
|
.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)]
|
#[derive(IntoElement)]
|
||||||
pub struct Icon {
|
pub struct Icon {
|
||||||
base: Svg,
|
base: Svg,
|
||||||
path: SharedString,
|
path: SharedString,
|
||||||
text_color: Option<Hsla>,
|
text_color: Option<Hsla>,
|
||||||
size: IconSize,
|
size: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Icon {
|
impl Default for Icon {
|
||||||
|
|
@ -107,7 +87,7 @@ impl Default for Icon {
|
||||||
base: svg().flex_none().size_4(),
|
base: svg().flex_none().size_4(),
|
||||||
path: "".into(),
|
path: "".into(),
|
||||||
text_color: None,
|
text_color: None,
|
||||||
size: IconSize::Medium,
|
size: Size::Medium,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +115,7 @@ impl Icon {
|
||||||
///
|
///
|
||||||
/// Also can receive a `ButtonSize` to convert to `IconSize`,
|
/// Also can receive a `ButtonSize` to convert to `IconSize`,
|
||||||
/// Or a `Pixels` to set a custom size: `px(30.)`
|
/// 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.size = size.into();
|
||||||
|
|
||||||
self
|
self
|
||||||
|
|
@ -145,6 +125,11 @@ impl Icon {
|
||||||
pub fn view(self, cx: &mut WindowContext) -> View<Icon> {
|
pub fn view(self, cx: &mut WindowContext) -> View<Icon> {
|
||||||
cx.new_view(|_| self)
|
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 {
|
impl Styled for Icon {
|
||||||
|
|
@ -165,10 +150,11 @@ impl RenderOnce for Icon {
|
||||||
self.base
|
self.base
|
||||||
.text_color(text_color)
|
.text_color(text_color)
|
||||||
.map(|this| match self.size {
|
.map(|this| match self.size {
|
||||||
IconSize::Size(px) => this.size(px),
|
Size::Size(px) => this.size(px),
|
||||||
IconSize::XSmall => this.size_3(),
|
Size::XSmall => this.size_3(),
|
||||||
IconSize::Small => this.size_3p5(),
|
Size::Small => this.size_3p5(),
|
||||||
IconSize::Medium => this.size_4(),
|
Size::Medium => this.size_4(),
|
||||||
|
Size::Large => this.size_6(),
|
||||||
})
|
})
|
||||||
.path(self.path)
|
.path(self.path)
|
||||||
}
|
}
|
||||||
|
|
@ -189,10 +175,11 @@ impl Render for Icon {
|
||||||
.size_4()
|
.size_4()
|
||||||
.text_color(text_color)
|
.text_color(text_color)
|
||||||
.map(|this| match self.size {
|
.map(|this| match self.size {
|
||||||
IconSize::Size(px) => this.size(px),
|
Size::Size(px) => this.size(px),
|
||||||
IconSize::XSmall => this.size_3(),
|
Size::XSmall => this.size_3(),
|
||||||
IconSize::Small => this.size_3p5(),
|
Size::Small => this.size_3p5(),
|
||||||
IconSize::Medium => this.size_4(),
|
Size::Medium => this.size_4(),
|
||||||
|
Size::Large => this.size_6(),
|
||||||
})
|
})
|
||||||
.path(self.path.clone())
|
.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 event;
|
||||||
mod focusable;
|
mod focusable;
|
||||||
mod icon;
|
mod icon;
|
||||||
|
|
||||||
mod scroll;
|
mod scroll;
|
||||||
mod scrollbar;
|
mod scrollbar;
|
||||||
mod selectable;
|
mod selectable;
|
||||||
|
|
@ -15,6 +16,7 @@ pub mod button;
|
||||||
pub mod checkbox;
|
pub mod checkbox;
|
||||||
pub mod divider;
|
pub mod divider;
|
||||||
pub mod dropdown;
|
pub mod dropdown;
|
||||||
|
pub mod indicator;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod label;
|
pub mod label;
|
||||||
pub mod list;
|
pub mod list;
|
||||||
|
|
@ -35,7 +37,7 @@ pub use disableable::Disableable;
|
||||||
pub use event::InterativeElementExt;
|
pub use event::InterativeElementExt;
|
||||||
pub use focusable::FocusableCycle;
|
pub use focusable::FocusableCycle;
|
||||||
pub use selectable::{Selectable, Selection};
|
pub use selectable::{Selectable, Selection};
|
||||||
pub use styled_ext::StyledExt;
|
pub use styled_ext::{Size, StyledExt};
|
||||||
|
|
||||||
pub use colors::*;
|
pub use colors::*;
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ impl RenderOnce for Progress {
|
||||||
})
|
})
|
||||||
.h_full()
|
.h_full()
|
||||||
.w(relative_w)
|
.w(relative_w)
|
||||||
.bg(cx.theme().primary),
|
.bg(cx.theme().progress_bar),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
|
@ -57,7 +57,7 @@ impl RenderOnce for Progress {
|
||||||
})
|
})
|
||||||
.h_full()
|
.h_full()
|
||||||
.flex_1()
|
.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 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};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
pub enum ElevationIndex {
|
pub enum ElevationIndex {
|
||||||
|
|
@ -133,3 +133,19 @@ pub trait StyledExt: Styled + Sized {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Styled> StyledExt for E {}
|
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::{
|
use crate::{
|
||||||
button::ButtonSize,
|
|
||||||
stack::h_flex,
|
stack::h_flex,
|
||||||
theme::{ActiveTheme, Colorize},
|
theme::{ActiveTheme, Colorize},
|
||||||
Disableable,
|
Disableable, Size,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, prelude::FluentBuilder as _, Div, InteractiveElement, IntoElement, ParentElement as _,
|
div, prelude::FluentBuilder as _, Div, InteractiveElement, IntoElement, ParentElement as _,
|
||||||
|
|
@ -31,7 +30,7 @@ pub struct Switch {
|
||||||
label: Option<SharedString>,
|
label: Option<SharedString>,
|
||||||
label_side: LabelSide,
|
label_side: LabelSide,
|
||||||
on_click: Option<OnClick>,
|
on_click: Option<OnClick>,
|
||||||
size: ButtonSize,
|
size: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Switch {
|
impl Switch {
|
||||||
|
|
@ -46,7 +45,7 @@ impl Switch {
|
||||||
label: None,
|
label: None,
|
||||||
on_click: None,
|
on_click: None,
|
||||||
label_side: LabelSide::Right,
|
label_side: LabelSide::Right,
|
||||||
size: ButtonSize::Medium,
|
size: Size::Medium,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,7 +59,8 @@ impl Switch {
|
||||||
self
|
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.size = size;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -107,8 +107,8 @@ impl RenderOnce for Switch {
|
||||||
.child(
|
.child(
|
||||||
self.base
|
self.base
|
||||||
.map(|this| match self.size {
|
.map(|this| match self.size {
|
||||||
ButtonSize::Medium => this.w_11().h_6().rounded_xl(),
|
Size::XSmall | Size::Small => this.w_8().h_4().rounded_lg(),
|
||||||
ButtonSize::XSmall | ButtonSize::Small => this.w_8().h_4().rounded_lg(),
|
_ => this.w_11().h_6().rounded_xl(),
|
||||||
})
|
})
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
|
|
@ -125,15 +125,15 @@ impl RenderOnce for Switch {
|
||||||
.rounded_full()
|
.rounded_full()
|
||||||
.bg(toggle_bg)
|
.bg(toggle_bg)
|
||||||
.map(|this| match self.size {
|
.map(|this| match self.size {
|
||||||
ButtonSize::Medium => this.w_5().h_5(),
|
Size::XSmall | Size::Small => this.w_3().h_3(),
|
||||||
ButtonSize::XSmall | ButtonSize::Small => this.w_3().h_3(),
|
_ => this.w_5().h_5(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.when_some(self.label, |this, label| {
|
.when_some(self.label, |this, label| {
|
||||||
this.child(div().child(label).map(|this| match self.size {
|
this.child(div().child(label).map(|this| match self.size {
|
||||||
ButtonSize::Medium => this.text_base(),
|
Size::XSmall | Size::Small => this.text_sm(),
|
||||||
ButtonSize::XSmall | ButtonSize::Small => this.text_sm(),
|
_ => this.text_base(),
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
.when_some(
|
.when_some(
|
||||||
|
|
|
||||||
|
|
@ -301,6 +301,8 @@ pub struct Theme {
|
||||||
pub tab_active: Hsla,
|
pub tab_active: Hsla,
|
||||||
pub tab_foreground: Hsla,
|
pub tab_foreground: Hsla,
|
||||||
pub tab_active_foreground: Hsla,
|
pub tab_active_foreground: Hsla,
|
||||||
|
pub indicator: Hsla,
|
||||||
|
pub progress_bar: Hsla,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Global for Theme {}
|
impl Global for Theme {}
|
||||||
|
|
@ -355,6 +357,8 @@ impl From<Colors> for Theme {
|
||||||
tab_active: colors.background,
|
tab_active: colors.background,
|
||||||
tab_foreground: colors.foreground,
|
tab_foreground: colors.foreground,
|
||||||
tab_active_foreground: colors.foreground,
|
tab_active_foreground: colors.foreground,
|
||||||
|
indicator: colors.secondary_foreground,
|
||||||
|
progress_bar: colors.primary,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue