Detailed changes
@@ -37,7 +37,10 @@ use gpui::{
};
use project::Project;
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 workspace::{notifications::NotifyResultExt, Workspace};
@@ -298,6 +301,27 @@ impl Render for CollabTitlebarItem {
})
.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,
WindowContext,
};
-use theme2::ActiveTheme;
+use ui::prelude::*;
actions!(ActionA, ActionB, ActionC);
@@ -4,7 +4,7 @@ use gpui::{
};
use picker::{Picker, PickerDelegate};
use std::sync::Arc;
-use theme2::ActiveTheme;
+use ui::prelude::*;
use ui::{Label, ListItem};
pub struct PickerStory {
@@ -1,5 +1,5 @@
use gpui::{div, prelude::*, px, Div, Render, SharedString, Stateful, Styled, View, WindowContext};
-use theme2::ActiveTheme;
+use ui::prelude::*;
use ui::Tooltip;
pub struct ScrollStory;
@@ -19,7 +19,6 @@ pub enum ComponentStory {
Focus,
Icon,
IconButton,
- Input,
Keybinding,
Label,
ListItem,
@@ -39,7 +38,6 @@ impl ComponentStory {
Self::Focus => FocusStory::view(cx).into(),
Self::Icon => cx.build_view(|_| ui::IconStory).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::Label => cx.build_view(|_| ui::LabelStory).into(),
Self::ListItem => cx.build_view(|_| ui::ListItemStory).into(),
@@ -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 button;
+mod button2;
mod checkbox;
mod context_menu;
mod disclosure;
mod divider;
mod icon;
mod icon_button;
-mod input;
mod keybinding;
mod label;
mod list;
@@ -21,13 +21,13 @@ mod stories;
pub use avatar::*;
pub use button::*;
+pub use button2::*;
pub use checkbox::*;
pub use context_menu::*;
pub use disclosure::*;
pub use divider::*;
pub use icon::*;
pub use icon_button::*;
-pub use input::*;
pub use keybinding::*;
pub use label::*;
pub use list::*;
@@ -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,
size: IconSize,
variant: ButtonVariant,
- state: InteractionState,
+ disabled: bool,
selected: bool,
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
@@ -18,9 +18,9 @@ impl RenderOnce for IconButton {
type Rendered = Stateful<Div>;
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- let icon_color = match (self.state, self.color) {
- (InteractionState::Disabled, _) => Color::Disabled,
- (InteractionState::Active, _) => Color::Selected,
+ let icon_color = match (self.disabled, self.selected, self.color) {
+ (true, _, _) => Color::Disabled,
+ (false, true, _) => Color::Selected,
_ => self.color,
};
@@ -82,8 +82,8 @@ impl IconButton {
color: Color::default(),
size: Default::default(),
variant: ButtonVariant::default(),
- state: InteractionState::default(),
selected: false,
+ disabled: false,
tooltip: None,
on_click: None,
}
@@ -109,13 +109,13 @@ impl IconButton {
self
}
- pub fn state(mut self, state: InteractionState) -> Self {
- self.state = state;
+ pub fn selected(mut self, selected: bool) -> Self {
+ self.selected = selected;
self
}
- pub fn selected(mut self, selected: bool) -> Self {
- self.selected = selected;
+ pub fn disabled(mut self, disabled: bool) -> Self {
+ self.disabled = disabled;
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 icon;
mod icon_button;
-mod input;
mod keybinding;
mod label;
mod list_item;
-
pub use avatar::*;
pub use button::*;
pub use checkbox::*;
pub use context_menu::*;
pub use icon::*;
pub use icon_button::*;
-pub use input::*;
pub use keybinding::*;
pub use label::*;
pub use list_item::*;
@@ -1,9 +1,8 @@
-use gpui::{rems, Div, Render};
+use gpui::{Div, Render};
use story::Story;
-use strum::IntoEnumIterator;
use crate::prelude::*;
-use crate::{h_stack, v_stack, Button, Icon, IconPosition, Label};
+use crate::{h_stack, Button, Icon, IconPosition};
pub struct ButtonStory;
@@ -11,8 +10,6 @@ impl Render for ButtonStory {
type Element = Div;
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
- let states = InteractionState::iter();
-
Story::container()
.child(Story::title_for::<Button>())
.child(
@@ -20,121 +17,56 @@ impl Render for ButtonStory {
.flex()
.gap_8()
.child(
- div()
- .child(Story::label("Ghost (Default)"))
- .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), // .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(
- div()
- .child(Story::label("Filled"))
- .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),
- )
- })))
- .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),
- )
- }))),
+ div().child(Story::label("Ghost (Default)")).child(
+ h_stack()
+ .gap_2()
+ .child(Button::new("Label").variant(ButtonVariant::Ghost)),
+ ),
)
+ .child(Story::label("Ghost – Left Icon"))
.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())),
- )
- }))),
+ h_stack().gap_2().child(
+ Button::new("Label")
+ .variant(ButtonVariant::Ghost)
+ .icon(Icon::Plus)
+ .icon_position(IconPosition::Left),
+ ),
),
)
+ .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(
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")))
- }
-}
@@ -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,
};
+pub use crate::clickable::*;
+pub use crate::fixed::*;
+pub use crate::selectable::*;
pub use crate::StyledExt;
pub use crate::{ButtonVariant, Color};
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,
- }
- }
-}
@@ -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 theme::ActiveTheme;
-#[derive(Default, PartialEq, Copy, Clone)]
+#[derive(Debug, Default, PartialEq, Copy, Clone)]
pub enum Color {
#[default]
Default,
@@ -12,13 +12,19 @@
#![doc = include_str!("../docs/building-ui.md")]
#![doc = include_str!("../docs/todo.md")]
+mod clickable;
mod components;
+mod fixed;
pub mod prelude;
+mod selectable;
mod styled_ext;
mod styles;
pub mod utils;
+pub use clickable::*;
pub use components::*;
+pub use fixed::*;
pub use prelude::*;
+pub use selectable::*;
pub use styled_ext::*;
pub use styles::*;
@@ -7,8 +7,8 @@ use gpui::{
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
-use theme2::ActiveTheme;
-use ui::{h_stack, menu_handle, ContextMenu, IconButton, InteractionState, Tooltip};
+use ui::prelude::*;
+use ui::{h_stack, menu_handle, ContextMenu, IconButton, Tooltip};
pub enum PanelEvent {
ChangePosition,
@@ -686,22 +686,26 @@ impl Render for PanelButtons {
let name = entry.panel.persistent_name();
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 tooltip: SharedString =
format!("Close {} dock", dock.position.to_label()).into();
- IconButton::new(name, icon)
- .state(InteractionState::Active)
- .action(action.boxed_clone())
- .tooltip(move |cx| Tooltip::for_action(tooltip.clone(), &*action, cx))
+
+ (action, tooltip)
} else {
let action = entry.panel.toggle_action(cx);
- IconButton::new(name, icon)
- .action(action.boxed_clone())
- .tooltip(move |cx| Tooltip::for_action(name, &*action, cx))
+ (action, name.into())
};
+ 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(
menu_handle(name)
.menu(move |cx| {
@@ -1482,18 +1482,14 @@ impl Pane {
.gap_px()
.child(
div().border().border_color(gpui::red()).child(
- IconButton::new("navigate_backward", Icon::ArrowLeft).state(
- InteractionState::Enabled
- .if_enabled(self.can_navigate_backward()),
- ),
+ IconButton::new("navigate_backward", Icon::ArrowLeft)
+ .disabled(!self.can_navigate_backward()),
),
)
.child(
div().border().border_color(gpui::red()).child(
- IconButton::new("navigate_forward", Icon::ArrowRight).state(
- InteractionState::Enabled
- .if_enabled(self.can_navigate_forward()),
- ),
+ IconButton::new("navigate_forward", Icon::ArrowRight)
+ .disabled(!self.can_navigate_forward()),
),
),
),
@@ -5,7 +5,7 @@ use gpui::{
div, AnyView, Div, IntoElement, ParentElement, Render, Styled, Subscription, View, ViewContext,
WindowContext,
};
-use theme2::ActiveTheme;
+use ui::prelude::*;
use ui::{h_stack, Button, Icon, IconButton};
use util::ResultExt;
@@ -3,7 +3,7 @@ use gpui::{
div, AnyView, Div, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View,
ViewContext, WindowContext,
};
-use theme2::ActiveTheme;
+use ui::prelude::*;
use ui::{h_stack, v_stack, Button, Color, Icon, IconButton, Label};
pub enum ToolbarItemEvent {