parent
2234f91b7b
commit
555692fac6
5 changed files with 269 additions and 49 deletions
|
@ -1,6 +1,6 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use gpui::{ClickEvent, CursorStyle};
|
||||
use gpui::{ClickEvent, CursorStyle, SharedString};
|
||||
|
||||
use crate::{Color, IconButton, IconButtonShape, IconName, IconSize, prelude::*};
|
||||
|
||||
|
@ -14,6 +14,7 @@ pub struct Disclosure {
|
|||
cursor_style: CursorStyle,
|
||||
opened_icon: IconName,
|
||||
closed_icon: IconName,
|
||||
visible_on_hover: Option<SharedString>,
|
||||
}
|
||||
|
||||
impl Disclosure {
|
||||
|
@ -27,6 +28,7 @@ impl Disclosure {
|
|||
cursor_style: CursorStyle::PointingHand,
|
||||
opened_icon: IconName::ChevronDown,
|
||||
closed_icon: IconName::ChevronRight,
|
||||
visible_on_hover: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,6 +75,13 @@ impl Clickable for Disclosure {
|
|||
}
|
||||
}
|
||||
|
||||
impl VisibleOnHover for Disclosure {
|
||||
fn visible_on_hover(mut self, group_name: impl Into<SharedString>) -> Self {
|
||||
self.visible_on_hover = Some(group_name.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Disclosure {
|
||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||
IconButton::new(
|
||||
|
@ -87,6 +96,9 @@ impl RenderOnce for Disclosure {
|
|||
.icon_size(IconSize::Small)
|
||||
.disabled(self.disabled)
|
||||
.toggle_state(self.selected)
|
||||
.when_some(self.visible_on_hover.clone(), |this, group_name| {
|
||||
this.visible_on_hover(group_name)
|
||||
})
|
||||
.when_some(self.on_toggle, move |this, on_toggle| {
|
||||
this.on_click(move |event, window, cx| on_toggle(event, window, cx))
|
||||
})
|
||||
|
|
|
@ -2,8 +2,10 @@ mod highlighted_label;
|
|||
mod label;
|
||||
mod label_like;
|
||||
mod loading_label;
|
||||
mod spinner_label;
|
||||
|
||||
pub use highlighted_label::*;
|
||||
pub use label::*;
|
||||
pub use label_like::*;
|
||||
pub use loading_label::*;
|
||||
pub use spinner_label::*;
|
||||
|
|
192
crates/ui/src/components/label/spinner_label.rs
Normal file
192
crates/ui/src/components/label/spinner_label.rs
Normal file
|
@ -0,0 +1,192 @@
|
|||
use crate::prelude::*;
|
||||
use gpui::{Animation, AnimationExt, FontWeight};
|
||||
use std::time::Duration;
|
||||
|
||||
/// Different types of spinner animations
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub enum SpinnerVariant {
|
||||
#[default]
|
||||
Dots,
|
||||
DotsVariant,
|
||||
}
|
||||
|
||||
/// A spinner indication, based on the label component, that loops through
|
||||
/// frames of the specified animation. It implements `LabelCommon` as well.
|
||||
///
|
||||
/// # Default Example
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{SpinnerLabel};
|
||||
///
|
||||
/// SpinnerLabel::new();
|
||||
/// ```
|
||||
///
|
||||
/// # Variant Example
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{SpinnerLabel};
|
||||
///
|
||||
/// SpinnerLabel::dots_variant();
|
||||
/// ```
|
||||
#[derive(IntoElement, RegisterComponent)]
|
||||
pub struct SpinnerLabel {
|
||||
base: Label,
|
||||
variant: SpinnerVariant,
|
||||
frames: Vec<&'static str>,
|
||||
duration: Duration,
|
||||
}
|
||||
|
||||
impl SpinnerVariant {
|
||||
fn frames(&self) -> Vec<&'static str> {
|
||||
match self {
|
||||
SpinnerVariant::Dots => vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
||||
SpinnerVariant::DotsVariant => vec!["⣼", "⣹", "⢻", "⠿", "⡟", "⣏", "⣧", "⣶"],
|
||||
}
|
||||
}
|
||||
|
||||
fn duration(&self) -> Duration {
|
||||
match self {
|
||||
SpinnerVariant::Dots => Duration::from_millis(1000),
|
||||
SpinnerVariant::DotsVariant => Duration::from_millis(1000),
|
||||
}
|
||||
}
|
||||
|
||||
fn animation_id(&self) -> &'static str {
|
||||
match self {
|
||||
SpinnerVariant::Dots => "spinner_label_dots",
|
||||
SpinnerVariant::DotsVariant => "spinner_label_dots_variant",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SpinnerLabel {
|
||||
pub fn new() -> Self {
|
||||
Self::with_variant(SpinnerVariant::default())
|
||||
}
|
||||
|
||||
pub fn with_variant(variant: SpinnerVariant) -> Self {
|
||||
let frames = variant.frames();
|
||||
let duration = variant.duration();
|
||||
|
||||
SpinnerLabel {
|
||||
base: Label::new(frames[0]),
|
||||
variant,
|
||||
frames,
|
||||
duration,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dots() -> Self {
|
||||
Self::with_variant(SpinnerVariant::Dots)
|
||||
}
|
||||
|
||||
pub fn dots_variant() -> Self {
|
||||
Self::with_variant(SpinnerVariant::DotsVariant)
|
||||
}
|
||||
}
|
||||
|
||||
impl LabelCommon for SpinnerLabel {
|
||||
fn size(mut self, size: LabelSize) -> Self {
|
||||
self.base = self.base.size(size);
|
||||
self
|
||||
}
|
||||
|
||||
fn weight(mut self, weight: FontWeight) -> Self {
|
||||
self.base = self.base.weight(weight);
|
||||
self
|
||||
}
|
||||
|
||||
fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
|
||||
self.base = self.base.line_height_style(line_height_style);
|
||||
self
|
||||
}
|
||||
|
||||
fn color(mut self, color: Color) -> Self {
|
||||
self.base = self.base.color(color);
|
||||
self
|
||||
}
|
||||
|
||||
fn strikethrough(mut self) -> Self {
|
||||
self.base = self.base.strikethrough();
|
||||
self
|
||||
}
|
||||
|
||||
fn italic(mut self) -> Self {
|
||||
self.base = self.base.italic();
|
||||
self
|
||||
}
|
||||
|
||||
fn alpha(mut self, alpha: f32) -> Self {
|
||||
self.base = self.base.alpha(alpha);
|
||||
self
|
||||
}
|
||||
|
||||
fn underline(mut self) -> Self {
|
||||
self.base = self.base.underline();
|
||||
self
|
||||
}
|
||||
|
||||
fn truncate(mut self) -> Self {
|
||||
self.base = self.base.truncate();
|
||||
self
|
||||
}
|
||||
|
||||
fn single_line(mut self) -> Self {
|
||||
self.base = self.base.single_line();
|
||||
self
|
||||
}
|
||||
|
||||
fn buffer_font(mut self, cx: &App) -> Self {
|
||||
self.base = self.base.buffer_font(cx);
|
||||
self
|
||||
}
|
||||
|
||||
fn inline_code(mut self, cx: &App) -> Self {
|
||||
self.base = self.base.inline_code(cx);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for SpinnerLabel {
|
||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||
let frames = self.frames.clone();
|
||||
let duration = self.duration;
|
||||
|
||||
self.base.color(Color::Muted).with_animation(
|
||||
self.variant.animation_id(),
|
||||
Animation::new(duration).repeat(),
|
||||
move |mut label, delta| {
|
||||
let frame_index = (delta * frames.len() as f32) as usize % frames.len();
|
||||
|
||||
label.set_text(frames[frame_index]);
|
||||
label
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for SpinnerLabel {
|
||||
fn scope() -> ComponentScope {
|
||||
ComponentScope::Loading
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
"Spinner Label"
|
||||
}
|
||||
|
||||
fn sort_name() -> &'static str {
|
||||
"Spinner Label"
|
||||
}
|
||||
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
|
||||
let examples = vec![
|
||||
single_example("Default", SpinnerLabel::new().into_any_element()),
|
||||
single_example(
|
||||
"Dots Variant",
|
||||
SpinnerLabel::dots_variant().into_any_element(),
|
||||
),
|
||||
];
|
||||
|
||||
Some(example_group(examples).vertical().into_any_element())
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue