gpui-component/crates/story/src/progress_story.rs
Jason Lee 9f2cd84b23
color: Add mix method and merge ColorExt to Colorize. (#561)
- Add `mix` method to mix two color.
- Export `ui::theme::*` to crate root, before `use
ui:theme::ActiveTheme` to `use ui::ActiveTheme`.

## Break Changes

- Moved `ui::theme::Colorize` to `ui::Colorize`.
- Merged `ColorExt` to `Colorize`.
- Renamed `to_hex_string` to `to_hex`, `parse_hex_string` to
`parse_hex`.
2025-01-21 18:31:01 +08:00

298 lines
10 KiB
Rust

use gpui::{
div, hsla, px, Hsla, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
View, ViewContext, VisualContext, WindowContext,
};
use ui::{
button::Button,
clipboard::Clipboard,
divider::Divider,
h_flex,
indicator::Indicator,
progress::Progress,
skeleton::Skeleton,
slider::{Slider, SliderEvent},
v_flex, Colorize as _, ContextModal, IconName, Sizable,
};
pub struct ProgressStory {
focus_handle: gpui::FocusHandle,
value: f32,
slider1: View<Slider>,
slider1_value: f32,
slider2: View<Slider>,
slider2_value: f32,
slider_hsl: [View<Slider>; 4],
slider_hsl_value: Hsla,
_subscritions: Vec<Subscription>,
}
impl super::Story for ProgressStory {
fn title() -> &'static str {
"Progress"
}
fn new_view(cx: &mut WindowContext) -> View<impl gpui::FocusableView> {
Self::view(cx)
}
}
impl ProgressStory {
pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.new_view(Self::new)
}
fn new(cx: &mut ViewContext<Self>) -> Self {
let slider1 = cx.new_view(|_| {
Slider::horizontal()
.min(-255.)
.max(255.)
.default_value(15.)
.step(15.)
});
let slider2 = cx.new_view(|_| Slider::horizontal().min(0.).max(5.).step(1.0));
let slider_hsl = [
cx.new_view(|_| {
Slider::vertical()
.reverse()
.min(0.)
.max(1.)
.step(0.01)
.default_value(0.)
}),
cx.new_view(|_| {
Slider::vertical()
.reverse()
.min(0.)
.max(1.)
.step(0.01)
.default_value(0.5)
}),
cx.new_view(|_| {
Slider::vertical()
.reverse()
.min(0.)
.max(1.)
.step(0.01)
.default_value(0.5)
}),
cx.new_view(|_| {
Slider::vertical()
.reverse()
.min(0.)
.max(1.)
.step(0.01)
.default_value(1.)
}),
];
let mut _subscritions = vec![
cx.subscribe(&slider1, |this, _, event: &SliderEvent, cx| match event {
SliderEvent::Change(value) => {
this.slider1_value = *value;
cx.notify();
}
}),
cx.subscribe(&slider2, |this, _, event: &SliderEvent, cx| match event {
SliderEvent::Change(value) => {
this.slider2_value = *value;
cx.notify();
}
}),
];
_subscritions.extend(
slider_hsl
.iter()
.map(|slider| {
cx.subscribe(slider, |this, _, event: &SliderEvent, cx| match event {
SliderEvent::Change(_) => {
this.slider_hsl_value = hsla(
this.slider_hsl[0].read(cx).value(),
this.slider_hsl[1].read(cx).value(),
this.slider_hsl[2].read(cx).value(),
this.slider_hsl[3].read(cx).value(),
);
cx.notify();
}
})
})
.collect::<Vec<_>>(),
);
Self {
focus_handle: cx.focus_handle(),
value: 50.,
slider1_value: 0.,
slider2_value: 0.,
slider1,
slider2,
slider_hsl,
slider_hsl_value: gpui::red(),
_subscritions,
}
}
pub fn set_value(&mut self, value: f32) {
self.value = value;
}
}
impl gpui::FocusableView for ProgressStory {
fn focus_handle(&self, _: &gpui::AppContext) -> gpui::FocusHandle {
self.focus_handle.clone()
}
}
impl Render for ProgressStory {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let rgb = SharedString::from(self.slider_hsl_value.to_hex());
v_flex()
.items_center()
.gap_y_3()
.child(
h_flex()
.gap_x_2()
.child(Button::new("button-1").label("0%").on_click(cx.listener(
|this, _, _| {
this.set_value(0.);
},
)))
.child(Button::new("button-2").label("25%").on_click(cx.listener(
|this, _, _| {
this.set_value(25.);
},
)))
.child(Button::new("button-3").label("75%").on_click(cx.listener(
|this, _, _| {
this.set_value(75.);
},
)))
.child(Button::new("button-4").label("100%").on_click(cx.listener(
|this, _, _| {
this.set_value(100.);
},
))),
)
.child(div().w_1_2().child(Progress::new().value(self.value)))
.child(
h_flex()
.gap_x_2()
.child(
Button::new("button-5")
.icon(IconName::Minus)
.on_click(cx.listener(|this, _, _| {
this.set_value((this.value - 1.).max(0.));
})),
)
.child(
Button::new("button-6")
.icon(IconName::Plus)
.on_click(cx.listener(|this, _, _| {
this.set_value((this.value + 1.).min(100.));
})),
),
)
.child(
h_flex()
.gap_x_2()
.child(Indicator::new().xsmall())
.child(Indicator::new().small())
.child(Indicator::new())
.child(
Indicator::new()
.large()
.icon(IconName::LoaderCircle)
.color(ui::blue_500()),
)
.child(Indicator::new().with_size(px(64.))),
)
.child(
Divider::horizontal()
.mt_10()
.label("Slider")
.color(ui::gray_300()),
)
.child(self.slider1.clone())
.child(format!("Slider 1: {}", self.slider1_value))
.child(
v_flex()
.gap_3()
.w(px(200.))
.child(self.slider2.clone())
.child(format!("Slider 2: {}", self.slider2_value)),
)
.child(
h_flex()
.gap_3()
.justify_start()
.child(
v_flex()
.w_32()
.h_40()
.gap_3()
.items_center()
.child(self.slider_hsl[0].clone())
.child(format!("H: {:.0}", self.slider_hsl_value.h * 360.)),
)
.child(
v_flex()
.w_32()
.h_40()
.gap_3()
.items_center()
.child(self.slider_hsl[1].clone())
.child(format!("S: {:.0}", self.slider_hsl_value.s * 100.)),
)
.child(
v_flex()
.w_32()
.h_40()
.gap_3()
.items_center()
.child(self.slider_hsl[2].clone())
.child(format!("L: {:.0}", self.slider_hsl_value.l * 100.)),
)
.child(
v_flex()
.w_32()
.h_40()
.gap_3()
.items_center()
.child(self.slider_hsl[3].clone())
.child(format!("A: {:.0}", self.slider_hsl_value.a * 100.)),
)
.child(
h_flex()
.gap_2()
.items_center()
.child(
h_flex()
.w_32()
.p_1()
.rounded_lg()
.justify_center()
.bg(self.slider_hsl_value)
.child(rgb.clone())
.text_color(self.slider_hsl_value.invert()),
)
.child(Clipboard::new("copy-hsl").value(rgb).on_copied(|_, cx| {
cx.push_notification("Color copied to clipboard.")
})),
),
)
.child(
h_flex()
.mt_5()
.gap_4()
.child(Skeleton::new().size_12().rounded_full())
.child(
v_flex()
.gap_2()
.child(Skeleton::new().w(px(250.)).h_4())
.child(Skeleton::new().w(px(240.)).h_4()),
),
)
}
}