From b357ae4dc36c872e28be0ffd2d80aabe46d7ca0a Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 29 Nov 2023 17:41:44 -0500 Subject: [PATCH] Add new `Button` and `IconButton` components (#3448) This PR adds new `Button` and `IconButton` components built on top of our new button abstractions. Both of these buttons are built from the common `ButtonLike` base, and implement the `ButtonCommon` (name TBD) trait in order to provide a common interface. There are still some visual tweaks that we'll need to make to the new buttons, but those should be straightforward to make after we land this. Release Notes: - N/A --- crates/collab_ui2/src/collab_panel.rs | 39 +-- crates/collab_ui2/src/collab_titlebar_item.rs | 46 ++-- .../incoming_call_notification.rs | 31 ++- crates/diagnostics2/src/toolbar_controls.rs | 1 + crates/editor2/src/editor.rs | 5 +- crates/editor2/src/element.rs | 1 + crates/search2/src/buffer_search.rs | 14 +- crates/search2/src/search.rs | 19 +- crates/search2/src/search_bar.rs | 13 +- crates/ui2/src/components.rs | 4 - crates/ui2/src/components/button.rs | 228 ------------------ crates/ui2/src/components/button/button.rs | 91 +++++++ .../{button2.rs => button/button_like.rs} | 169 ++----------- .../ui2/src/components/button/icon_button.rs | 102 ++++++++ crates/ui2/src/components/button/mod.rs | 7 + crates/ui2/src/components/disclosure.rs | 4 +- crates/ui2/src/components/icon_button.rs | 135 ----------- crates/ui2/src/components/list/list_header.rs | 2 +- crates/ui2/src/components/stories/button.rs | 69 +----- crates/ui2/src/prelude.rs | 3 +- crates/workspace2/src/notifications.rs | 15 +- crates/workspace2/src/status_bar.rs | 4 +- crates/workspace2/src/toolbar.rs | 4 +- 23 files changed, 324 insertions(+), 682 deletions(-) delete mode 100644 crates/ui2/src/components/button.rs create mode 100644 crates/ui2/src/components/button/button.rs rename crates/ui2/src/components/{button2.rs => button/button_like.rs} (68%) create mode 100644 crates/ui2/src/components/button/icon_button.rs create mode 100644 crates/ui2/src/components/button/mod.rs delete mode 100644 crates/ui2/src/components/icon_button.rs diff --git a/crates/collab_ui2/src/collab_panel.rs b/crates/collab_ui2/src/collab_panel.rs index 3f99fea315..6b51d30026 100644 --- a/crates/collab_ui2/src/collab_panel.rs +++ b/crates/collab_ui2/src/collab_panel.rs @@ -178,6 +178,7 @@ use gpui::{ use project::Fs; 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, @@ -2338,18 +2339,20 @@ impl CollabPanel { } fn render_signed_out(&mut self, cx: &mut ViewContext) -> 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) -> List { @@ -2564,7 +2567,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(move |this, _, cx| { this.remove_contact(user_id, &github_login, cx); @@ -2688,13 +2691,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 { @@ -2703,7 +2706,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))] }; @@ -2846,7 +2849,7 @@ impl CollabPanel { "channel_chat", Icon::MessageBubbles, ) - .color(if has_messages_notification { + .icon_color(if has_messages_notification { Color::Default } else { Color::Muted @@ -2861,7 +2864,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 diff --git a/crates/collab_ui2/src/collab_titlebar_item.rs b/crates/collab_ui2/src/collab_titlebar_item.rs index ac72176d67..d76242afa3 100644 --- a/crates/collab_ui2/src/collab_titlebar_item.rs +++ b/crates/collab_ui2/src/collab_titlebar_item.rs @@ -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)), - ), - ) } }) } diff --git a/crates/collab_ui2/src/notifications/incoming_call_notification.rs b/crates/collab_ui2/src/notifications/incoming_call_notification.rs index 0519b6fc4a..ce6c8f0f57 100644 --- a/crates/collab_ui2/src/notifications/incoming_call_notification.rs +++ b/crates/collab_ui2/src/notifications/incoming_call_notification.rs @@ -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) -> 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 {} diff --git a/crates/diagnostics2/src/toolbar_controls.rs b/crates/diagnostics2/src/toolbar_controls.rs index e513076ec8..1a604b76c8 100644 --- a/crates/diagnostics2/src/toolbar_controls.rs +++ b/crates/diagnostics2/src/toolbar_controls.rs @@ -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}; diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index c8ce37d7e2..c53403c9c3 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -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() diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 74ca292ecf..24402c7e37 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -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; diff --git a/crates/search2/src/buffer_search.rs b/crates/search2/src/buffer_search.rs index d80d9f5d50..b3d6006113 100644 --- a/crates/search2/src/buffer_search.rs +++ b/crates/search2/src/buffer_search.rs @@ -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) { diff --git a/crates/search2/src/search.rs b/crates/search2/src/search.rs index 118d9054e6..65a4ddfd42 100644 --- a/crates/search2/src/search.rs +++ b/crates/search2/src/search.rs @@ -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()); }) } diff --git a/crates/search2/src/search_bar.rs b/crates/search2/src/search_bar.rs index f5a9a8c8f7..44ba287d78 100644 --- a/crates/search2/src/search_bar.rs +++ b/crates/search2/src/search_bar.rs @@ -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) } diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index c5d06bf0dd..be95fc1fab 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -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::*; diff --git a/crates/ui2/src/components/button.rs b/crates/ui2/src/components/button.rs deleted file mode 100644 index fbe5b951fa..0000000000 --- a/crates/ui2/src/components/button.rs +++ /dev/null @@ -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