Button2 – Part1 (#3420)
## TODO - [x] Remove `InteractionState` - [ ] `Selectable` should use `Selection` instead of a boolean - [x] Clean out ui2 prelude - [ ] Build out button2 button types - [ ] Port old buttons Release Notes: - N/A --------- Co-authored-by: Marshall Bowers <1486634+maxdeviant@users.noreply.github.com>
This commit is contained in:
parent
5d59108b97
commit
a8bf0834e6
22 changed files with 567 additions and 339 deletions
|
@ -37,7 +37,10 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use ui::{h_stack, Avatar, Button, ButtonVariant, Color, IconButton, KeyBinding, Tooltip};
|
use ui::{
|
||||||
|
h_stack, Avatar, Button, ButtonCommon, ButtonLike, ButtonVariant, Clickable, Color, IconButton,
|
||||||
|
IconElement, IconSize, KeyBinding, Tooltip,
|
||||||
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::{notifications::NotifyResultExt, Workspace};
|
use workspace::{notifications::NotifyResultExt, Workspace};
|
||||||
|
|
||||||
|
@ -298,6 +301,27 @@ impl Render for CollabTitlebarItem {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}))
|
}))
|
||||||
|
// Temporary, will be removed when the last part of button2 is merged
|
||||||
|
.child(
|
||||||
|
div().border().border_color(gpui::blue()).child(
|
||||||
|
ButtonLike::new("test-button")
|
||||||
|
.children([
|
||||||
|
Avatar::uri(
|
||||||
|
"https://avatars.githubusercontent.com/u/1714999?v=4",
|
||||||
|
)
|
||||||
|
.into_element()
|
||||||
|
.into_any(),
|
||||||
|
IconElement::new(ui::Icon::ChevronDown)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.into_element()
|
||||||
|
.into_any(),
|
||||||
|
])
|
||||||
|
.on_click(move |event, _cx| {
|
||||||
|
dbg!(format!("clicked: {:?}", event.down.position));
|
||||||
|
})
|
||||||
|
.tooltip(|cx| Tooltip::text("Test tooltip", cx)),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use gpui::{
|
||||||
actions, div, prelude::*, Div, FocusHandle, Focusable, KeyBinding, Render, Stateful, View,
|
actions, div, prelude::*, Div, FocusHandle, Focusable, KeyBinding, Render, Stateful, View,
|
||||||
WindowContext,
|
WindowContext,
|
||||||
};
|
};
|
||||||
use theme2::ActiveTheme;
|
use ui::prelude::*;
|
||||||
|
|
||||||
actions!(ActionA, ActionB, ActionC);
|
actions!(ActionA, ActionB, ActionC);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme2::ActiveTheme;
|
use ui::prelude::*;
|
||||||
use ui::{Label, ListItem};
|
use ui::{Label, ListItem};
|
||||||
|
|
||||||
pub struct PickerStory {
|
pub struct PickerStory {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use gpui::{div, prelude::*, px, Div, Render, SharedString, Stateful, Styled, View, WindowContext};
|
use gpui::{div, prelude::*, px, Div, Render, SharedString, Stateful, Styled, View, WindowContext};
|
||||||
use theme2::ActiveTheme;
|
use ui::prelude::*;
|
||||||
use ui::Tooltip;
|
use ui::Tooltip;
|
||||||
|
|
||||||
pub struct ScrollStory;
|
pub struct ScrollStory;
|
||||||
|
|
|
@ -19,7 +19,6 @@ pub enum ComponentStory {
|
||||||
Focus,
|
Focus,
|
||||||
Icon,
|
Icon,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
|
||||||
Keybinding,
|
Keybinding,
|
||||||
Label,
|
Label,
|
||||||
ListItem,
|
ListItem,
|
||||||
|
@ -39,7 +38,6 @@ impl ComponentStory {
|
||||||
Self::Focus => FocusStory::view(cx).into(),
|
Self::Focus => FocusStory::view(cx).into(),
|
||||||
Self::Icon => cx.build_view(|_| ui::IconStory).into(),
|
Self::Icon => cx.build_view(|_| ui::IconStory).into(),
|
||||||
Self::IconButton => cx.build_view(|_| ui::IconButtonStory).into(),
|
Self::IconButton => cx.build_view(|_| ui::IconButtonStory).into(),
|
||||||
Self::Input => cx.build_view(|_| ui::InputStory).into(),
|
|
||||||
Self::Keybinding => cx.build_view(|_| ui::KeybindingStory).into(),
|
Self::Keybinding => cx.build_view(|_| ui::KeybindingStory).into(),
|
||||||
Self::Label => cx.build_view(|_| ui::LabelStory).into(),
|
Self::Label => cx.build_view(|_| ui::LabelStory).into(),
|
||||||
Self::ListItem => cx.build_view(|_| ui::ListItemStory).into(),
|
Self::ListItem => cx.build_view(|_| ui::ListItemStory).into(),
|
||||||
|
|
5
crates/ui2/src/clickable.rs
Normal file
5
crates/ui2/src/clickable.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
use gpui::{ClickEvent, WindowContext};
|
||||||
|
|
||||||
|
pub trait Clickable {
|
||||||
|
fn on_click(self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self;
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
mod avatar;
|
mod avatar;
|
||||||
mod button;
|
mod button;
|
||||||
|
mod button2;
|
||||||
mod checkbox;
|
mod checkbox;
|
||||||
mod context_menu;
|
mod context_menu;
|
||||||
mod disclosure;
|
mod disclosure;
|
||||||
mod divider;
|
mod divider;
|
||||||
mod icon;
|
mod icon;
|
||||||
mod icon_button;
|
mod icon_button;
|
||||||
mod input;
|
|
||||||
mod keybinding;
|
mod keybinding;
|
||||||
mod label;
|
mod label;
|
||||||
mod list;
|
mod list;
|
||||||
|
@ -21,13 +21,13 @@ mod stories;
|
||||||
|
|
||||||
pub use avatar::*;
|
pub use avatar::*;
|
||||||
pub use button::*;
|
pub use button::*;
|
||||||
|
pub use button2::*;
|
||||||
pub use checkbox::*;
|
pub use checkbox::*;
|
||||||
pub use context_menu::*;
|
pub use context_menu::*;
|
||||||
pub use disclosure::*;
|
pub use disclosure::*;
|
||||||
pub use divider::*;
|
pub use divider::*;
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
pub use icon_button::*;
|
pub use icon_button::*;
|
||||||
pub use input::*;
|
|
||||||
pub use keybinding::*;
|
pub use keybinding::*;
|
||||||
pub use label::*;
|
pub use label::*;
|
||||||
pub use list::*;
|
pub use list::*;
|
||||||
|
|
413
crates/ui2/src/components/button2.rs
Normal file
413
crates/ui2/src/components/button2.rs
Normal file
|
@ -0,0 +1,413 @@
|
||||||
|
use gpui::{
|
||||||
|
rems, AnyElement, AnyView, ClickEvent, Div, Hsla, IntoElement, Rems, Stateful,
|
||||||
|
StatefulInteractiveElement, WindowContext,
|
||||||
|
};
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
use crate::{h_stack, prelude::*};
|
||||||
|
|
||||||
|
// 🚧 Heavily WIP 🚧
|
||||||
|
|
||||||
|
// #[derive(Default, PartialEq, Clone, Copy)]
|
||||||
|
// pub enum ButtonType2 {
|
||||||
|
// #[default]
|
||||||
|
// DefaultButton,
|
||||||
|
// IconButton,
|
||||||
|
// ButtonLike,
|
||||||
|
// SplitButton,
|
||||||
|
// ToggleButton,
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Clone, Copy)]
|
||||||
|
pub enum IconPosition2 {
|
||||||
|
#[default]
|
||||||
|
Before,
|
||||||
|
After,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Clone, Copy)]
|
||||||
|
pub enum ButtonStyle2 {
|
||||||
|
#[default]
|
||||||
|
Filled,
|
||||||
|
// Tinted,
|
||||||
|
Subtle,
|
||||||
|
Transparent,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ButtonStyle {
|
||||||
|
pub background: Hsla,
|
||||||
|
pub border_color: Hsla,
|
||||||
|
pub label_color: Hsla,
|
||||||
|
pub icon_color: Hsla,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ButtonStyle2 {
|
||||||
|
pub fn enabled(self, cx: &mut WindowContext) -> ButtonStyle {
|
||||||
|
match self {
|
||||||
|
ButtonStyle2::Filled => ButtonStyle {
|
||||||
|
background: cx.theme().colors().element_background,
|
||||||
|
border_color: gpui::transparent_black(),
|
||||||
|
label_color: Color::Default.color(cx),
|
||||||
|
icon_color: Color::Default.color(cx),
|
||||||
|
},
|
||||||
|
ButtonStyle2::Subtle => ButtonStyle {
|
||||||
|
background: cx.theme().colors().ghost_element_background,
|
||||||
|
border_color: gpui::transparent_black(),
|
||||||
|
label_color: Color::Default.color(cx),
|
||||||
|
icon_color: Color::Default.color(cx),
|
||||||
|
},
|
||||||
|
ButtonStyle2::Transparent => ButtonStyle {
|
||||||
|
background: gpui::transparent_black(),
|
||||||
|
border_color: gpui::transparent_black(),
|
||||||
|
label_color: Color::Default.color(cx),
|
||||||
|
icon_color: Color::Default.color(cx),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hovered(self, cx: &mut WindowContext) -> ButtonStyle {
|
||||||
|
match self {
|
||||||
|
ButtonStyle2::Filled => ButtonStyle {
|
||||||
|
background: cx.theme().colors().element_hover,
|
||||||
|
border_color: gpui::transparent_black(),
|
||||||
|
label_color: Color::Default.color(cx),
|
||||||
|
icon_color: Color::Default.color(cx),
|
||||||
|
},
|
||||||
|
ButtonStyle2::Subtle => ButtonStyle {
|
||||||
|
background: cx.theme().colors().ghost_element_hover,
|
||||||
|
border_color: gpui::transparent_black(),
|
||||||
|
label_color: Color::Default.color(cx),
|
||||||
|
icon_color: Color::Default.color(cx),
|
||||||
|
},
|
||||||
|
ButtonStyle2::Transparent => ButtonStyle {
|
||||||
|
background: gpui::transparent_black(),
|
||||||
|
border_color: gpui::transparent_black(),
|
||||||
|
// TODO: These are not great
|
||||||
|
label_color: Color::Muted.color(cx),
|
||||||
|
// TODO: These are not great
|
||||||
|
icon_color: Color::Muted.color(cx),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active(self, cx: &mut WindowContext) -> ButtonStyle {
|
||||||
|
match self {
|
||||||
|
ButtonStyle2::Filled => ButtonStyle {
|
||||||
|
background: cx.theme().colors().element_active,
|
||||||
|
border_color: gpui::transparent_black(),
|
||||||
|
label_color: Color::Default.color(cx),
|
||||||
|
icon_color: Color::Default.color(cx),
|
||||||
|
},
|
||||||
|
ButtonStyle2::Subtle => ButtonStyle {
|
||||||
|
background: cx.theme().colors().ghost_element_active,
|
||||||
|
border_color: gpui::transparent_black(),
|
||||||
|
label_color: Color::Default.color(cx),
|
||||||
|
icon_color: Color::Default.color(cx),
|
||||||
|
},
|
||||||
|
ButtonStyle2::Transparent => ButtonStyle {
|
||||||
|
background: gpui::transparent_black(),
|
||||||
|
border_color: gpui::transparent_black(),
|
||||||
|
// TODO: These are not great
|
||||||
|
label_color: Color::Muted.color(cx),
|
||||||
|
// TODO: These are not great
|
||||||
|
icon_color: Color::Muted.color(cx),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focused(self, cx: &mut WindowContext) -> ButtonStyle {
|
||||||
|
match self {
|
||||||
|
ButtonStyle2::Filled => ButtonStyle {
|
||||||
|
background: cx.theme().colors().element_background,
|
||||||
|
border_color: cx.theme().colors().border_focused,
|
||||||
|
label_color: Color::Default.color(cx),
|
||||||
|
icon_color: Color::Default.color(cx),
|
||||||
|
},
|
||||||
|
ButtonStyle2::Subtle => ButtonStyle {
|
||||||
|
background: cx.theme().colors().ghost_element_background,
|
||||||
|
border_color: cx.theme().colors().border_focused,
|
||||||
|
label_color: Color::Default.color(cx),
|
||||||
|
icon_color: Color::Default.color(cx),
|
||||||
|
},
|
||||||
|
ButtonStyle2::Transparent => ButtonStyle {
|
||||||
|
background: gpui::transparent_black(),
|
||||||
|
border_color: cx.theme().colors().border_focused,
|
||||||
|
label_color: Color::Accent.color(cx),
|
||||||
|
icon_color: Color::Accent.color(cx),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disabled(self, cx: &mut WindowContext) -> ButtonStyle {
|
||||||
|
match self {
|
||||||
|
ButtonStyle2::Filled => ButtonStyle {
|
||||||
|
background: cx.theme().colors().element_disabled,
|
||||||
|
border_color: cx.theme().colors().border_disabled,
|
||||||
|
label_color: Color::Disabled.color(cx),
|
||||||
|
icon_color: Color::Disabled.color(cx),
|
||||||
|
},
|
||||||
|
ButtonStyle2::Subtle => ButtonStyle {
|
||||||
|
background: cx.theme().colors().ghost_element_disabled,
|
||||||
|
border_color: cx.theme().colors().border_disabled,
|
||||||
|
label_color: Color::Disabled.color(cx),
|
||||||
|
icon_color: Color::Disabled.color(cx),
|
||||||
|
},
|
||||||
|
ButtonStyle2::Transparent => ButtonStyle {
|
||||||
|
background: gpui::transparent_black(),
|
||||||
|
border_color: gpui::transparent_black(),
|
||||||
|
label_color: Color::Disabled.color(cx),
|
||||||
|
icon_color: Color::Disabled.color(cx),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Clone, Copy)]
|
||||||
|
pub enum ButtonSize2 {
|
||||||
|
#[default]
|
||||||
|
Default,
|
||||||
|
Compact,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ButtonSize2 {
|
||||||
|
fn height(self) -> Rems {
|
||||||
|
match self {
|
||||||
|
ButtonSize2::Default => rems(22. / 16.),
|
||||||
|
ButtonSize2::Compact => rems(18. / 16.),
|
||||||
|
ButtonSize2::None => rems(16. / 16.),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub struct Button {
|
||||||
|
// id: ElementId,
|
||||||
|
// icon: Option<Icon>,
|
||||||
|
// icon_color: Option<Color>,
|
||||||
|
// icon_position: Option<IconPosition2>,
|
||||||
|
// label: Option<Label>,
|
||||||
|
// label_color: Option<Color>,
|
||||||
|
// appearance: ButtonAppearance2,
|
||||||
|
// state: InteractionState,
|
||||||
|
// selected: bool,
|
||||||
|
// disabled: bool,
|
||||||
|
// tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
|
||||||
|
// width: Option<DefiniteLength>,
|
||||||
|
// action: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
|
||||||
|
// secondary_action: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
|
||||||
|
// /// Used to pass down some content to the button
|
||||||
|
// /// to enable creating custom buttons.
|
||||||
|
// children: SmallVec<[AnyElement; 2]>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub trait ButtonCommon: Clickable {
|
||||||
|
fn id(&self) -> &ElementId;
|
||||||
|
fn style(self, style: ButtonStyle2) -> Self;
|
||||||
|
fn disabled(self, disabled: bool) -> Self;
|
||||||
|
fn size(self, size: ButtonSize2) -> Self;
|
||||||
|
fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
|
||||||
|
// fn width(&mut self, width: DefiniteLength) -> &mut Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub struct LabelButton {
|
||||||
|
// // Base properties...
|
||||||
|
// id: ElementId,
|
||||||
|
// appearance: ButtonAppearance,
|
||||||
|
// state: InteractionState,
|
||||||
|
// disabled: bool,
|
||||||
|
// size: ButtonSize,
|
||||||
|
// tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
|
||||||
|
// width: Option<DefiniteLength>,
|
||||||
|
// // Button-specific properties...
|
||||||
|
// label: Option<SharedString>,
|
||||||
|
// label_color: Option<Color>,
|
||||||
|
// icon: Option<Icon>,
|
||||||
|
// icon_color: Option<Color>,
|
||||||
|
// icon_position: Option<IconPosition>,
|
||||||
|
// // Define more fields for additional properties as needed
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl ButtonCommon for LabelButton {
|
||||||
|
// fn id(&self) -> &ElementId {
|
||||||
|
// &self.id
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn appearance(&mut self, appearance: ButtonAppearance) -> &mut Self {
|
||||||
|
// self.style= style;
|
||||||
|
// self
|
||||||
|
// }
|
||||||
|
// // implement methods from ButtonCommon trait...
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl LabelButton {
|
||||||
|
// pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
|
||||||
|
// Self {
|
||||||
|
// id: id.into(),
|
||||||
|
// label: Some(label.into()),
|
||||||
|
// // initialize other fields with default values...
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ... Define other builder methods specific to Button type...
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO: Icon Button
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct ButtonLike {
|
||||||
|
id: ElementId,
|
||||||
|
style: ButtonStyle2,
|
||||||
|
disabled: bool,
|
||||||
|
size: ButtonSize2,
|
||||||
|
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
|
||||||
|
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
||||||
|
children: SmallVec<[AnyElement; 2]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ButtonLike {
|
||||||
|
pub fn children(
|
||||||
|
&mut self,
|
||||||
|
children: impl IntoIterator<Item = impl Into<AnyElement>>,
|
||||||
|
) -> &mut Self {
|
||||||
|
self.children = children.into_iter().map(Into::into).collect();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||||
|
Self {
|
||||||
|
id: id.into(),
|
||||||
|
style: ButtonStyle2::default(),
|
||||||
|
disabled: false,
|
||||||
|
size: ButtonSize2::Default,
|
||||||
|
tooltip: None,
|
||||||
|
children: SmallVec::new(),
|
||||||
|
on_click: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clickable for ButtonLike {
|
||||||
|
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
|
||||||
|
self.on_click = Some(Box::new(handler));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl Selectable for ButtonLike {
|
||||||
|
// fn selected(&mut self, selected: bool) -> &mut Self {
|
||||||
|
// todo!()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn selected_tooltip(
|
||||||
|
// &mut self,
|
||||||
|
// tooltip: Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>,
|
||||||
|
// ) -> &mut Self {
|
||||||
|
// todo!()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl ButtonCommon for ButtonLike {
|
||||||
|
fn id(&self) -> &ElementId {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style(mut self, style: ButtonStyle2) -> Self {
|
||||||
|
self.style = style;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disabled(mut self, disabled: bool) -> Self {
|
||||||
|
self.disabled = disabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(mut self, size: ButtonSize2) -> Self {
|
||||||
|
self.size = size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
|
||||||
|
self.tooltip = Some(Box::new(tooltip));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for ButtonLike {
|
||||||
|
type Rendered = Stateful<Div>;
|
||||||
|
|
||||||
|
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||||
|
h_stack()
|
||||||
|
.id(self.id.clone())
|
||||||
|
.h(self.size.height())
|
||||||
|
.rounded_md()
|
||||||
|
.cursor_pointer()
|
||||||
|
.gap_1()
|
||||||
|
.px_1()
|
||||||
|
.bg(self.style.enabled(cx).background)
|
||||||
|
.hover(|hover| hover.bg(self.style.hovered(cx).background))
|
||||||
|
.active(|active| active.bg(self.style.active(cx).background))
|
||||||
|
.when_some(
|
||||||
|
self.on_click.filter(|_| !self.disabled),
|
||||||
|
|this, on_click| this.on_click(move |event, cx| (on_click)(event, cx)),
|
||||||
|
)
|
||||||
|
.when_some(self.tooltip, |this, tooltip| {
|
||||||
|
this.tooltip(move |cx| tooltip(cx))
|
||||||
|
})
|
||||||
|
.children(self.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParentElement for ButtonLike {
|
||||||
|
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
|
||||||
|
&mut self.children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub struct ToggleButton {
|
||||||
|
// // based on either IconButton2 or Button, with additional 'selected: bool' property
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl ButtonCommon for ToggleButton {
|
||||||
|
// fn id(&self) -> &ElementId {
|
||||||
|
// &self.id
|
||||||
|
// }
|
||||||
|
// // ... Implement other methods from ButtonCommon trait with builder patterns...
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl ToggleButton {
|
||||||
|
// pub fn new() -> Self {
|
||||||
|
// // Initialize with default values
|
||||||
|
// Self {
|
||||||
|
// // ... initialize fields, possibly with defaults or required parameters...
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ... Define other builder methods specific to ToggleButton type...
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub struct SplitButton {
|
||||||
|
// // Base properties...
|
||||||
|
// id: ElementId,
|
||||||
|
// // Button-specific properties, possibly including a DefaultButton
|
||||||
|
// secondary_action: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext)>>,
|
||||||
|
// // More fields as necessary...
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl ButtonCommon for SplitButton {
|
||||||
|
// fn id(&self) -> &ElementId {
|
||||||
|
// &self.id
|
||||||
|
// }
|
||||||
|
// // ... Implement other methods from ButtonCommon trait with builder patterns...
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl SplitButton {
|
||||||
|
// pub fn new(id: impl Into<ElementId>) -> Self {
|
||||||
|
// Self {
|
||||||
|
// id: id.into(),
|
||||||
|
// // ... initialize other fields with default values...
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ... Define other builder methods specific to SplitButton type...
|
||||||
|
// }
|
|
@ -8,7 +8,7 @@ pub struct IconButton {
|
||||||
color: Color,
|
color: Color,
|
||||||
size: IconSize,
|
size: IconSize,
|
||||||
variant: ButtonVariant,
|
variant: ButtonVariant,
|
||||||
state: InteractionState,
|
disabled: bool,
|
||||||
selected: bool,
|
selected: bool,
|
||||||
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
|
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
|
||||||
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
||||||
|
@ -18,9 +18,9 @@ impl RenderOnce for IconButton {
|
||||||
type Rendered = Stateful<Div>;
|
type Rendered = Stateful<Div>;
|
||||||
|
|
||||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||||
let icon_color = match (self.state, self.color) {
|
let icon_color = match (self.disabled, self.selected, self.color) {
|
||||||
(InteractionState::Disabled, _) => Color::Disabled,
|
(true, _, _) => Color::Disabled,
|
||||||
(InteractionState::Active, _) => Color::Selected,
|
(false, true, _) => Color::Selected,
|
||||||
_ => self.color,
|
_ => self.color,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,8 +82,8 @@ impl IconButton {
|
||||||
color: Color::default(),
|
color: Color::default(),
|
||||||
size: Default::default(),
|
size: Default::default(),
|
||||||
variant: ButtonVariant::default(),
|
variant: ButtonVariant::default(),
|
||||||
state: InteractionState::default(),
|
|
||||||
selected: false,
|
selected: false,
|
||||||
|
disabled: false,
|
||||||
tooltip: None,
|
tooltip: None,
|
||||||
on_click: None,
|
on_click: None,
|
||||||
}
|
}
|
||||||
|
@ -109,13 +109,13 @@ impl IconButton {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state(mut self, state: InteractionState) -> Self {
|
pub fn selected(mut self, selected: bool) -> Self {
|
||||||
self.state = state;
|
self.selected = selected;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected(mut self, selected: bool) -> Self {
|
pub fn disabled(mut self, disabled: bool) -> Self {
|
||||||
self.selected = selected;
|
self.disabled = disabled;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
use crate::{prelude::*, Label};
|
|
||||||
use gpui::{prelude::*, Div, IntoElement, Stateful};
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq)]
|
|
||||||
pub enum InputVariant {
|
|
||||||
#[default]
|
|
||||||
Ghost,
|
|
||||||
Filled,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
pub struct Input {
|
|
||||||
placeholder: SharedString,
|
|
||||||
value: String,
|
|
||||||
state: InteractionState,
|
|
||||||
variant: InputVariant,
|
|
||||||
disabled: bool,
|
|
||||||
is_active: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for Input {
|
|
||||||
type Rendered = Stateful<Div>;
|
|
||||||
|
|
||||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
|
||||||
let (input_bg, input_hover_bg, input_active_bg) = match self.variant {
|
|
||||||
InputVariant::Ghost => (
|
|
||||||
cx.theme().colors().ghost_element_background,
|
|
||||||
cx.theme().colors().ghost_element_hover,
|
|
||||||
cx.theme().colors().ghost_element_active,
|
|
||||||
),
|
|
||||||
InputVariant::Filled => (
|
|
||||||
cx.theme().colors().element_background,
|
|
||||||
cx.theme().colors().element_hover,
|
|
||||||
cx.theme().colors().element_active,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let placeholder_label = Label::new(self.placeholder.clone()).color(if self.disabled {
|
|
||||||
Color::Disabled
|
|
||||||
} else {
|
|
||||||
Color::Placeholder
|
|
||||||
});
|
|
||||||
|
|
||||||
let label = Label::new(self.value.clone()).color(if self.disabled {
|
|
||||||
Color::Disabled
|
|
||||||
} else {
|
|
||||||
Color::Default
|
|
||||||
});
|
|
||||||
|
|
||||||
div()
|
|
||||||
.id("input")
|
|
||||||
.h_7()
|
|
||||||
.w_full()
|
|
||||||
.px_2()
|
|
||||||
.border()
|
|
||||||
.border_color(cx.theme().styles.system.transparent)
|
|
||||||
.bg(input_bg)
|
|
||||||
.hover(|style| style.bg(input_hover_bg))
|
|
||||||
.active(|style| style.bg(input_active_bg))
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.child(div().flex().items_center().text_ui_sm().map(move |this| {
|
|
||||||
if self.value.is_empty() {
|
|
||||||
this.child(placeholder_label)
|
|
||||||
} else {
|
|
||||||
this.child(label)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Input {
|
|
||||||
pub fn new(placeholder: impl Into<SharedString>) -> Self {
|
|
||||||
Self {
|
|
||||||
placeholder: placeholder.into(),
|
|
||||||
value: "".to_string(),
|
|
||||||
state: InteractionState::default(),
|
|
||||||
variant: InputVariant::default(),
|
|
||||||
disabled: false,
|
|
||||||
is_active: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value(mut self, value: String) -> Self {
|
|
||||||
self.value = value;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn state(mut self, state: InteractionState) -> Self {
|
|
||||||
self.state = state;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn variant(mut self, variant: InputVariant) -> Self {
|
|
||||||
self.variant = variant;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disabled(mut self, disabled: bool) -> Self {
|
|
||||||
self.disabled = disabled;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_active(mut self, is_active: bool) -> Self {
|
|
||||||
self.is_active = is_active;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,18 +4,15 @@ mod checkbox;
|
||||||
mod context_menu;
|
mod context_menu;
|
||||||
mod icon;
|
mod icon;
|
||||||
mod icon_button;
|
mod icon_button;
|
||||||
mod input;
|
|
||||||
mod keybinding;
|
mod keybinding;
|
||||||
mod label;
|
mod label;
|
||||||
mod list_item;
|
mod list_item;
|
||||||
|
|
||||||
pub use avatar::*;
|
pub use avatar::*;
|
||||||
pub use button::*;
|
pub use button::*;
|
||||||
pub use checkbox::*;
|
pub use checkbox::*;
|
||||||
pub use context_menu::*;
|
pub use context_menu::*;
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
pub use icon_button::*;
|
pub use icon_button::*;
|
||||||
pub use input::*;
|
|
||||||
pub use keybinding::*;
|
pub use keybinding::*;
|
||||||
pub use label::*;
|
pub use label::*;
|
||||||
pub use list_item::*;
|
pub use list_item::*;
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use gpui::{rems, Div, Render};
|
use gpui::{Div, Render};
|
||||||
use story::Story;
|
use story::Story;
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{h_stack, v_stack, Button, Icon, IconPosition, Label};
|
use crate::{h_stack, Button, Icon, IconPosition};
|
||||||
|
|
||||||
pub struct ButtonStory;
|
pub struct ButtonStory;
|
||||||
|
|
||||||
|
@ -11,8 +10,6 @@ impl Render for ButtonStory {
|
||||||
type Element = Div;
|
type Element = Div;
|
||||||
|
|
||||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||||
let states = InteractionState::iter();
|
|
||||||
|
|
||||||
Story::container()
|
Story::container()
|
||||||
.child(Story::title_for::<Button>())
|
.child(Story::title_for::<Button>())
|
||||||
.child(
|
.child(
|
||||||
|
@ -20,121 +17,56 @@ impl Render for ButtonStory {
|
||||||
.flex()
|
.flex()
|
||||||
.gap_8()
|
.gap_8()
|
||||||
.child(
|
.child(
|
||||||
div()
|
div().child(Story::label("Ghost (Default)")).child(
|
||||||
.child(Story::label("Ghost (Default)"))
|
h_stack()
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
.gap_2()
|
||||||
v_stack()
|
.child(Button::new("Label").variant(ButtonVariant::Ghost)),
|
||||||
.gap_1()
|
),
|
||||||
.child(Label::new(state.to_string()).color(Color::Muted))
|
|
||||||
.child(
|
|
||||||
Button::new("Label").variant(ButtonVariant::Ghost), // .state(state),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label("Ghost – Left Icon"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(Label::new(state.to_string()).color(Color::Muted))
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Ghost)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Left), // .state(state),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label("Ghost – Right Icon"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(Label::new(state.to_string()).color(Color::Muted))
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Ghost)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Right), // .state(state),
|
|
||||||
)
|
|
||||||
}))),
|
|
||||||
)
|
)
|
||||||
|
.child(Story::label("Ghost – Left Icon"))
|
||||||
.child(
|
.child(
|
||||||
div()
|
h_stack().gap_2().child(
|
||||||
.child(Story::label("Filled"))
|
Button::new("Label")
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
.variant(ButtonVariant::Ghost)
|
||||||
v_stack()
|
.icon(Icon::Plus)
|
||||||
.gap_1()
|
.icon_position(IconPosition::Left),
|
||||||
.child(Label::new(state.to_string()).color(Color::Muted))
|
),
|
||||||
.child(
|
|
||||||
Button::new("Label").variant(ButtonVariant::Filled), // .state(state),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label("Filled – Left Button"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(Label::new(state.to_string()).color(Color::Muted))
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Filled)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Left), // .state(state),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label("Filled – Right Button"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(Label::new(state.to_string()).color(Color::Muted))
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Filled)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Right), // .state(state),
|
|
||||||
)
|
|
||||||
}))),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.child(Story::label("Fixed With"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(Label::new(state.to_string()).color(Color::Muted))
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Filled)
|
|
||||||
// .state(state)
|
|
||||||
.width(Some(rems(6.).into())),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label("Fixed With – Left Icon"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(Label::new(state.to_string()).color(Color::Muted))
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Filled)
|
|
||||||
// .state(state)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Left)
|
|
||||||
.width(Some(rems(6.).into())),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label("Fixed With – Right Icon"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(Label::new(state.to_string()).color(Color::Muted))
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Filled)
|
|
||||||
// .state(state)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Right)
|
|
||||||
.width(Some(rems(6.).into())),
|
|
||||||
)
|
|
||||||
}))),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.child(Story::label("Ghost – Right Icon"))
|
||||||
|
.child(
|
||||||
|
h_stack().gap_2().child(
|
||||||
|
Button::new("Label")
|
||||||
|
.variant(ButtonVariant::Ghost)
|
||||||
|
.icon(Icon::Plus)
|
||||||
|
.icon_position(IconPosition::Right),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div().child(Story::label("Filled")).child(
|
||||||
|
h_stack()
|
||||||
|
.gap_2()
|
||||||
|
.child(Button::new("Label").variant(ButtonVariant::Filled)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(Story::label("Filled – Left Button"))
|
||||||
|
.child(
|
||||||
|
h_stack().gap_2().child(
|
||||||
|
Button::new("Label")
|
||||||
|
.variant(ButtonVariant::Filled)
|
||||||
|
.icon(Icon::Plus)
|
||||||
|
.icon_position(IconPosition::Left),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(Story::label("Filled – Right Button"))
|
||||||
|
.child(
|
||||||
|
h_stack().gap_2().child(
|
||||||
|
Button::new("Label")
|
||||||
|
.variant(ButtonVariant::Filled)
|
||||||
|
.icon(Icon::Plus)
|
||||||
|
.icon_position(IconPosition::Right),
|
||||||
|
),
|
||||||
|
)
|
||||||
.child(Story::label("Button with `on_click`"))
|
.child(Story::label("Button with `on_click`"))
|
||||||
.child(
|
.child(
|
||||||
Button::new("Label")
|
Button::new("Label")
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
use gpui::{Div, Render};
|
|
||||||
use story::Story;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::Input;
|
|
||||||
|
|
||||||
pub struct InputStory;
|
|
||||||
|
|
||||||
impl Render for InputStory {
|
|
||||||
type Element = Div;
|
|
||||||
|
|
||||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
|
||||||
Story::container()
|
|
||||||
.child(Story::title_for::<Input>())
|
|
||||||
.child(Story::label("Default"))
|
|
||||||
.child(div().flex().child(Input::new("Search")))
|
|
||||||
}
|
|
||||||
}
|
|
6
crates/ui2/src/fixed.rs
Normal file
6
crates/ui2/src/fixed.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
use gpui::DefiniteLength;
|
||||||
|
|
||||||
|
pub trait FixedWidth {
|
||||||
|
fn width(self, width: DefiniteLength) -> Self;
|
||||||
|
fn full_width(self) -> Self;
|
||||||
|
}
|
|
@ -3,62 +3,9 @@ pub use gpui::{
|
||||||
ViewContext, WindowContext,
|
ViewContext, WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use crate::clickable::*;
|
||||||
|
pub use crate::fixed::*;
|
||||||
|
pub use crate::selectable::*;
|
||||||
pub use crate::StyledExt;
|
pub use crate::StyledExt;
|
||||||
pub use crate::{ButtonVariant, Color};
|
pub use crate::{ButtonVariant, Color};
|
||||||
pub use theme::ActiveTheme;
|
pub use theme::ActiveTheme;
|
||||||
|
|
||||||
use strum::EnumIter;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
|
||||||
pub enum IconSide {
|
|
||||||
#[default]
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone, EnumIter, strum::Display)]
|
|
||||||
pub enum InteractionState {
|
|
||||||
/// An element that is enabled and not hovered, active, focused, or disabled.
|
|
||||||
///
|
|
||||||
/// This is often referred to as the "default" state.
|
|
||||||
#[default]
|
|
||||||
Enabled,
|
|
||||||
/// An element that is hovered.
|
|
||||||
Hovered,
|
|
||||||
/// An element has an active mouse down or touch start event on it.
|
|
||||||
Active,
|
|
||||||
/// An element that is focused using the keyboard.
|
|
||||||
Focused,
|
|
||||||
/// An element that is disabled.
|
|
||||||
Disabled,
|
|
||||||
/// A toggleable element that is selected, like the active button in a
|
|
||||||
/// button toggle group.
|
|
||||||
Selected,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InteractionState {
|
|
||||||
pub fn if_enabled(&self, enabled: bool) -> Self {
|
|
||||||
if enabled {
|
|
||||||
*self
|
|
||||||
} else {
|
|
||||||
InteractionState::Disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
|
|
||||||
pub enum Selection {
|
|
||||||
#[default]
|
|
||||||
Unselected,
|
|
||||||
Indeterminate,
|
|
||||||
Selected,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Selection {
|
|
||||||
pub fn inverse(&self) -> Self {
|
|
||||||
match self {
|
|
||||||
Self::Unselected | Self::Indeterminate => Self::Selected,
|
|
||||||
Self::Selected => Self::Unselected,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
26
crates/ui2/src/selectable.rs
Normal file
26
crates/ui2/src/selectable.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use gpui::{AnyView, WindowContext};
|
||||||
|
|
||||||
|
pub trait Selectable {
|
||||||
|
fn selected(self, selected: bool) -> Self;
|
||||||
|
fn selected_tooltip(
|
||||||
|
self,
|
||||||
|
tooltip: Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>,
|
||||||
|
) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
|
pub enum Selection {
|
||||||
|
#[default]
|
||||||
|
Unselected,
|
||||||
|
Indeterminate,
|
||||||
|
Selected,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Selection {
|
||||||
|
pub fn inverse(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Unselected | Self::Indeterminate => Self::Selected,
|
||||||
|
Self::Selected => Self::Unselected,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use gpui::{Hsla, WindowContext};
|
use gpui::{Hsla, WindowContext};
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone)]
|
#[derive(Debug, Default, PartialEq, Copy, Clone)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
#[default]
|
#[default]
|
||||||
Default,
|
Default,
|
||||||
|
|
|
@ -12,13 +12,19 @@
|
||||||
#![doc = include_str!("../docs/building-ui.md")]
|
#![doc = include_str!("../docs/building-ui.md")]
|
||||||
#![doc = include_str!("../docs/todo.md")]
|
#![doc = include_str!("../docs/todo.md")]
|
||||||
|
|
||||||
|
mod clickable;
|
||||||
mod components;
|
mod components;
|
||||||
|
mod fixed;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
mod selectable;
|
||||||
mod styled_ext;
|
mod styled_ext;
|
||||||
mod styles;
|
mod styles;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
pub use clickable::*;
|
||||||
pub use components::*;
|
pub use components::*;
|
||||||
|
pub use fixed::*;
|
||||||
pub use prelude::*;
|
pub use prelude::*;
|
||||||
|
pub use selectable::*;
|
||||||
pub use styled_ext::*;
|
pub use styled_ext::*;
|
||||||
pub use styles::*;
|
pub use styles::*;
|
||||||
|
|
|
@ -7,8 +7,8 @@ use gpui::{
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme2::ActiveTheme;
|
use ui::prelude::*;
|
||||||
use ui::{h_stack, menu_handle, ContextMenu, IconButton, InteractionState, Tooltip};
|
use ui::{h_stack, menu_handle, ContextMenu, IconButton, Tooltip};
|
||||||
|
|
||||||
pub enum PanelEvent {
|
pub enum PanelEvent {
|
||||||
ChangePosition,
|
ChangePosition,
|
||||||
|
@ -686,22 +686,26 @@ impl Render for PanelButtons {
|
||||||
let name = entry.panel.persistent_name();
|
let name = entry.panel.persistent_name();
|
||||||
let panel = entry.panel.clone();
|
let panel = entry.panel.clone();
|
||||||
|
|
||||||
let mut button: IconButton = if i == active_index && is_open {
|
let is_active_button = i == active_index && is_open;
|
||||||
|
|
||||||
|
let (action, tooltip) = if is_active_button {
|
||||||
let action = dock.toggle_action();
|
let action = dock.toggle_action();
|
||||||
|
|
||||||
let tooltip: SharedString =
|
let tooltip: SharedString =
|
||||||
format!("Close {} dock", dock.position.to_label()).into();
|
format!("Close {} dock", dock.position.to_label()).into();
|
||||||
IconButton::new(name, icon)
|
|
||||||
.state(InteractionState::Active)
|
(action, tooltip)
|
||||||
.action(action.boxed_clone())
|
|
||||||
.tooltip(move |cx| Tooltip::for_action(tooltip.clone(), &*action, cx))
|
|
||||||
} else {
|
} else {
|
||||||
let action = entry.panel.toggle_action(cx);
|
let action = entry.panel.toggle_action(cx);
|
||||||
|
|
||||||
IconButton::new(name, icon)
|
(action, name.into())
|
||||||
.action(action.boxed_clone())
|
|
||||||
.tooltip(move |cx| Tooltip::for_action(name, &*action, cx))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let button = IconButton::new(name, icon)
|
||||||
|
.selected(is_active_button)
|
||||||
|
.action(action.boxed_clone())
|
||||||
|
.tooltip(move |cx| Tooltip::for_action(tooltip.clone(), &*action, cx));
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
menu_handle(name)
|
menu_handle(name)
|
||||||
.menu(move |cx| {
|
.menu(move |cx| {
|
||||||
|
|
|
@ -1482,18 +1482,14 @@ impl Pane {
|
||||||
.gap_px()
|
.gap_px()
|
||||||
.child(
|
.child(
|
||||||
div().border().border_color(gpui::red()).child(
|
div().border().border_color(gpui::red()).child(
|
||||||
IconButton::new("navigate_backward", Icon::ArrowLeft).state(
|
IconButton::new("navigate_backward", Icon::ArrowLeft)
|
||||||
InteractionState::Enabled
|
.disabled(!self.can_navigate_backward()),
|
||||||
.if_enabled(self.can_navigate_backward()),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div().border().border_color(gpui::red()).child(
|
div().border().border_color(gpui::red()).child(
|
||||||
IconButton::new("navigate_forward", Icon::ArrowRight).state(
|
IconButton::new("navigate_forward", Icon::ArrowRight)
|
||||||
InteractionState::Enabled
|
.disabled(!self.can_navigate_forward()),
|
||||||
.if_enabled(self.can_navigate_forward()),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -5,7 +5,7 @@ use gpui::{
|
||||||
div, AnyView, Div, IntoElement, ParentElement, Render, Styled, Subscription, View, ViewContext,
|
div, AnyView, Div, IntoElement, ParentElement, Render, Styled, Subscription, View, ViewContext,
|
||||||
WindowContext,
|
WindowContext,
|
||||||
};
|
};
|
||||||
use theme2::ActiveTheme;
|
use ui::prelude::*;
|
||||||
use ui::{h_stack, Button, Icon, IconButton};
|
use ui::{h_stack, Button, Icon, IconButton};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use gpui::{
|
||||||
div, AnyView, Div, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View,
|
div, AnyView, Div, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View,
|
||||||
ViewContext, WindowContext,
|
ViewContext, WindowContext,
|
||||||
};
|
};
|
||||||
use theme2::ActiveTheme;
|
use ui::prelude::*;
|
||||||
use ui::{h_stack, v_stack, Button, Color, Icon, IconButton, Label};
|
use ui::{h_stack, v_stack, Button, Color, Icon, IconButton, Label};
|
||||||
|
|
||||||
pub enum ToolbarItemEvent {
|
pub enum ToolbarItemEvent {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue