Detailed changes
@@ -178,6 +178,7 @@ use gpui::{
use project::{Fs, Project};
use serde_derive::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
+use ui::prelude::*;
use ui::{
h_stack, v_stack, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconElement, IconSize,
Label, List, ListHeader, ListItem, Tooltip,
@@ -2333,18 +2334,20 @@ impl CollabPanel {
}
fn render_signed_out(&mut self, cx: &mut ViewContext<Self>) -> Div {
- v_stack().child(Button::new("Sign in to collaborate").on_click(cx.listener(
- |this, _, cx| {
- let client = this.client.clone();
- cx.spawn(|_, mut cx| async move {
- client
- .authenticate_and_connect(true, &cx)
- .await
- .notify_async_err(&mut cx);
- })
- .detach()
- },
- )))
+ v_stack().child(
+ Button::new("sign_in", "Sign in to collaborate").on_click(cx.listener(
+ |this, _, cx| {
+ let client = this.client.clone();
+ cx.spawn(|_, mut cx| async move {
+ client
+ .authenticate_and_connect(true, &cx)
+ .await
+ .notify_async_err(&mut cx);
+ })
+ .detach()
+ },
+ )),
+ )
}
fn render_signed_in(&mut self, cx: &mut ViewContext<Self>) -> List {
@@ -2559,7 +2562,7 @@ impl CollabPanel {
.group_hover("", |style| style.visible())
.child(
IconButton::new("remove_contact", Icon::Close)
- .color(Color::Muted)
+ .icon_color(Color::Muted)
.tooltip(|cx| Tooltip::text("Remove Contact", cx))
.on_click(cx.listener({
let github_login = github_login.clone();
@@ -2623,13 +2626,13 @@ impl CollabPanel {
.on_click(cx.listener(move |this, _, cx| {
this.respond_to_contact_request(user_id, false, cx);
}))
- .color(color)
+ .icon_color(color)
.tooltip(|cx| Tooltip::text("Decline invite", cx)),
IconButton::new("remove_contact", Icon::Check)
.on_click(cx.listener(move |this, _, cx| {
this.respond_to_contact_request(user_id, true, cx);
}))
- .color(color)
+ .icon_color(color)
.tooltip(|cx| Tooltip::text("Accept invite", cx)),
]
} else {
@@ -2638,7 +2641,7 @@ impl CollabPanel {
.on_click(cx.listener(move |this, _, cx| {
this.remove_contact(user_id, &github_login, cx);
}))
- .color(color)
+ .icon_color(color)
.tooltip(|cx| Tooltip::text("Cancel invite", cx))]
};
@@ -2780,7 +2783,7 @@ impl CollabPanel {
"channel_chat",
Icon::MessageBubbles,
)
- .color(if has_messages_notification {
+ .icon_color(if has_messages_notification {
Color::Default
} else {
Color::Muted
@@ -2795,7 +2798,7 @@ impl CollabPanel {
.group_hover("", |style| style.visible())
.child(
IconButton::new("channel_notes", Icon::File)
- .color(if has_notes_notification {
+ .icon_color(if has_notes_notification {
Color::Default
} else {
Color::Muted
@@ -37,10 +37,7 @@ use gpui::{
};
use project::Project;
use theme::ActiveTheme;
-use ui::{
- h_stack, Avatar, Button, ButtonCommon, ButtonLike, ButtonVariant, Clickable, Color, IconButton,
- IconElement, IconSize, KeyBinding, Tooltip,
-};
+use ui::{h_stack, prelude::*, Avatar, Button, ButtonStyle2, IconButton, KeyBinding, Tooltip};
use util::ResultExt;
use workspace::{notifications::NotifyResultExt, Workspace};
@@ -156,8 +153,8 @@ impl Render for CollabTitlebarItem {
.border_color(gpui::red())
.id("project_owner_indicator")
.child(
- Button::new("player")
- .variant(ButtonVariant::Ghost)
+ Button::new("player", "player")
+ .style(ButtonStyle2::Subtle)
.color(Some(Color::Player(0))),
)
.tooltip(move |cx| Tooltip::text("Toggle following", cx)),
@@ -168,7 +165,10 @@ impl Render for CollabTitlebarItem {
.border()
.border_color(gpui::red())
.id("titlebar_project_menu_button")
- .child(Button::new("project_name").variant(ButtonVariant::Ghost))
+ .child(
+ Button::new("project_name", "project_name")
+ .style(ButtonStyle2::Subtle),
+ )
.tooltip(move |cx| Tooltip::text("Recent Projects", cx)),
)
// TODO - Add git menu
@@ -178,8 +178,8 @@ impl Render for CollabTitlebarItem {
.border_color(gpui::red())
.id("titlebar_git_menu_button")
.child(
- Button::new("branch_name")
- .variant(ButtonVariant::Ghost)
+ Button::new("branch_name", "branch_name")
+ .style(ButtonStyle2::Subtle)
.color(Some(Color::Muted)),
)
.tooltip(move |cx| {
@@ -238,7 +238,10 @@ impl Render for CollabTitlebarItem {
h_stack()
.child(
h_stack()
- .child(Button::new(if is_shared { "Unshare" } else { "Share" }))
+ .child(Button::new(
+ "toggle_sharing",
+ if is_shared { "Unshare" } else { "Share" },
+ ))
.child(IconButton::new("leave-call", ui::Icon::Exit).on_click({
let workspace = workspace.clone();
move |_, cx| {
@@ -291,7 +294,7 @@ impl Render for CollabTitlebarItem {
this.child(ui::Avatar::data(avatar))
})
} else {
- this.child(Button::new("Sign in").on_click(move |_, cx| {
+ this.child(Button::new("sign_in", "Sign in").on_click(move |_, cx| {
let client = client.clone();
cx.spawn(move |mut cx| async move {
client
@@ -301,27 +304,6 @@ 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,10 +2,11 @@ use crate::notification_window_options;
use call::{ActiveCall, IncomingCall};
use futures::StreamExt;
use gpui::{
- div, green, px, red, AppContext, Div, Element, ParentElement, Render, RenderOnce,
- StatefulInteractiveElement, Styled, ViewContext, VisualContext as _, WindowHandle,
+ div, px, red, AppContext, Div, Element, ParentElement, Render, RenderOnce, Styled, ViewContext,
+ VisualContext as _, WindowHandle,
};
use std::sync::{Arc, Weak};
+use ui::prelude::*;
use ui::{h_stack, v_stack, Avatar, Button, Label};
use util::ResultExt;
use workspace::AppState;
@@ -199,14 +200,24 @@ impl IncomingCallNotification {
fn render_buttons(&self, cx: &mut ViewContext<Self>) -> impl Element {
h_stack()
- .child(Button::new("Accept").render(cx).bg(green()).on_click({
- let state = self.state.clone();
- move |_, cx| state.respond(true, cx)
- }))
- .child(Button::new("Decline").render(cx).bg(red()).on_click({
- let state = self.state.clone();
- move |_, cx| state.respond(false, cx)
- }))
+ .child(
+ Button::new("accept", "Accept")
+ .render(cx)
+ // .bg(green())
+ .on_click({
+ let state = self.state.clone();
+ move |_, cx| state.respond(true, cx)
+ }),
+ )
+ .child(
+ Button::new("decline", "Decline")
+ .render(cx)
+ // .bg(red())
+ .on_click({
+ let state = self.state.clone();
+ move |_, cx| state.respond(false, cx)
+ }),
+ )
// enum Accept {}
// enum Decline {}
@@ -1,5 +1,6 @@
use crate::ProjectDiagnosticsEditor;
use gpui::{div, Div, EventEmitter, ParentElement, Render, ViewContext, WeakView};
+use ui::prelude::*;
use ui::{Icon, IconButton, Tooltip};
use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
@@ -99,7 +99,8 @@ use text::{OffsetUtf16, Rope};
use theme::{
ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings,
};
-use ui::{h_stack, v_stack, HighlightedLabel, IconButton, Popover, StyledExt, Tooltip};
+use ui::prelude::*;
+use ui::{h_stack, v_stack, HighlightedLabel, IconButton, Popover, Tooltip};
use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
use workspace::{
item::{ItemEvent, ItemHandle},
@@ -4391,7 +4392,7 @@ impl Editor {
editor.fold_at(&FoldAt { buffer_row }, cx);
}
}))
- .color(ui::Color::Muted)
+ .icon_color(ui::Color::Muted)
})
})
.flatten()
@@ -48,6 +48,7 @@ use std::{
};
use sum_tree::Bias;
use theme::{ActiveTheme, PlayerColor};
+use ui::prelude::*;
use ui::{h_stack, IconButton, Tooltip};
use util::ResultExt;
use workspace::item::Item;
@@ -18,7 +18,7 @@ use project::search::SearchQuery;
use serde::Deserialize;
use std::{any::Any, sync::Arc};
-use ui::{h_stack, ButtonGroup, Icon, IconButton, IconElement};
+use ui::{h_stack, Icon, IconButton, IconElement};
use util::ResultExt;
use workspace::{
item::ItemHandle,
@@ -214,10 +214,11 @@ impl Render for BufferSearchBar {
.child(
h_stack()
.flex_none()
- .child(ButtonGroup::new(vec![
- search_button_for_mode(SearchMode::Text),
- search_button_for_mode(SearchMode::Regex),
- ]))
+ .child(
+ h_stack()
+ .child(search_button_for_mode(SearchMode::Text))
+ .child(search_button_for_mode(SearchMode::Regex)),
+ )
.when(supported_options.replacement, |this| {
this.child(super::toggle_replace_button(self.replace_enabled))
}),
@@ -586,8 +587,7 @@ impl BufferSearchBar {
// let style = theme.search.action_button.clone();
- IconButton::new(0, ui::Icon::SelectAll)
- .on_click(|_, cx| cx.dispatch_action(Box::new(SelectAllMatches)))
+ IconButton::new(0, ui::Icon::SelectAll).action(Box::new(SelectAllMatches))
}
pub fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext<Self>) {
@@ -3,7 +3,8 @@ pub use buffer_search::BufferSearchBar;
use gpui::{actions, Action, AppContext, IntoElement};
pub use mode::SearchMode;
use project::search::SearchQuery;
-use ui::ButtonVariant;
+use ui::prelude::*;
+use ui::{ButtonStyle2, Icon, IconButton};
//pub use project_search::{ProjectSearchBar, ProjectSearchView};
// use theme::components::{
// action_button::Button, svg::Svg, ComponentExt, IconButtonStyle, ToggleIconButtonStyle,
@@ -83,35 +84,35 @@ impl SearchOptions {
}
pub fn as_button(&self, active: bool) -> impl IntoElement {
- ui::IconButton::new(0, self.icon())
+ IconButton::new(0, self.icon())
.on_click({
let action = self.to_toggle_action();
move |_, cx| {
cx.dispatch_action(action.boxed_clone());
}
})
- .variant(ui::ButtonVariant::Ghost)
- .when(active, |button| button.variant(ButtonVariant::Filled))
+ .style(ButtonStyle2::Subtle)
+ .when(active, |button| button.style(ButtonStyle2::Filled))
}
}
fn toggle_replace_button(active: bool) -> impl IntoElement {
// todo: add toggle_replace button
- ui::IconButton::new(0, ui::Icon::Replace)
+ IconButton::new(0, Icon::Replace)
.on_click(|_, cx| {
cx.dispatch_action(Box::new(ToggleReplace));
cx.notify();
})
- .variant(ui::ButtonVariant::Ghost)
- .when(active, |button| button.variant(ButtonVariant::Filled))
+ .style(ButtonStyle2::Subtle)
+ .when(active, |button| button.style(ButtonStyle2::Filled))
}
fn render_replace_button(
action: impl Action + 'static + Send + Sync,
- icon: ui::Icon,
+ icon: Icon,
) -> impl IntoElement {
// todo: add tooltip
- ui::IconButton::new(0, icon).on_click(move |_, cx| {
+ IconButton::new(0, icon).on_click(move |_, cx| {
cx.dispatch_action(action.boxed_clone());
})
}
@@ -1,5 +1,6 @@
use gpui::{ClickEvent, IntoElement, WindowContext};
-use ui::{Button, ButtonVariant, IconButton};
+use ui::prelude::*;
+use ui::{Button, IconButton};
use crate::mode::SearchMode;
@@ -23,13 +24,7 @@ pub(crate) fn render_search_mode_button(
is_active: bool,
on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
) -> Button {
- let button_variant = if is_active {
- ButtonVariant::Filled
- } else {
- ButtonVariant::Ghost
- };
-
- Button::new(mode.label())
+ Button::new(mode.label(), mode.label())
+ .selected(is_active)
.on_click(on_click)
- .variant(button_variant)
}
@@ -1,12 +1,10 @@
mod avatar;
mod button;
-mod button2;
mod checkbox;
mod context_menu;
mod disclosure;
mod divider;
mod icon;
-mod icon_button;
mod keybinding;
mod label;
mod list;
@@ -19,13 +17,11 @@ 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 keybinding::*;
pub use label::*;
pub use list::*;
@@ -1,228 +0,0 @@
-use gpui::{
- ClickEvent, DefiniteLength, Div, Hsla, IntoElement, StatefulInteractiveElement, WindowContext,
-};
-use std::rc::Rc;
-
-use crate::prelude::*;
-use crate::{h_stack, Color, Icon, IconButton, IconElement, Label, LineHeightStyle};
-
-/// Provides the flexibility to use either a standard
-/// button or an icon button in a given context.
-pub enum ButtonOrIconButton {
- Button(Button),
- IconButton(IconButton),
-}
-
-impl From<Button> for ButtonOrIconButton {
- fn from(value: Button) -> Self {
- Self::Button(value)
- }
-}
-
-impl From<IconButton> for ButtonOrIconButton {
- fn from(value: IconButton) -> Self {
- Self::IconButton(value)
- }
-}
-
-#[derive(Default, PartialEq, Clone, Copy)]
-pub enum IconPosition {
- #[default]
- Left,
- Right,
-}
-
-#[derive(Default, Copy, Clone, PartialEq)]
-pub enum ButtonVariant {
- #[default]
- Ghost,
- Filled,
-}
-
-impl ButtonVariant {
- pub fn bg_color(&self, cx: &mut WindowContext) -> Hsla {
- match self {
- ButtonVariant::Ghost => cx.theme().colors().ghost_element_background,
- ButtonVariant::Filled => cx.theme().colors().element_background,
- }
- }
-
- pub fn bg_color_hover(&self, cx: &mut WindowContext) -> Hsla {
- match self {
- ButtonVariant::Ghost => cx.theme().colors().ghost_element_hover,
- ButtonVariant::Filled => cx.theme().colors().element_hover,
- }
- }
-
- pub fn bg_color_active(&self, cx: &mut WindowContext) -> Hsla {
- match self {
- ButtonVariant::Ghost => cx.theme().colors().ghost_element_active,
- ButtonVariant::Filled => cx.theme().colors().element_active,
- }
- }
-}
-
-#[derive(IntoElement)]
-pub struct Button {
- disabled: bool,
- click_handler: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext)>>,
- icon: Option<Icon>,
- icon_position: Option<IconPosition>,
- label: SharedString,
- variant: ButtonVariant,
- width: Option<DefiniteLength>,
- color: Option<Color>,
-}
-
-impl RenderOnce for Button {
- type Rendered = gpui::Stateful<Div>;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- let (icon_color, label_color) = match (self.disabled, self.color) {
- (true, _) => (Color::Disabled, Color::Disabled),
- (_, None) => (Color::Default, Color::Default),
- (_, Some(color)) => (Color::from(color), color),
- };
-
- let mut button = h_stack()
- .id(SharedString::from(format!("{}", self.label)))
- .relative()
- .p_1()
- .text_ui()
- .rounded_md()
- .bg(self.variant.bg_color(cx))
- .cursor_pointer()
- .hover(|style| style.bg(self.variant.bg_color_hover(cx)))
- .active(|style| style.bg(self.variant.bg_color_active(cx)));
-
- match (self.icon, self.icon_position) {
- (Some(_), Some(IconPosition::Left)) => {
- button = button
- .gap_1()
- .child(self.render_label(label_color))
- .children(self.render_icon(icon_color))
- }
- (Some(_), Some(IconPosition::Right)) => {
- button = button
- .gap_1()
- .children(self.render_icon(icon_color))
- .child(self.render_label(label_color))
- }
- (_, _) => button = button.child(self.render_label(label_color)),
- }
-
- if let Some(width) = self.width {
- button = button.w(width).justify_center();
- }
-
- if let Some(click_handler) = self.click_handler.clone() {
- button = button.on_click(move |event, cx| {
- click_handler(event, cx);
- });
- }
-
- button
- }
-}
-
-impl Button {
- pub fn new(label: impl Into<SharedString>) -> Self {
- Self {
- disabled: false,
- click_handler: None,
- icon: None,
- icon_position: None,
- label: label.into(),
- variant: Default::default(),
- width: Default::default(),
- color: None,
- }
- }
-
- pub fn ghost(label: impl Into<SharedString>) -> Self {
- Self::new(label).variant(ButtonVariant::Ghost)
- }
-
- pub fn variant(mut self, variant: ButtonVariant) -> Self {
- self.variant = variant;
- self
- }
-
- pub fn icon(mut self, icon: Icon) -> Self {
- self.icon = Some(icon);
- self
- }
-
- pub fn icon_position(mut self, icon_position: IconPosition) -> Self {
- if self.icon.is_none() {
- panic!("An icon must be present if an icon_position is provided.");
- }
- self.icon_position = Some(icon_position);
- self
- }
-
- pub fn width(mut self, width: Option<DefiniteLength>) -> Self {
- self.width = width;
- self
- }
-
- pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
- self.click_handler = Some(Rc::new(handler));
- self
- }
-
- pub fn disabled(mut self, disabled: bool) -> Self {
- self.disabled = disabled;
- self
- }
-
- pub fn color(mut self, color: Option<Color>) -> Self {
- self.color = color;
- self
- }
-
- pub fn label_color(&self, color: Option<Color>) -> Color {
- if self.disabled {
- Color::Disabled
- } else if let Some(color) = color {
- color
- } else {
- Default::default()
- }
- }
-
- fn render_label(&self, color: Color) -> Label {
- Label::new(self.label.clone())
- .color(color)
- .line_height_style(LineHeightStyle::UILabel)
- }
-
- fn render_icon(&self, icon_color: Color) -> Option<IconElement> {
- self.icon.map(|i| IconElement::new(i).color(icon_color))
- }
-}
-
-#[derive(IntoElement)]
-pub struct ButtonGroup {
- buttons: Vec<Button>,
-}
-
-impl RenderOnce for ButtonGroup {
- type Rendered = Div;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- let mut group = h_stack();
-
- for button in self.buttons.into_iter() {
- group = group.child(button.render(cx));
- }
-
- group
- }
-}
-
-impl ButtonGroup {
- pub fn new(buttons: Vec<Button>) -> Self {
- Self { buttons }
- }
-}
@@ -0,0 +1,91 @@
+use gpui::AnyView;
+
+use crate::prelude::*;
+use crate::{ButtonCommon, ButtonLike, ButtonSize2, ButtonStyle2, Label, LineHeightStyle};
+
+#[derive(IntoElement)]
+pub struct Button {
+ base: ButtonLike,
+ label: SharedString,
+ label_color: Option<Color>,
+ selected: bool,
+}
+
+impl Button {
+ pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
+ Self {
+ base: ButtonLike::new(id),
+ label: label.into(),
+ label_color: None,
+ selected: false,
+ }
+ }
+
+ pub fn selected(mut self, selected: bool) -> Self {
+ self.selected = selected;
+ self
+ }
+
+ pub fn color(mut self, label_color: impl Into<Option<Color>>) -> Self {
+ self.label_color = label_color.into();
+ self
+ }
+}
+
+impl Clickable for Button {
+ fn on_click(
+ mut self,
+ handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
+ ) -> Self {
+ self.base = self.base.on_click(handler);
+ self
+ }
+}
+
+impl Disableable for Button {
+ fn disabled(mut self, disabled: bool) -> Self {
+ self.base = self.base.disabled(disabled);
+ self
+ }
+}
+
+impl ButtonCommon for Button {
+ fn id(&self) -> &ElementId {
+ self.base.id()
+ }
+
+ fn style(mut self, style: ButtonStyle2) -> Self {
+ self.base = self.base.style(style);
+ self
+ }
+
+ fn size(mut self, size: ButtonSize2) -> Self {
+ self.base = self.base.size(size);
+ self
+ }
+
+ fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
+ self.base = self.base.tooltip(tooltip);
+ self
+ }
+}
+
+impl RenderOnce for Button {
+ type Rendered = ButtonLike;
+
+ fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
+ let label_color = if self.base.disabled {
+ Color::Disabled
+ } else if self.selected {
+ Color::Selected
+ } else {
+ Color::Default
+ };
+
+ self.base.child(
+ Label::new(self.label)
+ .color(label_color)
+ .line_height_style(LineHeightStyle::UILabel),
+ )
+ }
+}
@@ -1,31 +1,17 @@
-use gpui::{
- rems, AnyElement, AnyView, ClickEvent, Div, Hsla, IntoElement, Rems, Stateful,
- StatefulInteractiveElement, WindowContext,
-};
+use gpui::{rems, AnyElement, AnyView, ClickEvent, Div, Hsla, Rems, Stateful};
use smallvec::SmallVec;
-use crate::{h_stack, prelude::*};
+use crate::h_stack;
+use crate::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,
+pub trait ButtonCommon: Clickable + Disableable {
+ fn id(&self) -> &ElementId;
+ fn style(self, style: ButtonStyle2) -> Self;
+ fn size(self, size: ButtonSize2) -> Self;
+ fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
}
-#[derive(Default, PartialEq, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
pub enum ButtonStyle2 {
#[default]
Filled,
@@ -34,7 +20,7 @@ pub enum ButtonStyle2 {
Transparent,
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
pub struct ButtonStyle {
pub background: Hsla,
pub border_color: Hsla,
@@ -181,82 +167,11 @@ impl ButtonSize2 {
}
}
-// 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 + Disableable {
- fn id(&self) -> &ElementId;
- fn style(self, style: ButtonStyle2) -> Self;
- fn size(self, size: ButtonSize2) -> Self;
- fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> 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,
+ pub(super) style: ButtonStyle2,
+ pub(super) disabled: bool,
size: ButtonSize2,
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
@@ -325,6 +240,12 @@ impl ButtonCommon for ButtonLike {
}
}
+impl ParentElement for ButtonLike {
+ fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
+ &mut self.children
+ }
+}
+
impl RenderOnce for ButtonLike {
type Rendered = Stateful<Div>;
@@ -349,57 +270,3 @@ impl RenderOnce for ButtonLike {
.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...
-// }
@@ -0,0 +1,102 @@
+use gpui::{Action, AnyView};
+
+use crate::prelude::*;
+use crate::{ButtonCommon, ButtonLike, ButtonSize2, ButtonStyle2, Icon, IconElement, IconSize};
+
+#[derive(IntoElement)]
+pub struct IconButton {
+ base: ButtonLike,
+ icon: Icon,
+ icon_size: IconSize,
+ icon_color: Color,
+ selected: bool,
+}
+
+impl IconButton {
+ pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
+ Self {
+ base: ButtonLike::new(id),
+ icon,
+ icon_size: IconSize::default(),
+ icon_color: Color::Default,
+ selected: false,
+ }
+ }
+
+ pub fn selected(mut self, selected: bool) -> Self {
+ self.selected = selected;
+ self
+ }
+
+ pub fn icon_size(mut self, icon_size: IconSize) -> Self {
+ self.icon_size = icon_size;
+ self
+ }
+
+ pub fn icon_color(mut self, icon_color: Color) -> Self {
+ self.icon_color = icon_color;
+ self
+ }
+
+ pub fn action(self, action: Box<dyn Action>) -> Self {
+ self.on_click(move |_event, cx| cx.dispatch_action(action.boxed_clone()))
+ }
+}
+
+impl Clickable for IconButton {
+ fn on_click(
+ mut self,
+ handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
+ ) -> Self {
+ self.base = self.base.on_click(handler);
+ self
+ }
+}
+
+impl Disableable for IconButton {
+ fn disabled(mut self, disabled: bool) -> Self {
+ self.base = self.base.disabled(disabled);
+ self
+ }
+}
+
+impl ButtonCommon for IconButton {
+ fn id(&self) -> &ElementId {
+ self.base.id()
+ }
+
+ fn style(mut self, style: ButtonStyle2) -> Self {
+ self.base = self.base.style(style);
+ self
+ }
+
+ fn size(mut self, size: ButtonSize2) -> Self {
+ self.base = self.base.size(size);
+ self
+ }
+
+ fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
+ self.base = self.base.tooltip(tooltip);
+ self
+ }
+}
+
+impl RenderOnce for IconButton {
+ type Rendered = ButtonLike;
+
+ fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
+ let icon_color = if self.base.disabled {
+ Color::Disabled
+ } else if self.selected {
+ Color::Selected
+ } else {
+ self.icon_color
+ };
+
+ self.base.child(
+ IconElement::new(self.icon)
+ .size(self.icon_size)
+ .color(icon_color),
+ )
+ }
+}
@@ -0,0 +1,7 @@
+mod button;
+mod button_like;
+mod icon_button;
+
+pub use button::*;
+pub use button_like::*;
+pub use icon_button::*;
@@ -39,8 +39,8 @@ impl RenderOnce for Disclosure {
false => Icon::ChevronRight,
},
)
- .color(Color::Muted)
- .size(IconSize::Small)
+ .icon_color(Color::Muted)
+ .icon_size(IconSize::Small)
.when_some(self.on_toggle, move |this, on_toggle| {
this.on_click(move |event, cx| on_toggle(event, cx))
})
@@ -1,135 +0,0 @@
-use crate::{h_stack, prelude::*, Icon, IconElement, IconSize};
-use gpui::{prelude::*, Action, AnyView, ClickEvent, Div, Stateful};
-
-#[derive(IntoElement)]
-pub struct IconButton {
- id: ElementId,
- icon: Icon,
- color: Color,
- size: IconSize,
- variant: ButtonVariant,
- disabled: bool,
- selected: bool,
- tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
- on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
-}
-
-impl RenderOnce for IconButton {
- type Rendered = Stateful<Div>;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- let icon_color = match (self.disabled, self.selected, self.color) {
- (true, _, _) => Color::Disabled,
- (false, true, _) => Color::Selected,
- _ => self.color,
- };
-
- let (mut bg_color, bg_active_color) = match self.variant {
- ButtonVariant::Filled => (
- cx.theme().colors().element_background,
- cx.theme().colors().element_active,
- ),
- ButtonVariant::Ghost => (
- cx.theme().colors().ghost_element_background,
- cx.theme().colors().ghost_element_active,
- ),
- };
-
- if self.selected {
- bg_color = cx.theme().colors().element_selected;
- }
-
- let mut button = h_stack()
- .id(self.id.clone())
- .justify_center()
- .rounded_md()
- .p_1()
- .bg(bg_color)
- .cursor_pointer()
- // Nate: Trying to figure out the right places we want to show a
- // hover state here. I think it is a bit heavy to have it on every
- // place we use an icon button.
- // .hover(|style| style.bg(bg_hover_color))
- .active(|style| style.bg(bg_active_color))
- .child(
- IconElement::new(self.icon)
- .size(self.size)
- .color(icon_color),
- );
-
- if let Some(click_handler) = self.on_click {
- button = button.on_click(move |event, cx| {
- cx.stop_propagation();
- click_handler(event, cx);
- })
- }
-
- if let Some(tooltip) = self.tooltip {
- if !self.selected {
- button = button.tooltip(move |cx| tooltip(cx))
- }
- }
-
- button
- }
-}
-
-impl IconButton {
- pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
- Self {
- id: id.into(),
- icon,
- color: Color::default(),
- size: Default::default(),
- variant: ButtonVariant::default(),
- selected: false,
- disabled: false,
- tooltip: None,
- on_click: None,
- }
- }
-
- pub fn icon(mut self, icon: Icon) -> Self {
- self.icon = icon;
- self
- }
-
- pub fn color(mut self, color: Color) -> Self {
- self.color = color;
- self
- }
-
- pub fn size(mut self, size: IconSize) -> Self {
- self.size = size;
- self
- }
-
- pub fn variant(mut self, variant: ButtonVariant) -> Self {
- self.variant = variant;
- self
- }
-
- pub fn selected(mut self, selected: bool) -> Self {
- self.selected = selected;
- self
- }
-
- pub fn disabled(mut self, disabled: bool) -> Self {
- self.disabled = disabled;
- self
- }
-
- pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
- self.tooltip = Some(Box::new(tooltip));
- self
- }
-
- pub fn on_click(mut self, handler: impl 'static + Fn(&ClickEvent, &mut WindowContext)) -> Self {
- self.on_click = Some(Box::new(handler));
- self
- }
-
- pub fn action(self, action: Box<dyn Action>) -> Self {
- self.on_click(move |_event, cx| cx.dispatch_action(action.boxed_clone()))
- }
-}
@@ -78,7 +78,7 @@ impl RenderOnce for ListHeader {
h_stack()
.gap_2()
.items_center()
- .children(icons.into_iter().map(|i| i.color(Color::Muted))),
+ .children(icons.into_iter().map(|i| i.icon_color(Color::Muted))),
),
Some(ListHeaderMeta::Button(label)) => div().child(label),
Some(ListHeaderMeta::Text(label)) => div().child(label),
@@ -2,7 +2,7 @@ use gpui::{Div, Render};
use story::Story;
use crate::prelude::*;
-use crate::{h_stack, Button, Icon, IconPosition};
+use crate::{Button, ButtonStyle2};
pub struct ButtonStory;
@@ -12,66 +12,11 @@ impl Render for ButtonStory {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
Story::container()
.child(Story::title_for::<Button>())
- .child(
- div()
- .flex()
- .gap_8()
- .child(
- 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(
- 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")
- .variant(ButtonVariant::Ghost)
- .on_click(|_, _cx| println!("Button clicked.")),
- )
+ .child(Story::label("Default"))
+ .child(Button::new("default_filled", "Click me"))
+ .child(Story::label("Default (Subtle)"))
+ .child(Button::new("default_subtle", "Click me").style(ButtonStyle2::Subtle))
+ .child(Story::label("Default (Transparent)"))
+ .child(Button::new("default_transparent", "Click me").style(ButtonStyle2::Transparent))
}
}
@@ -14,6 +14,12 @@ impl Render for IconButtonStory {
.child(Story::title_for::<IconButton>())
.child(Story::label("Default"))
.child(div().w_8().child(IconButton::new("icon_a", Icon::Hash)))
+ .child(Story::label("Selected"))
+ .child(
+ div()
+ .w_8()
+ .child(IconButton::new("icon_a", Icon::Hash).selected(true)),
+ )
.child(Story::label("With `on_click`"))
.child(
div()
@@ -8,6 +8,5 @@ pub use crate::clickable::*;
pub use crate::disableable::*;
pub use crate::fixed::*;
pub use crate::selectable::*;
-pub use crate::StyledExt;
-pub use crate::{ButtonVariant, Color};
+pub use crate::{ButtonCommon, Color, StyledExt};
pub use theme::ActiveTheme;
@@ -701,11 +701,6 @@ impl Render for PanelButtons {
(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| {
@@ -731,7 +726,14 @@ impl Render for PanelButtons {
})
.anchor(menu_anchor)
.attach(menu_attach)
- .child(|is_open| button.selected(is_open)),
+ .child(move |_is_open| {
+ IconButton::new(name, icon)
+ .selected(is_active_button)
+ .action(action.boxed_clone())
+ .tooltip(move |cx| {
+ Tooltip::for_action(tooltip.clone(), &*action, cx)
+ })
+ }),
)
});
@@ -181,6 +181,7 @@ pub mod simple_message_notification {
};
use serde::Deserialize;
use std::{borrow::Cow, sync::Arc};
+ use ui::prelude::*;
use ui::{h_stack, v_stack, Button, Icon, IconElement, Label, StyledExt};
#[derive(Clone, Default, Deserialize, PartialEq)]
@@ -287,12 +288,14 @@ pub mod simple_message_notification {
),
)
.children(self.click_message.iter().map(|message| {
- Button::new(message.clone()).on_click(cx.listener(|this, _, cx| {
- if let Some(on_click) = this.on_click.as_ref() {
- (on_click)(cx)
- };
- this.dismiss(cx)
- }))
+ Button::new(message.clone(), message.clone()).on_click(cx.listener(
+ |this, _, cx| {
+ if let Some(on_click) = this.on_click.as_ref() {
+ (on_click)(cx)
+ };
+ this.dismiss(cx)
+ },
+ ))
}))
}
}
@@ -71,14 +71,14 @@ impl Render for StatusBar {
div()
.border()
.border_color(gpui::red())
- .child(Button::new("15:22")),
+ .child(Button::new("status_line_column_numbers", "15:22")),
)
.child(
// TODO: Language picker
div()
.border()
.border_color(gpui::red())
- .child(Button::new("Rust")),
+ .child(Button::new("status_buffer_language", "Rust")),
),
)
.child(
@@ -94,9 +94,9 @@ impl Render for Toolbar {
.border()
.border_color(gpui::red())
.p_1()
- .child(Button::new("crates"))
+ .child(Button::new("breadcrumb_crates", "crates"))
.child(Label::new("/").color(Color::Muted))
- .child(Button::new("workspace2")),
+ .child(Button::new("breadcrumb_workspace2", "workspace2")),
)
// Toolbar right side
.child(