From 4e8937b62d8b14e22d82a0ea4a06f4fc280f1491 Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Fri, 13 Mar 2026 14:13:12 -0300 Subject: [PATCH] ui: Refactor the `Button` component icon methods (#51496) Previously, if you wanted to have a button that contains icons on both edges, you'd need to use a `ButtonLike` component, which takes any children. Meanwhile, the `Button` would only take one icon, where you could control its position through the `IconPosition` enum. This has always felt unnecessarily limiting. So, this PR removes this limitation by adding two new methods to the button: `start_icon` and `end_icon`. In the meantime, I have also been bothered by the unnecessary indirection in the `IconButton` due to the existence of the `ButtonIcon` component. So I figured I could also completely eliminate that by adding some of its methods directly to the `IconButton` and in the Button, just using a regular `Icon` component. --- ## Before ```rust Button::new("id", "Label") .icon(IconName::Plus) .icon_position(IconPosition::Start) .icon_size(IconSize::Small) .icon_color(Color::Muted) ``` ## After ```rust Button::new("id", "Label") .start_icon(Icon::new(IconName::Check)) .end_icon(Icon::new(IconName::ChevronDown).size(IconSize::XSmall)) ``` This should have no visual impact to the UI. Release Notes: - N/A --- crates/agent_ui/src/agent_configuration.rs | 45 ++-- .../add_llm_provider_modal.rs | 18 +- .../configure_context_server_modal.rs | 8 +- crates/agent_ui/src/agent_diff.rs | 9 +- crates/agent_ui/src/agent_model_selector.rs | 17 +- crates/agent_ui/src/agent_panel.rs | 51 ++-- crates/agent_ui/src/agent_registry_ui.rs | 17 +- crates/agent_ui/src/config_options.rs | 5 +- .../src/connection_view/thread_view.rs | 89 +++---- crates/agent_ui/src/inline_prompt_editor.rs | 8 +- crates/agent_ui/src/message_editor.rs | 8 +- crates/agent_ui/src/mode_selector.rs | 5 +- crates/agent_ui/src/model_selector_popover.rs | 17 +- crates/agent_ui/src/profile_selector.rs | 8 +- crates/agent_ui/src/sidebar.rs | 18 +- crates/agent_ui/src/text_thread_editor.rs | 29 +-- .../agent_ui/src/ui/acp_onboarding_modal.rs | 9 +- .../src/ui/claude_agent_onboarding_modal.rs | 9 +- crates/collab_ui/src/collab_panel.rs | 6 +- crates/collab_ui/src/notification_panel.rs | 4 +- crates/copilot_ui/src/sign_in.rs | 27 ++- crates/debugger_ui/src/debugger_panel.rs | 36 +-- .../src/rate_prediction_modal.rs | 8 +- crates/extensions_ui/src/extensions_ui.rs | 31 +-- crates/git_graph/src/git_graph.rs | 21 +- crates/git_ui/src/blame_ui.rs | 18 +- crates/git_ui/src/commit_modal.rs | 9 +- crates/git_ui/src/commit_tooltip.rs | 13 +- crates/git_ui/src/commit_view.rs | 9 +- crates/git_ui/src/conflict_view.rs | 9 +- crates/git_ui/src/file_history_view.rs | 9 +- crates/git_ui/src/git_ui.rs | 3 +- crates/git_ui/src/project_diff.rs | 16 +- crates/keymap_editor/src/keymap_editor.rs | 8 +- .../language_models/src/provider/bedrock.rs | 3 +- crates/language_models/src/provider/cloud.rs | 6 +- .../language_models/src/provider/lmstudio.rs | 34 +-- crates/language_models/src/provider/ollama.rs | 38 +-- .../language_models/src/provider/open_ai.rs | 8 +- .../src/provider/open_ai_compatible.rs | 4 +- crates/language_onboarding/src/python.rs | 4 +- crates/language_tools/src/lsp_log_view.rs | 35 +-- crates/onboarding/src/basics_page.rs | 8 +- crates/onboarding/src/multibuffer_hint.rs | 9 +- crates/panel/src/panel.rs | 1 - .../src/disconnected_overlay.rs | 9 +- crates/recent_projects/src/remote_servers.rs | 6 +- crates/repl/src/components/kernel_options.rs | 9 +- crates/repl/src/notebook/notebook_ui.rs | 9 +- crates/rules_library/src/rules_library.rs | 9 +- crates/search/src/project_search.rs | 20 +- .../src/pages/tool_permissions_setup.rs | 17 +- crates/settings_ui/src/settings_ui.rs | 31 +-- .../theme_selector/src/icon_theme_selector.rs | 9 +- crates/theme_selector/src/theme_selector.rs | 9 +- crates/title_bar/src/title_bar.rs | 25 +- .../src/components/ai/configured_api_card.rs | 9 +- crates/ui/src/components/banner.rs | 10 +- crates/ui/src/components/button.rs | 1 - crates/ui/src/components/button/button.rs | 226 +++++++----------- .../ui/src/components/button/button_icon.rs | 199 --------------- .../ui/src/components/button/icon_button.rs | 49 ++-- crates/ui/src/components/dropdown_menu.rs | 9 +- crates/workspace/src/notifications.rs | 28 ++- 64 files changed, 584 insertions(+), 847 deletions(-) delete mode 100644 crates/ui/src/components/button/button_icon.rs diff --git a/crates/agent_ui/src/agent_configuration.rs b/crates/agent_ui/src/agent_configuration.rs index ef3f3fdacc3d155554f3e2576ed1ed27c1d9ff0d..6b7f46d87f2db1e9262eadf9e7064c06245b1e3c 100644 --- a/crates/agent_ui/src/agent_configuration.rs +++ b/crates/agent_ui/src/agent_configuration.rs @@ -332,10 +332,11 @@ impl AgentConfiguration { .full_width() .style(ButtonStyle::Outlined) .layer(ElevationIndex::ModalSurface) - .icon_position(IconPosition::Start) - .icon(IconName::Thread) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .start_icon( + Icon::new(IconName::Thread) + .size(IconSize::Small) + .color(Color::Muted), + ) .label_size(LabelSize::Small) .on_click(cx.listener({ let provider = provider.clone(); @@ -357,10 +358,11 @@ impl AgentConfiguration { ) .full_width() .style(ButtonStyle::Outlined) - .icon_position(IconPosition::Start) - .icon(IconName::Trash) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .start_icon( + Icon::new(IconName::Trash) + .size(IconSize::Small) + .color(Color::Muted), + ) .label_size(LabelSize::Small) .on_click(cx.listener({ let provider = provider.clone(); @@ -426,10 +428,11 @@ impl AgentConfiguration { .trigger( Button::new("add-provider", "Add Provider") .style(ButtonStyle::Outlined) - .icon_position(IconPosition::Start) - .icon(IconName::Plus) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .start_icon( + Icon::new(IconName::Plus) + .size(IconSize::Small) + .color(Color::Muted), + ) .label_size(LabelSize::Small), ) .menu({ @@ -525,10 +528,11 @@ impl AgentConfiguration { .trigger( Button::new("add-server", "Add Server") .style(ButtonStyle::Outlined) - .icon_position(IconPosition::Start) - .icon(IconName::Plus) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .start_icon( + Icon::new(IconName::Plus) + .size(IconSize::Small) + .color(Color::Muted), + ) .label_size(LabelSize::Small), ) .menu({ @@ -970,10 +974,11 @@ impl AgentConfiguration { .trigger( Button::new("add-agent", "Add Agent") .style(ButtonStyle::Outlined) - .icon_position(IconPosition::Start) - .icon(IconName::Plus) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .start_icon( + Icon::new(IconName::Plus) + .size(IconSize::Small) + .color(Color::Muted), + ) .label_size(LabelSize::Small), ) .menu({ diff --git a/crates/agent_ui/src/agent_configuration/add_llm_provider_modal.rs b/crates/agent_ui/src/agent_configuration/add_llm_provider_modal.rs index a3a389ac0a068d92112ee98caacb2986c499ad86..3d18d734af4890ef06a67dccec0c0e884a219a79 100644 --- a/crates/agent_ui/src/agent_configuration/add_llm_provider_modal.rs +++ b/crates/agent_ui/src/agent_configuration/add_llm_provider_modal.rs @@ -340,10 +340,11 @@ impl AddLlmProviderModal { .child(Label::new("Models").size(LabelSize::Small)) .child( Button::new("add-model", "Add Model") - .icon(IconName::Plus) - .icon_position(IconPosition::Start) - .icon_size(IconSize::XSmall) - .icon_color(Color::Muted) + .start_icon( + Icon::new(IconName::Plus) + .size(IconSize::XSmall) + .color(Color::Muted), + ) .label_size(LabelSize::Small) .on_click(cx.listener(|this, _, window, cx| { this.input.add_model(window, cx); @@ -446,10 +447,11 @@ impl AddLlmProviderModal { .when(has_more_than_one_model, |this| { this.child( Button::new(("remove-model", ix), "Remove Model") - .icon(IconName::Trash) - .icon_position(IconPosition::Start) - .icon_size(IconSize::XSmall) - .icon_color(Color::Muted) + .start_icon( + Icon::new(IconName::Trash) + .size(IconSize::XSmall) + .color(Color::Muted), + ) .label_size(LabelSize::Small) .style(ButtonStyle::Outlined) .full_width() diff --git a/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs b/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs index 38805f2c26693f168c7273afddf5aceea44f83e3..857a084b720e732b218f0060f1fbee312f712540 100644 --- a/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs +++ b/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs @@ -693,9 +693,11 @@ impl ConfigureContextServerModal { { Some( Button::new("open-repository", "Open Repository") - .icon(IconName::ArrowUpRight) - .icon_color(Color::Muted) - .icon_size(IconSize::Small) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .tooltip({ let repository_url = repository_url.clone(); move |_window, cx| { diff --git a/crates/agent_ui/src/agent_diff.rs b/crates/agent_ui/src/agent_diff.rs index 13e62eb502de1d4bf454b47b216374a0abf2bc79..bb1367b7da31d7975ab271ec821fb43a5da70605 100644 --- a/crates/agent_ui/src/agent_diff.rs +++ b/crates/agent_ui/src/agent_diff.rs @@ -686,10 +686,11 @@ impl Render for AgentDiffPane { .child( Button::new("continue-iterating", "Continue Iterating") .style(ButtonStyle::Filled) - .icon(IconName::ForwardArrow) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .start_icon( + Icon::new(IconName::ForwardArrow) + .size(IconSize::Small) + .color(Color::Muted), + ) .full_width() .key_binding(KeyBinding::for_action_in( &ToggleFocus, diff --git a/crates/agent_ui/src/agent_model_selector.rs b/crates/agent_ui/src/agent_model_selector.rs index 465eb808404dd5521ef056b62c813a9566bb7a47..93984121c261034a5cc6198621e79d87d2de1ff4 100644 --- a/crates/agent_ui/src/agent_model_selector.rs +++ b/crates/agent_ui/src/agent_model_selector.rs @@ -9,7 +9,7 @@ use language_model::IconOrSvg; use picker::popover_menu::PickerPopoverMenu; use settings::update_settings_file; use std::sync::Arc; -use ui::{ButtonLike, PopoverMenuHandle, TintColor, Tooltip, prelude::*}; +use ui::{PopoverMenuHandle, Tooltip, prelude::*}; pub struct AgentModelSelector { selector: Entity, @@ -112,9 +112,11 @@ impl Render for AgentModelSelector { PickerPopoverMenu::new( self.selector.clone(), - ButtonLike::new("active-model") + Button::new("active-model", model_name) + .label_size(LabelSize::Small) + .color(color) .when_some(provider_icon, |this, icon| { - this.child( + this.start_icon( match icon { IconOrSvg::Svg(path) => Icon::from_external_svg(path), IconOrSvg::Icon(name) => Icon::new(name), @@ -123,14 +125,7 @@ impl Render for AgentModelSelector { .size(IconSize::XSmall), ) }) - .selected_style(ButtonStyle::Tinted(TintColor::Accent)) - .child( - Label::new(model_name) - .color(color) - .size(LabelSize::Small) - .ml_0p5(), - ) - .child( + .end_icon( Icon::new(IconName::ChevronDown) .color(color) .size(IconSize::XSmall), diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index e69b6a9f164a07d17c01057ea8a57c287ab6f938..d5c2942cf3528b94ad7d93271ef75e976bcbea56 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -80,9 +80,8 @@ use search::{BufferSearchBar, buffer_search}; use settings::{Settings, update_settings_file}; use theme::ThemeSettings; use ui::{ - Button, ButtonLike, Callout, ContextMenu, ContextMenuEntry, DocumentationSide, Indicator, - KeyBinding, PopoverMenu, PopoverMenuHandle, SpinnerLabel, Tab, TintColor, Tooltip, prelude::*, - utils::WithRemSize, + Button, Callout, ContextMenu, ContextMenuEntry, DocumentationSide, Indicator, KeyBinding, + PopoverMenu, PopoverMenuHandle, SpinnerLabel, Tab, Tooltip, prelude::*, utils::WithRemSize, }; use util::{ResultExt as _, debug_panic}; use workspace::{ @@ -3632,11 +3631,7 @@ impl AgentPanel { }; let trigger_button = Button::new("thread-target-trigger", trigger_label) - .icon(icon) - .icon_size(IconSize::XSmall) - .icon_position(IconPosition::End) - .icon_color(Color::Muted) - .selected_style(ButtonStyle::Tinted(TintColor::Accent)) + .end_icon(Icon::new(icon).size(IconSize::XSmall).color(Color::Muted)) .disabled(is_creating); let dock_position = AgentSettings::get_global(cx).dock; @@ -4290,32 +4285,22 @@ impl AgentPanel { (IconName::ChevronDown, Color::Muted, Color::Default) }; - let agent_icon_element: AnyElement = - if let Some(icon_path) = selected_agent_custom_icon_for_button { - Icon::from_external_svg(icon_path) - .size(IconSize::Small) - .color(icon_color) - .into_any_element() - } else { - let icon_name = selected_agent_builtin_icon.unwrap_or(IconName::ZedAgent); - Icon::new(icon_name) - .size(IconSize::Small) - .color(icon_color) - .into_any_element() - }; + let agent_icon = if let Some(icon_path) = selected_agent_custom_icon_for_button { + Icon::from_external_svg(icon_path) + .size(IconSize::Small) + .color(icon_color) + } else { + let icon_name = selected_agent_builtin_icon.unwrap_or(IconName::ZedAgent); + Icon::new(icon_name).size(IconSize::Small).color(icon_color) + }; - let agent_selector_button = ButtonLike::new("agent-selector-trigger") - .selected_style(ButtonStyle::Tinted(TintColor::Accent)) - .child( - h_flex() - .gap_1() - .child(agent_icon_element) - .child(Label::new(selected_agent_label).color(label_color).ml_0p5()) - .child( - Icon::new(chevron_icon) - .color(icon_color) - .size(IconSize::XSmall), - ), + let agent_selector_button = Button::new("agent-selector-trigger", selected_agent_label) + .start_icon(agent_icon) + .color(label_color) + .end_icon( + Icon::new(chevron_icon) + .color(icon_color) + .size(IconSize::XSmall), ); let agent_selector_menu = PopoverMenu::new("new_thread_menu") diff --git a/crates/agent_ui/src/agent_registry_ui.rs b/crates/agent_ui/src/agent_registry_ui.rs index d003ba958276c8c2370011d83028eda2e9121440..cb99077697a59b4f0c1a50277172ef1eaf0b77aa 100644 --- a/crates/agent_ui/src/agent_registry_ui.rs +++ b/crates/agent_ui/src/agent_registry_ui.rs @@ -467,10 +467,11 @@ impl AgentRegistryPage { let agent_id = agent.id().to_string(); Button::new(button_id, "Install") .style(ButtonStyle::Tinted(ui::TintColor::Accent)) - .icon(IconName::Download) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::Download) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(move |_, _, cx| { let agent_id = agent_id.clone(); update_settings_file(fs.clone(), cx, move |settings, _| { @@ -541,9 +542,11 @@ impl Render for AgentRegistryPage { Button::new("learn-more", "Learn More") .style(ButtonStyle::Outlined) .size(ButtonSize::Medium) - .icon(IconName::ArrowUpRight) - .icon_color(Color::Muted) - .icon_size(IconSize::Small) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(move |_, _, cx| { cx.open_url(&zed_urls::acp_registry_blog(cx)) }), diff --git a/crates/agent_ui/src/config_options.rs b/crates/agent_ui/src/config_options.rs index 6ec2595202490ca7474717f8985b6e4f6d7ca0b9..b8cf7e5d57921c7710392911829fc2b5045a0f90 100644 --- a/crates/agent_ui/src/config_options.rs +++ b/crates/agent_ui/src/config_options.rs @@ -350,10 +350,7 @@ impl ConfigOptionSelector { ) .label_size(LabelSize::Small) .color(Color::Muted) - .icon(icon) - .icon_size(IconSize::XSmall) - .icon_position(IconPosition::End) - .icon_color(Color::Muted) + .end_icon(Icon::new(icon).size(IconSize::XSmall).color(Color::Muted)) .disabled(self.setting_value) } } diff --git a/crates/agent_ui/src/connection_view/thread_view.rs b/crates/agent_ui/src/connection_view/thread_view.rs index 79af34d6da515c5f01764ffda9c72277c783729c..35df60b567de86762a9af330013df0fab35f3f01 100644 --- a/crates/agent_ui/src/connection_view/thread_view.rs +++ b/crates/agent_ui/src/connection_view/thread_view.rs @@ -3826,11 +3826,8 @@ impl ThreadView { .child(Divider::horizontal()) .child( Button::new("restore-checkpoint", "Restore Checkpoint") - .icon(IconName::Undo) - .icon_size(IconSize::XSmall) - .icon_position(IconPosition::Start) + .start_icon(Icon::new(IconName::Undo).size(IconSize::XSmall).color(Color::Muted)) .label_size(LabelSize::XSmall) - .icon_color(Color::Muted) .color(Color::Muted) .tooltip(Tooltip::text("Restores all files in the project to the content they had at this point in the conversation.")) .on_click(cx.listener(move |this, _, _window, cx| { @@ -5783,10 +5780,11 @@ impl ThreadView { .gap_0p5() .child( Button::new(("allow-btn", entry_ix), "Allow") - .icon(IconName::Check) - .icon_color(Color::Success) - .icon_position(IconPosition::Start) - .icon_size(IconSize::XSmall) + .start_icon( + Icon::new(IconName::Check) + .size(IconSize::XSmall) + .color(Color::Success), + ) .label_size(LabelSize::Small) .when(is_first, |this| { this.key_binding( @@ -5817,10 +5815,11 @@ impl ThreadView { ) .child( Button::new(("deny-btn", entry_ix), "Deny") - .icon(IconName::Close) - .icon_color(Color::Error) - .icon_position(IconPosition::Start) - .icon_size(IconSize::XSmall) + .start_icon( + Icon::new(IconName::Close) + .size(IconSize::XSmall) + .color(Color::Error), + ) .label_size(LabelSize::Small) .when(is_first, |this| { this.key_binding( @@ -5887,9 +5886,11 @@ impl ThreadView { .with_handle(permission_dropdown_handle) .trigger( Button::new(("granularity-trigger", entry_ix), current_label) - .icon(IconName::ChevronDown) - .icon_size(IconSize::XSmall) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ChevronDown) + .size(IconSize::XSmall) + .color(Color::Muted), + ) .label_size(LabelSize::Small) .when(is_first, |this| { this.key_binding( @@ -5962,24 +5963,35 @@ impl ThreadView { let option_id = SharedString::from(option.option_id.0.clone()); Button::new((option_id, entry_ix), option.name.clone()) .map(|this| { - let (this, action) = match option.kind { + let (icon, action) = match option.kind { acp::PermissionOptionKind::AllowOnce => ( - this.icon(IconName::Check).icon_color(Color::Success), + Icon::new(IconName::Check) + .size(IconSize::XSmall) + .color(Color::Success), Some(&AllowOnce as &dyn Action), ), acp::PermissionOptionKind::AllowAlways => ( - this.icon(IconName::CheckDouble).icon_color(Color::Success), + Icon::new(IconName::CheckDouble) + .size(IconSize::XSmall) + .color(Color::Success), Some(&AllowAlways as &dyn Action), ), acp::PermissionOptionKind::RejectOnce => ( - this.icon(IconName::Close).icon_color(Color::Error), + Icon::new(IconName::Close) + .size(IconSize::XSmall) + .color(Color::Error), Some(&RejectOnce as &dyn Action), ), - acp::PermissionOptionKind::RejectAlways | _ => { - (this.icon(IconName::Close).icon_color(Color::Error), None) - } + acp::PermissionOptionKind::RejectAlways | _ => ( + Icon::new(IconName::Close) + .size(IconSize::XSmall) + .color(Color::Error), + None, + ), }; + let this = this.start_icon(icon); + let Some(action) = action else { return this; }; @@ -5995,8 +6007,6 @@ impl ThreadView { .map(|kb| kb.size(rems_from_px(10.))), ) }) - .icon_position(IconPosition::Start) - .icon_size(IconSize::XSmall) .label_size(LabelSize::Small) .on_click(cx.listener({ let session_id = session_id.clone(); @@ -6373,9 +6383,11 @@ impl ThreadView { .color(Color::Muted) .truncate(true) .when(is_file.is_none(), |this| { - this.icon(IconName::ArrowUpRight) - .icon_size(IconSize::XSmall) - .icon_color(Color::Muted) + this.end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::XSmall) + .color(Color::Muted), + ) }) .on_click(cx.listener({ let workspace = self.workspace.clone(); @@ -7470,19 +7482,16 @@ impl ThreadView { .title("Codex on Windows") .description("For best performance, run Codex in Windows Subsystem for Linux (WSL2)") .actions_slot( - Button::new("open-wsl-modal", "Open in WSL") - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .on_click(cx.listener({ - move |_, _, _window, cx| { - #[cfg(windows)] - _window.dispatch_action( - zed_actions::wsl_actions::OpenWsl::default().boxed_clone(), - cx, - ); - cx.notify(); - } - })), + Button::new("open-wsl-modal", "Open in WSL").on_click(cx.listener({ + move |_, _, _window, cx| { + #[cfg(windows)] + _window.dispatch_action( + zed_actions::wsl_actions::OpenWsl::default().boxed_clone(), + cx, + ); + cx.notify(); + } + })), ) .dismiss_action( IconButton::new("dismiss", IconName::Close) diff --git a/crates/agent_ui/src/inline_prompt_editor.rs b/crates/agent_ui/src/inline_prompt_editor.rs index 0450efc4b7ebf466d0b9b13f516249a2cba0ecfa..fa68c86fe9fa39319b7d6adb1c7ae50544ae4f00 100644 --- a/crates/agent_ui/src/inline_prompt_editor.rs +++ b/crates/agent_ui/src/inline_prompt_editor.rs @@ -796,9 +796,11 @@ impl PromptEditor { vec![ Button::new("start", mode.start_label()) .label_size(LabelSize::Small) - .icon(IconName::Return) - .icon_size(IconSize::XSmall) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::Return) + .size(IconSize::XSmall) + .color(Color::Muted), + ) .on_click( cx.listener(|_, _, _, cx| cx.emit(PromptEditorEvent::StartRequested)), ) diff --git a/crates/agent_ui/src/message_editor.rs b/crates/agent_ui/src/message_editor.rs index 89b4caee69f5d26306077388edffa77f50ea7596..4170417df0c5fdfcdb86f2e4c0478c0ef59cefa9 100644 --- a/crates/agent_ui/src/message_editor.rs +++ b/crates/agent_ui/src/message_editor.rs @@ -33,7 +33,7 @@ use rope::Point; use settings::Settings; use std::{cell::RefCell, fmt::Write, ops::Range, rc::Rc, sync::Arc}; use theme::ThemeSettings; -use ui::{ButtonLike, ButtonStyle, ContextMenu, Disclosure, ElevationIndex, prelude::*}; +use ui::{ContextMenu, Disclosure, ElevationIndex, prelude::*}; use util::paths::PathStyle; use util::{ResultExt, debug_panic}; use workspace::{CollaboratorId, Workspace}; @@ -1161,11 +1161,9 @@ impl MessageEditor { render: Arc::new({ let title = title.clone(); move |_fold_id, _fold_range, _cx| { - ButtonLike::new("crease") - .style(ButtonStyle::Filled) + Button::new("crease", title.clone()) .layer(ElevationIndex::ElevatedSurface) - .child(Icon::new(icon)) - .child(Label::new(title.clone()).single_line()) + .start_icon(Icon::new(icon)) .into_any_element() } }), diff --git a/crates/agent_ui/src/mode_selector.rs b/crates/agent_ui/src/mode_selector.rs index 9ec25d6d2a1e11a12ef8f05061f143fec5fe53bb..60c9b8787092388ad2b3e2d5817834018dc7ea25 100644 --- a/crates/agent_ui/src/mode_selector.rs +++ b/crates/agent_ui/src/mode_selector.rs @@ -169,10 +169,7 @@ impl Render for ModeSelector { let trigger_button = Button::new("mode-selector-trigger", current_mode_name) .label_size(LabelSize::Small) .color(Color::Muted) - .icon(icon) - .icon_size(IconSize::XSmall) - .icon_position(IconPosition::End) - .icon_color(Color::Muted) + .end_icon(Icon::new(icon).size(IconSize::XSmall).color(Color::Muted)) .disabled(self.setting_mode); PopoverMenu::new("mode-selector") diff --git a/crates/agent_ui/src/model_selector_popover.rs b/crates/agent_ui/src/model_selector_popover.rs index 7a4e9dbf8633680fe9c6ee3bda4acdb0ff5b1478..74ebd78ba61681325cc4905be8d577b225e50e92 100644 --- a/crates/agent_ui/src/model_selector_popover.rs +++ b/crates/agent_ui/src/model_selector_popover.rs @@ -5,7 +5,7 @@ use acp_thread::{AgentModelIcon, AgentModelInfo, AgentModelSelector}; use fs::Fs; use gpui::{AnyView, Entity, FocusHandle}; use picker::popover_menu::PickerPopoverMenu; -use ui::{ButtonLike, PopoverMenuHandle, TintColor, Tooltip, prelude::*}; +use ui::{PopoverMenuHandle, Tooltip, prelude::*}; use crate::ui::ModelSelectorTooltip; use crate::{ModelSelector, model_selector::acp_model_selector}; @@ -96,11 +96,12 @@ impl Render for ModelSelectorPopover { PickerPopoverMenu::new( self.selector.clone(), - ButtonLike::new("active-model") + Button::new("active-model", model_name) + .label_size(LabelSize::Small) + .color(color) .disabled(self.disabled) - .selected_style(ButtonStyle::Tinted(TintColor::Accent)) .when_some(model_icon, |this, icon| { - this.child( + this.start_icon( match icon { AgentModelIcon::Path(path) => Icon::from_external_svg(path), AgentModelIcon::Named(icon_name) => Icon::new(icon_name), @@ -109,13 +110,7 @@ impl Render for ModelSelectorPopover { .size(IconSize::XSmall), ) }) - .child( - Label::new(model_name) - .color(color) - .size(LabelSize::Small) - .ml_0p5(), - ) - .child( + .end_icon( Icon::new(icon) .map(|this| { if self.disabled { diff --git a/crates/agent_ui/src/profile_selector.rs b/crates/agent_ui/src/profile_selector.rs index f785c936a643f4280121d083831eba4c909bc0f5..661f887b53116094b5a8694bf93b21389bd9f58b 100644 --- a/crates/agent_ui/src/profile_selector.rs +++ b/crates/agent_ui/src/profile_selector.rs @@ -16,7 +16,7 @@ use std::{ }; use ui::{ DocumentationAside, DocumentationSide, HighlightedLabel, KeyBinding, LabelSize, ListItem, - ListItemSpacing, PopoverMenuHandle, TintColor, Tooltip, prelude::*, + ListItemSpacing, PopoverMenuHandle, Tooltip, prelude::*, }; /// Trait for types that can provide and manage agent profiles @@ -192,11 +192,7 @@ impl Render for ProfileSelector { .disabled(self.disabled) .label_size(LabelSize::Small) .color(Color::Muted) - .icon(icon) - .icon_size(IconSize::XSmall) - .icon_position(IconPosition::End) - .icon_color(Color::Muted) - .selected_style(ButtonStyle::Tinted(TintColor::Accent)); + .end_icon(Icon::new(icon).size(IconSize::XSmall).color(Color::Muted)); let disabled = self.disabled; diff --git a/crates/agent_ui/src/sidebar.rs b/crates/agent_ui/src/sidebar.rs index 0bc0968ea44c25ec9cfd3d68d8600814f922fc12..aed642ccc9987569fb3681ab93bb2c8fe6de2674 100644 --- a/crates/agent_ui/src/sidebar.rs +++ b/crates/agent_ui/src/sidebar.rs @@ -1775,10 +1775,11 @@ impl Sidebar { ) .full_width() .style(ButtonStyle::Outlined) - .icon(IconName::Plus) - .icon_color(Color::Muted) - .icon_size(IconSize::Small) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::Plus) + .size(IconSize::Small) + .color(Color::Muted), + ) .toggle_state(is_selected) .on_click(cx.listener(move |this, _, window, cx| { this.selection = None; @@ -1833,10 +1834,11 @@ impl Sidebar { .full_width() .label_size(LabelSize::Small) .style(ButtonStyle::Outlined) - .icon(IconName::Archive) - .icon_color(Color::Muted) - .icon_size(IconSize::XSmall) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::Archive) + .size(IconSize::XSmall) + .color(Color::Muted), + ) .on_click(cx.listener(|this, _, window, cx| { this.show_archive(window, cx); })), diff --git a/crates/agent_ui/src/text_thread_editor.rs b/crates/agent_ui/src/text_thread_editor.rs index 13764bd655c23176b3aa016f36eae193e16f92de..118de80af215d5ede10b125af1fe154461c3f80d 100644 --- a/crates/agent_ui/src/text_thread_editor.rs +++ b/crates/agent_ui/src/text_thread_editor.rs @@ -1191,11 +1191,11 @@ impl TextThreadEditor { Button::new("show-error", "Error") .color(Color::Error) .selected_label_color(Color::Error) - .selected_icon_color(Color::Error) - .icon(IconName::XCircle) - .icon_color(Color::Error) - .icon_size(IconSize::XSmall) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::XCircle) + .size(IconSize::XSmall) + .color(Color::Error), + ) .tooltip(Tooltip::text("View Details")) .on_click({ let text_thread = text_thread.clone(); @@ -2287,20 +2287,11 @@ impl TextThreadEditor { PickerPopoverMenu::new( self.language_model_selector.clone(), - ButtonLike::new("active-model") - .selected_style(ButtonStyle::Tinted(TintColor::Accent)) - .child( - h_flex() - .gap_0p5() - .child(provider_icon_element) - .child( - Label::new(model_name) - .color(color) - .size(LabelSize::Small) - .ml_0p5(), - ) - .child(Icon::new(icon).color(color).size(IconSize::XSmall)), - ), + Button::new("active-model", model_name) + .color(color) + .label_size(LabelSize::Small) + .start_icon(provider_icon_element) + .end_icon(Icon::new(icon).color(color).size(IconSize::XSmall)), tooltip, gpui::Corner::BottomRight, cx, diff --git a/crates/agent_ui/src/ui/acp_onboarding_modal.rs b/crates/agent_ui/src/ui/acp_onboarding_modal.rs index 23f3eadc4b259aa854f6c2cbb6bb3a68ec46deb5..ee214e07ffb526f1c4ef89cc9301b4ea7e8d6ebf 100644 --- a/crates/agent_ui/src/ui/acp_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/acp_onboarding_modal.rs @@ -193,15 +193,16 @@ impl Render for AcpOnboardingModal { let copy = "Bring the agent of your choice to Zed via our new Agent Client Protocol (ACP), starting with Google's Gemini CLI integration."; let open_panel_button = Button::new("open-panel", "Start with Gemini CLI") - .icon_size(IconSize::Indicator) .style(ButtonStyle::Tinted(TintColor::Accent)) .full_width() .on_click(cx.listener(Self::open_panel)); let docs_button = Button::new("add-other-agents", "Add Other Agents") - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Indicator) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Indicator) + .color(Color::Muted), + ) .full_width() .on_click(cx.listener(Self::open_agent_registry)); diff --git a/crates/agent_ui/src/ui/claude_agent_onboarding_modal.rs b/crates/agent_ui/src/ui/claude_agent_onboarding_modal.rs index 9e499690efcb797e28f32ca8b3bd0f2c2f0da9db..3a9010b0a155873e658946b4155f09f8867e498a 100644 --- a/crates/agent_ui/src/ui/claude_agent_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/claude_agent_onboarding_modal.rs @@ -201,15 +201,16 @@ impl Render for ClaudeCodeOnboardingModal { let copy = "Powered by the Agent Client Protocol, you can now run Claude Agent as\na first-class citizen in Zed's agent panel."; let open_panel_button = Button::new("open-panel", "Start with Claude Agent") - .icon_size(IconSize::Indicator) .style(ButtonStyle::Tinted(TintColor::Accent)) .full_width() .on_click(cx.listener(Self::open_panel)); let docs_button = Button::new("add-other-agents", "Add Other Agents") - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Indicator) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Indicator) + .color(Color::Muted), + ) .full_width() .on_click(cx.listener(Self::view_docs)); diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index d0cac2e69f8d8c5b3fde588cc4ceee92d64962d7..9aeeeeb4233a7e5486ef49da8b0aeaaddd846d17 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -2347,9 +2347,7 @@ impl CollabPanel { .gap_2() .child( Button::new("sign_in", button_label) - .icon_color(Color::Muted) - .icon(IconName::Github) - .icon_position(IconPosition::Start) + .start_icon(Icon::new(IconName::Github).color(Color::Muted)) .style(ButtonStyle::Filled) .full_width() .disabled(is_signing_in) @@ -2597,9 +2595,9 @@ impl CollabPanel { Section::Channels => { Some( h_flex() - .gap_1() .child( IconButton::new("filter-active-channels", IconName::ListFilter) + .icon_size(IconSize::Small) .toggle_state(self.filter_active_channels) .when(!self.filter_active_channels, |button| { button.visible_on_hover("section-header") diff --git a/crates/collab_ui/src/notification_panel.rs b/crates/collab_ui/src/notification_panel.rs index f9ce68a6afe8497c50096b153847070b3eca35a2..fd70163896113f0a20b66c5181749d58385b4c34 100644 --- a/crates/collab_ui/src/notification_panel.rs +++ b/crates/collab_ui/src/notification_panel.rs @@ -544,9 +544,7 @@ impl Render for NotificationPanel { .p_4() .child( Button::new("connect_prompt_button", "Connect") - .icon_color(Color::Muted) - .icon(IconName::Github) - .icon_position(IconPosition::Start) + .start_icon(Icon::new(IconName::Github).color(Color::Muted)) .style(ButtonStyle::Filled) .full_width() .on_click({ diff --git a/crates/copilot_ui/src/sign_in.rs b/crates/copilot_ui/src/sign_in.rs index 24b1218305474a29ac2d2e7c8e0a212d6d757522..033effd230d65fee7594d0241b2828a41908a432 100644 --- a/crates/copilot_ui/src/sign_in.rs +++ b/crates/copilot_ui/src/sign_in.rs @@ -387,10 +387,11 @@ impl CopilotCodeVerification { .full_width() .style(ButtonStyle::Outlined) .size(ButtonSize::Medium) - .icon(IconName::Download) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) + .start_icon( + Icon::new(IconName::Download) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(move |_, window, cx| { reinstall_and_sign_in(copilot.clone(), window, cx) }), @@ -570,10 +571,11 @@ impl ConfigurationView { } }) .style(ButtonStyle::Outlined) - .icon(IconName::Github) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) + .start_icon( + Icon::new(IconName::Github) + .size(IconSize::Small) + .color(Color::Muted), + ) .when(edit_prediction, |this| this.tab_index(0isize)) .on_click(|_, window, cx| { if let Some(app_state) = AppState::global(cx).upgrade() @@ -600,10 +602,11 @@ impl ConfigurationView { } }) .style(ButtonStyle::Outlined) - .icon(IconName::Download) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) + .start_icon( + Icon::new(IconName::Download) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(|_, window, cx| { if let Some(app_state) = AppState::global(cx).upgrade() && let Some(copilot) = GlobalCopilotAuth::try_get_or_init(app_state, cx) diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index cac96918e32cde4770bedac69fb92a08825e3b25..7e11fe4e19f9acafdb9e2d0be30069f3d5457e5c 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -1821,20 +1821,22 @@ impl Render for DebugPanel { .gap_2() .child( Button::new("spawn-new-session-empty-state", "New Session") - .icon(IconName::Plus) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::Plus) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(|_, window, cx| { window.dispatch_action(crate::Start.boxed_clone(), cx); }), ) .child( Button::new("edit-debug-settings", "Edit debug.json") - .icon(IconName::Code) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::Code) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(|_, window, cx| { window.dispatch_action( zed_actions::OpenProjectDebugTasks.boxed_clone(), @@ -1844,10 +1846,11 @@ impl Render for DebugPanel { ) .child( Button::new("open-debugger-docs", "Debugger Docs") - .icon(IconName::Book) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::Book) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(|_, _, cx| cx.open_url("https://zed.dev/docs/debugger")), ) .child( @@ -1855,10 +1858,11 @@ impl Render for DebugPanel { "spawn-new-session-install-extensions", "Debugger Extensions", ) - .icon(IconName::Blocks) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::Blocks) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(|_, window, cx| { window.dispatch_action( zed_actions::Extensions { diff --git a/crates/edit_prediction_ui/src/rate_prediction_modal.rs b/crates/edit_prediction_ui/src/rate_prediction_modal.rs index 1c4328d8a1d301b7cc01aa520c166bda4b40e32d..b2e7209c1a7e9dd403ed0ee70336119ef0f1bdc9 100644 --- a/crates/edit_prediction_ui/src/rate_prediction_modal.rs +++ b/crates/edit_prediction_ui/src/rate_prediction_modal.rs @@ -765,9 +765,7 @@ impl RatePredictionsModal { .gap_1() .child( Button::new("bad", "Bad Prediction") - .icon(IconName::ThumbsDown) - .icon_size(IconSize::Small) - .icon_position(IconPosition::Start) + .start_icon(Icon::new(IconName::ThumbsDown).size(IconSize::Small)) .disabled(rated || feedback_empty) .when(feedback_empty, |this| { this.tooltip(Tooltip::text( @@ -791,9 +789,7 @@ impl RatePredictionsModal { ) .child( Button::new("good", "Good Prediction") - .icon(IconName::ThumbsUp) - .icon_size(IconSize::Small) - .icon_position(IconPosition::Start) + .start_icon(Icon::new(IconName::ThumbsUp).size(IconSize::Small)) .disabled(rated) .key_binding(KeyBinding::for_action_in( &ThumbsUpActivePrediction, diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index 7343edcdef3851bfeb7a3aa80f3449ff06f55d9f..2d0b151a107000e913ba4772d7d3d2bf50474fc1 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -1056,10 +1056,11 @@ impl ExtensionsPage { "Install", ) .style(ButtonStyle::Tinted(ui::TintColor::Accent)) - .icon(IconName::Download) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::Download) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click({ let extension_id = extension.id.clone(); move |_, _, cx| { @@ -1078,10 +1079,11 @@ impl ExtensionsPage { "Install", ) .style(ButtonStyle::Tinted(ui::TintColor::Accent)) - .icon(IconName::Download) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::Download) + .size(IconSize::Small) + .color(Color::Muted), + ) .disabled(true), configure: None, upgrade: None, @@ -1479,10 +1481,11 @@ impl ExtensionsPage { } }); let open_registry_button = Button::new("open_registry", "Learn More") - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Small) - .icon_position(IconPosition::End) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click({ move |_event, _window, cx| { telemetry::event!( @@ -1520,9 +1523,7 @@ impl ExtensionsPage { cx: &mut Context, ) -> impl IntoElement { let docs_url_button = Button::new("open_docs", "View Documentation") - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Small) - .icon_position(IconPosition::End) + .end_icon(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)) .on_click({ move |_event, _window, cx| { telemetry::event!( diff --git a/crates/git_graph/src/git_graph.rs b/crates/git_graph/src/git_graph.rs index 12ed44cd7ec2de0e68d56642b756e1be824e19fe..b0a4701cd25021e2725ff28b7cc45d1b4f203c8d 100644 --- a/crates/git_graph/src/git_graph.rs +++ b/crates/git_graph/src/git_graph.rs @@ -1494,10 +1494,9 @@ impl GitGraph { this.child( Button::new("author-email-copy", author_email.clone()) - .icon(icon) - .icon_size(IconSize::Small) - .icon_color(icon_color) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(icon).size(IconSize::Small).color(icon_color), + ) .label_size(LabelSize::Small) .truncate(true) .color(Color::Muted) @@ -1542,10 +1541,9 @@ impl GitGraph { }; Button::new("sha-button", &full_sha) - .icon(icon) - .icon_size(IconSize::Small) - .icon_color(icon_color) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(icon).size(IconSize::Small).color(icon_color), + ) .label_size(LabelSize::Small) .truncate(true) .color(Color::Muted) @@ -1602,10 +1600,9 @@ impl GitGraph { "view-on-provider", format!("View on {}", provider_name), ) - .icon(icon) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(icon).size(IconSize::Small).color(Color::Muted), + ) .label_size(LabelSize::Small) .truncate(true) .color(Color::Muted) diff --git a/crates/git_ui/src/blame_ui.rs b/crates/git_ui/src/blame_ui.rs index e91d98038818224594c1f139f70d7c3d11f2a78b..c2d7333484224bbfbc248e25fb2ac51a19f428e2 100644 --- a/crates/git_ui/src/blame_ui.rs +++ b/crates/git_ui/src/blame_ui.rs @@ -322,10 +322,11 @@ impl BlameRenderer for GitBlameRenderer { format!("#{}", pr.number), ) .color(Color::Muted) - .icon(IconName::PullRequest) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) + .start_icon( + Icon::new(IconName::PullRequest) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(move |_, _, cx| { cx.stop_propagation(); cx.open_url(pr.url.as_str()) @@ -339,10 +340,11 @@ impl BlameRenderer for GitBlameRenderer { short_commit_id.clone(), ) .color(Color::Muted) - .icon(IconName::FileGit) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) + .start_icon( + Icon::new(IconName::FileGit) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(move |_, window, cx| { CommitView::open( commit_summary.sha.clone().into(), diff --git a/crates/git_ui/src/commit_modal.rs b/crates/git_ui/src/commit_modal.rs index 57c25681439f9bb8ea7e5761c01d4c1a9defd427..432da803e6eedfec304836198f6111f5418084cc 100644 --- a/crates/git_ui/src/commit_modal.rs +++ b/crates/git_ui/src/commit_modal.rs @@ -366,11 +366,12 @@ impl CommitModal { .unwrap_or_else(|| "".to_owned()); let branch_picker_button = panel_button(branch) - .icon(IconName::GitBranch) - .icon_size(IconSize::Small) - .icon_color(Color::Placeholder) + .start_icon( + Icon::new(IconName::GitBranch) + .size(IconSize::Small) + .color(Color::Placeholder), + ) .color(Color::Muted) - .icon_position(IconPosition::Start) .on_click(cx.listener(|_, _, window, cx| { window.dispatch_action(zed_actions::git::Branch.boxed_clone(), cx); })) diff --git a/crates/git_ui/src/commit_tooltip.rs b/crates/git_ui/src/commit_tooltip.rs index 21e7d8a5d1f8e3f5c5b124fe8b276028df91b752..4740e148099980a7510a1f551d0d3f51c08892a1 100644 --- a/crates/git_ui/src/commit_tooltip.rs +++ b/crates/git_ui/src/commit_tooltip.rs @@ -336,9 +336,10 @@ impl Render for CommitTooltip { format!("#{}", pr.number), ) .color(Color::Muted) - .icon(IconName::PullRequest) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::PullRequest) + .color(Color::Muted), + ) .style(ButtonStyle::Subtle) .on_click(move |_, _, cx| { cx.stop_propagation(); @@ -354,9 +355,9 @@ impl Render for CommitTooltip { ) .style(ButtonStyle::Subtle) .color(Color::Muted) - .icon(IconName::FileGit) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::FileGit).color(Color::Muted), + ) .on_click( move |_, window, cx| { CommitView::open( diff --git a/crates/git_ui/src/commit_view.rs b/crates/git_ui/src/commit_view.rs index 8f2a019fddf0513c100a53956c81012d11c2ca30..b7f7b526ca16ed6686965f82180d0dcbb63f994a 100644 --- a/crates/git_ui/src/commit_view.rs +++ b/crates/git_ui/src/commit_view.rs @@ -524,10 +524,11 @@ impl CommitView { .when(self.stash.is_none(), |this| { this.child( Button::new("sha", "Commit SHA") - .icon(copy_icon) - .icon_color(copy_icon_color) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) + .start_icon( + Icon::new(copy_icon) + .size(IconSize::Small) + .color(copy_icon_color), + ) .tooltip({ let commit_sha = commit_sha.clone(); move |_, cx| { diff --git a/crates/git_ui/src/conflict_view.rs b/crates/git_ui/src/conflict_view.rs index 7bb880abe6d1209aaf6b15d78979cc388bf37a36..d3bb5213a5c5c94171d48d324c7ce05e6399399f 100644 --- a/crates/git_ui/src/conflict_view.rs +++ b/crates/git_ui/src/conflict_view.rs @@ -453,10 +453,11 @@ fn render_conflict_buttons( this.child(Divider::vertical()).child( Button::new("resolve-with-agent", "Resolve with Agent") .label_size(LabelSize::Small) - .icon(IconName::ZedAssistant) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .start_icon( + Icon::new(IconName::ZedAssistant) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click({ let conflict = conflict.clone(); move |_, window, cx| { diff --git a/crates/git_ui/src/file_history_view.rs b/crates/git_ui/src/file_history_view.rs index 03cf6671a23524a0e514ee5c11f55d5eba666796..e0cee4ef1d66b7c09ff249d2323fc9fa72abbd7c 100644 --- a/crates/git_ui/src/file_history_view.rs +++ b/crates/git_ui/src/file_history_view.rs @@ -429,10 +429,11 @@ impl Render for FileHistoryView { Button::new("load-more", "Load More") .disabled(self.loading_more) .label_size(LabelSize::Small) - .icon(IconName::ArrowCircle) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::ArrowCircle) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(cx.listener(|this, _, window, cx| { this.load_more(window, cx); })), diff --git a/crates/git_ui/src/git_ui.rs b/crates/git_ui/src/git_ui.rs index 1a9866fcc6e7ef420742620dab3faa2f38bfa5f5..01375e600392d2b18b34ec3241aff45c5fad6e67 100644 --- a/crates/git_ui/src/git_ui.rs +++ b/crates/git_ui/src/git_ui.rs @@ -872,8 +872,7 @@ impl Render for GitCloneModal { .child( Button::new("learn-more", "Learn More") .label_size(LabelSize::Small) - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::XSmall) + .end_icon(Icon::new(IconName::ArrowUpRight).size(IconSize::XSmall)) .on_click(|_, _, cx| { cx.open_url("https://github.com/git-guides/git-clone"); }), diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs index 3af77b8fb680abbca2688410b783007af573578d..41eff7b23a95ca2d4112d4b95aef67ff7d4a765f 100644 --- a/crates/git_ui/src/project_diff.rs +++ b/crates/git_ui/src/project_diff.rs @@ -1592,8 +1592,11 @@ fn render_send_review_to_agent_button(review_count: usize, focus_handle: &FocusH "send-review", format!("Send Review to Agent ({})", review_count), ) - .icon(IconName::ZedAssistant) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::ZedAssistant) + .size(IconSize::Small) + .color(Color::Muted), + ) .tooltip(Tooltip::for_action_title_in( "Send all review comments to the Agent panel", &SendReviewToAgent, @@ -1686,10 +1689,11 @@ impl Render for BranchDiffToolbar { let focus_handle = focus_handle.clone(); this.child(Divider::vertical()).child( Button::new("review-diff", "Review Diff") - .icon(IconName::ZedAssistant) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .start_icon( + Icon::new(IconName::ZedAssistant) + .size(IconSize::Small) + .color(Color::Muted), + ) .key_binding(KeyBinding::for_action_in(&ReviewDiff, &focus_handle, cx)) .tooltip(move |_, cx| { Tooltip::with_meta_in( diff --git a/crates/keymap_editor/src/keymap_editor.rs b/crates/keymap_editor/src/keymap_editor.rs index ff3389a4d4a10bc8472d0931d18ffa5be839c631..c8df5c1d8cf60ed07d6013cfb088bf8d362cf330 100644 --- a/crates/keymap_editor/src/keymap_editor.rs +++ b/crates/keymap_editor/src/keymap_editor.rs @@ -2928,9 +2928,11 @@ impl Render for KeybindingEditorModal { .child( Button::new("show_matching", "View") .label_size(LabelSize::Small) - .icon(IconName::ArrowUpRight) - .icon_color(Color::Muted) - .icon_size(IconSize::Small) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(cx.listener( |this, _, window, cx| { this.show_matching_bindings( diff --git a/crates/language_models/src/provider/bedrock.rs b/crates/language_models/src/provider/bedrock.rs index 5b493fdf1087911372d8796cc88f4ad14eef8df0..0df2f0856c36053367172dd3a0412a0cb6cf4e6f 100644 --- a/crates/language_models/src/provider/bedrock.rs +++ b/crates/language_models/src/provider/bedrock.rs @@ -1574,7 +1574,8 @@ impl Render for ConfigurationView { } v_flex() - .size_full() + .min_w_0() + .w_full() .track_focus(&self.focus_handle) .on_action(cx.listener(Self::on_tab)) .on_action(cx.listener(Self::on_tab_prev)) diff --git a/crates/language_models/src/provider/cloud.rs b/crates/language_models/src/provider/cloud.rs index 4fdf06cc959ccc853f92f4e150978cd15c8e70d3..b871015826f36fb3dc9b727fb8b194c46c0ec05c 100644 --- a/crates/language_models/src/provider/cloud.rs +++ b/crates/language_models/src/provider/cloud.rs @@ -1126,6 +1126,7 @@ impl RenderOnce for ZedAiConfiguration { let manage_subscription_buttons = if is_pro { Button::new("manage_settings", "Manage Subscription") .full_width() + .label_size(LabelSize::Small) .style(ButtonStyle::Tinted(TintColor::Accent)) .on_click(|_, _, cx| cx.open_url(&zed_urls::account_url(cx))) .into_any_element() @@ -1149,10 +1150,7 @@ impl RenderOnce for ZedAiConfiguration { .child(Label::new("Sign in to have access to Zed's complete agentic experience with hosted models.")) .child( Button::new("sign_in", "Sign In to use Zed AI") - .icon_color(Color::Muted) - .icon(IconName::Github) - .icon_size(IconSize::Small) - .icon_position(IconPosition::Start) + .start_icon(Icon::new(IconName::Github).size(IconSize::Small).color(Color::Muted)) .full_width() .on_click({ let callback = self.sign_in_callback.clone(); diff --git a/crates/language_models/src/provider/lmstudio.rs b/crates/language_models/src/provider/lmstudio.rs index ee08f1689aeea9cfa18346108cd2d314b2259583..6c8d3c6e1c50185a4b09e9afc80c688f4c8d1381 100644 --- a/crates/language_models/src/provider/lmstudio.rs +++ b/crates/language_models/src/provider/lmstudio.rs @@ -820,9 +820,7 @@ impl ConfigurationView { .child( Button::new("reset-api-url", "Reset API URL") .label_size(LabelSize::Small) - .icon(IconName::Undo) - .icon_size(IconSize::Small) - .icon_position(IconPosition::Start) + .start_icon(Icon::new(IconName::Undo).size(IconSize::Small)) .layer(ElevationIndex::ModalSurface) .on_click( cx.listener(|this, _, _window, cx| this.reset_api_url(_window, cx)), @@ -918,9 +916,11 @@ impl Render for ConfigurationView { this.child( Button::new("lmstudio-site", "LM Studio") .style(ButtonStyle::Subtle) - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(move |_, _window, cx| { cx.open_url(LMSTUDIO_SITE) }) @@ -933,9 +933,11 @@ impl Render for ConfigurationView { "Download LM Studio", ) .style(ButtonStyle::Subtle) - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(move |_, _window, cx| { cx.open_url(LMSTUDIO_DOWNLOAD_URL) }) @@ -946,9 +948,11 @@ impl Render for ConfigurationView { .child( Button::new("view-models", "Model Catalog") .style(ButtonStyle::Subtle) - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(move |_, _window, cx| { cx.open_url(LMSTUDIO_CATALOG_URL) }), @@ -981,9 +985,9 @@ impl Render for ConfigurationView { } else { this.child( Button::new("retry_lmstudio_models", "Connect") - .icon_position(IconPosition::Start) - .icon_size(IconSize::XSmall) - .icon(IconName::PlayFilled) + .start_icon( + Icon::new(IconName::PlayFilled).size(IconSize::XSmall), + ) .on_click(cx.listener(move |this, _, _window, cx| { this.retry_connection(_window, cx) })), diff --git a/crates/language_models/src/provider/ollama.rs b/crates/language_models/src/provider/ollama.rs index 96343ec060e13ff4e63bbdf96db3b2501e32a461..30234687633215ec6a1da6f9d63ea136d08254b8 100644 --- a/crates/language_models/src/provider/ollama.rs +++ b/crates/language_models/src/provider/ollama.rs @@ -858,9 +858,7 @@ impl ConfigurationView { .child( Button::new("reset-context-window", "Reset") .label_size(LabelSize::Small) - .icon(IconName::Undo) - .icon_size(IconSize::Small) - .icon_position(IconPosition::Start) + .start_icon(Icon::new(IconName::Undo).size(IconSize::Small)) .layer(ElevationIndex::ModalSurface) .on_click( cx.listener(|this, _, window, cx| { @@ -905,9 +903,7 @@ impl ConfigurationView { .child( Button::new("reset-api-url", "Reset API URL") .label_size(LabelSize::Small) - .icon(IconName::Undo) - .icon_size(IconSize::Small) - .icon_position(IconPosition::Start) + .start_icon(Icon::new(IconName::Undo).size(IconSize::Small)) .layer(ElevationIndex::ModalSurface) .on_click( cx.listener(|this, _, window, cx| this.reset_api_url(window, cx)), @@ -949,9 +945,11 @@ impl Render for ConfigurationView { this.child( Button::new("ollama-site", "Ollama") .style(ButtonStyle::Subtle) - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::XSmall) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::XSmall) + .color(Color::Muted), + ) .on_click(move |_, _, cx| cx.open_url(OLLAMA_SITE)) .into_any_element(), ) @@ -959,9 +957,11 @@ impl Render for ConfigurationView { this.child( Button::new("download_ollama_button", "Download Ollama") .style(ButtonStyle::Subtle) - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::XSmall) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::XSmall) + .color(Color::Muted), + ) .on_click(move |_, _, cx| { cx.open_url(OLLAMA_DOWNLOAD_URL) }) @@ -972,9 +972,11 @@ impl Render for ConfigurationView { .child( Button::new("view-models", "View All Models") .style(ButtonStyle::Subtle) - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::XSmall) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::XSmall) + .color(Color::Muted), + ) .on_click(move |_, _, cx| cx.open_url(OLLAMA_LIBRARY_URL)), ), ) @@ -1005,9 +1007,9 @@ impl Render for ConfigurationView { } else { this.child( Button::new("retry_ollama_models", "Connect") - .icon_position(IconPosition::Start) - .icon_size(IconSize::XSmall) - .icon(IconName::PlayOutlined) + .start_icon( + Icon::new(IconName::PlayOutlined).size(IconSize::XSmall), + ) .on_click(cx.listener(move |this, _, window, cx| { this.retry_connection(window, cx) })), diff --git a/crates/language_models/src/provider/open_ai.rs b/crates/language_models/src/provider/open_ai.rs index ce79de7cb2df22847a2666d7b4847e2c696fb12e..c1ebf76e0b0678d35a5e013e87f9efd9488a4e8d 100644 --- a/crates/language_models/src/provider/open_ai.rs +++ b/crates/language_models/src/provider/open_ai.rs @@ -1415,9 +1415,11 @@ impl Render for ConfigurationView { ) .child( Button::new("docs", "Learn More") - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(move |_, _window, cx| { cx.open_url("https://zed.dev/docs/ai/llm-providers#openai-api-compatible") }), diff --git a/crates/language_models/src/provider/open_ai_compatible.rs b/crates/language_models/src/provider/open_ai_compatible.rs index b478bc843c05e01d428561d9c255ef0d2ca97148..87a08097782198238a5d2467af32cc66b3183664 100644 --- a/crates/language_models/src/provider/open_ai_compatible.rs +++ b/crates/language_models/src/provider/open_ai_compatible.rs @@ -545,9 +545,7 @@ impl Render for ConfigurationView { .child( Button::new("reset-api-key", "Reset API Key") .label_size(LabelSize::Small) - .icon(IconName::Undo) - .icon_size(IconSize::Small) - .icon_position(IconPosition::Start) + .start_icon(Icon::new(IconName::Undo).size(IconSize::Small)) .layer(ElevationIndex::ModalSurface) .when(env_var_set, |this| { this.tooltip(Tooltip::text(format!("To reset your API key, unset the {env_var_name} environment variable."))) diff --git a/crates/language_onboarding/src/python.rs b/crates/language_onboarding/src/python.rs index e715cb7c806f417980a93a62210c72ca8529fcb5..751980fd57af5d2bd28ca17f38b88aa09741e482 100644 --- a/crates/language_onboarding/src/python.rs +++ b/crates/language_onboarding/src/python.rs @@ -56,10 +56,8 @@ impl Render for BasedPyrightBanner { .gap_0p5() .child( Button::new("learn-more", "Learn More") - .icon(IconName::ArrowUpRight) .label_size(LabelSize::Small) - .icon_size(IconSize::XSmall) - .icon_color(Color::Muted) + .end_icon(Icon::new(IconName::ArrowUpRight).size(IconSize::XSmall).color(Color::Muted)) .on_click(|_, _, cx| { cx.open_url("https://zed.dev/docs/languages/python") }), diff --git a/crates/language_tools/src/lsp_log_view.rs b/crates/language_tools/src/lsp_log_view.rs index a4b8977da7661b09b85fff3cbb86c2a3ff1647aa..47c840ea4e2f22e1b64cfc5b78bb7f983255dcba 100644 --- a/crates/language_tools/src/lsp_log_view.rs +++ b/crates/language_tools/src/lsp_log_view.rs @@ -18,7 +18,7 @@ use project::{ }; use proto::toggle_lsp_logs::LogType; use std::{any::TypeId, borrow::Cow, sync::Arc}; -use ui::{Button, Checkbox, ContextMenu, Label, PopoverMenu, ToggleState, prelude::*}; +use ui::{Checkbox, ContextMenu, PopoverMenu, ToggleState, prelude::*}; use util::ResultExt as _; use workspace::{ SplitDirection, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, WorkspaceId, @@ -969,9 +969,11 @@ impl Render for LspLogToolbarItemView { }) .unwrap_or_else(|| "No server selected".into()), ) - .icon(IconName::ChevronDown) - .icon_size(IconSize::Small) - .icon_color(Color::Muted), + .end_icon( + Icon::new(IconName::ChevronDown) + .size(IconSize::Small) + .color(Color::Muted), + ), ) .menu({ let log_view = log_view.clone(); @@ -1030,10 +1032,11 @@ impl Render for LspLogToolbarItemView { PopoverMenu::new("LspViewSelector") .anchor(Corner::TopLeft) .trigger( - Button::new("language_server_menu_header", label) - .icon(IconName::ChevronDown) - .icon_size(IconSize::Small) - .icon_color(Color::Muted), + Button::new("language_server_menu_header", label).end_icon( + Icon::new(IconName::ChevronDown) + .size(IconSize::Small) + .color(Color::Muted), + ), ) .menu(move |window, cx| { let log_toolbar_view = log_toolbar_view.upgrade()?; @@ -1125,9 +1128,11 @@ impl Render for LspLogToolbarItemView { "language_server_trace_level_selector", "Trace level", ) - .icon(IconName::ChevronDown) - .icon_size(IconSize::Small) - .icon_color(Color::Muted), + .end_icon( + Icon::new(IconName::ChevronDown) + .size(IconSize::Small) + .color(Color::Muted), + ), ) .menu({ let log_view = log_view; @@ -1193,9 +1198,11 @@ impl Render for LspLogToolbarItemView { "language_server_log_level_selector", "Log level", ) - .icon(IconName::ChevronDown) - .icon_size(IconSize::Small) - .icon_color(Color::Muted), + .end_icon( + Icon::new(IconName::ChevronDown) + .size(IconSize::Small) + .color(Color::Muted), + ), ) .menu({ let log_view = log_view; diff --git a/crates/onboarding/src/basics_page.rs b/crates/onboarding/src/basics_page.rs index b683b13743819bbba692a99a7c559cfd9823a4b4..7221d8104cbff2e1e0a8ebe265b419b1c725472d 100644 --- a/crates/onboarding/src/basics_page.rs +++ b/crates/onboarding/src/basics_page.rs @@ -10,9 +10,8 @@ use theme::{ ThemeSettings, }; use ui::{ - Divider, ParentElement as _, StatefulInteractiveElement, SwitchField, TintColor, - ToggleButtonGroup, ToggleButtonGroupSize, ToggleButtonSimple, ToggleButtonWithIcon, Tooltip, - prelude::*, rems_from_px, + Divider, StatefulInteractiveElement, SwitchField, TintColor, ToggleButtonGroup, + ToggleButtonGroupSize, ToggleButtonSimple, ToggleButtonWithIcon, Tooltip, prelude::*, }; use vim_mode_setting::VimModeSetting; @@ -477,8 +476,7 @@ fn render_setting_import_button( .toggle_state(imported) .tab_index(tab_index) .when(imported, |this| { - this.icon(IconName::Check) - .icon_size(IconSize::Small) + this.end_icon(Icon::new(IconName::Check).size(IconSize::Small)) .color(Color::Success) }) .on_click(move |_, window, cx| { diff --git a/crates/onboarding/src/multibuffer_hint.rs b/crates/onboarding/src/multibuffer_hint.rs index 26ab409fbad6333f2e56ee4a274a43806adce676..1f710318a64760faeecb31c8a6a368a0e11537a4 100644 --- a/crates/onboarding/src/multibuffer_hint.rs +++ b/crates/onboarding/src/multibuffer_hint.rs @@ -158,10 +158,11 @@ impl Render for MultibufferHint { ) .child( Button::new("open_docs", "Learn More") - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::End) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(move |_event, _, cx| { cx.open_url("https://zed.dev/docs/multibuffers") }), diff --git a/crates/panel/src/panel.rs b/crates/panel/src/panel.rs index 133efa9cb61c122af79a228cdfb74f86e22792b4..cf6465f3f5973bf24429f010dadf369346123b8f 100644 --- a/crates/panel/src/panel.rs +++ b/crates/panel/src/panel.rs @@ -52,7 +52,6 @@ pub fn panel_button(label: impl Into) -> ui::Button { let id = ElementId::Name(label.to_lowercase().replace(' ', "_").into()); ui::Button::new(id, label) .label_size(ui::LabelSize::Small) - .icon_size(ui::IconSize::Small) // TODO: Change this once we use on_surface_bg in button_like .layer(ui::ElevationIndex::ModalSurface) .size(ui::ButtonSize::Compact) diff --git a/crates/recent_projects/src/disconnected_overlay.rs b/crates/recent_projects/src/disconnected_overlay.rs index 82ff0699054e5614b8078d3223d5e9282e5034b5..732b50c123d9d61750781df81ce00b392997af3c 100644 --- a/crates/recent_projects/src/disconnected_overlay.rs +++ b/crates/recent_projects/src/disconnected_overlay.rs @@ -2,11 +2,7 @@ use gpui::{ClickEvent, DismissEvent, EventEmitter, FocusHandle, Focusable, Rende use project::project_settings::ProjectSettings; use remote::RemoteConnectionOptions; use settings::Settings; -use ui::{ - Button, ButtonCommon, ButtonStyle, Clickable, Context, ElevationIndex, FluentBuilder, Headline, - HeadlineSize, IconName, IconPosition, InteractiveElement, IntoElement, Label, Modal, - ModalFooter, ModalHeader, ParentElement, Section, Styled, StyledExt, Window, div, h_flex, rems, -}; +use ui::{ElevationIndex, Modal, ModalFooter, ModalHeader, Section, prelude::*}; use workspace::{ ModalView, MultiWorkspace, OpenOptions, Workspace, notifications::DetachAndPromptErr, }; @@ -207,8 +203,7 @@ impl Render for DisconnectedOverlay { Button::new("reconnect", "Reconnect") .style(ButtonStyle::Filled) .layer(ElevationIndex::ModalSurface) - .icon(IconName::ArrowCircle) - .icon_position(IconPosition::Start) + .start_icon(Icon::new(IconName::ArrowCircle)) .on_click(cx.listener(Self::handle_reconnect)), ) }), diff --git a/crates/recent_projects/src/remote_servers.rs b/crates/recent_projects/src/remote_servers.rs index 60ebf85dd23460a8a0ce0c70da2d7b69761690db..d4cfb6520e6f73592ede5abcacb558967d10dbc7 100644 --- a/crates/recent_projects/src/remote_servers.rs +++ b/crates/recent_projects/src/remote_servers.rs @@ -2117,8 +2117,10 @@ impl RemoteServerProjects { .child( Button::new("learn-more", "Learn More") .label_size(LabelSize::Small) - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::XSmall) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::XSmall), + ) .on_click(|_, _, cx| { cx.open_url( "https://zed.dev/docs/remote-development", diff --git a/crates/repl/src/components/kernel_options.rs b/crates/repl/src/components/kernel_options.rs index b6d4f39c0ccb75619a7e4efd6a532202893c8722..ce68a4d30285fe04427c54aa8d5fbdc3aa059648 100644 --- a/crates/repl/src/components/kernel_options.rs +++ b/crates/repl/src/components/kernel_options.rs @@ -431,10 +431,11 @@ impl PickerDelegate for KernelPickerDelegate { .gap_4() .child( Button::new("kernel-docs", "Kernel Docs") - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::End) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(move |_, _, cx| cx.open_url(KERNEL_DOCS_URL)), ) .into_any(), diff --git a/crates/repl/src/notebook/notebook_ui.rs b/crates/repl/src/notebook/notebook_ui.rs index 87f18708a1988c70d66dc4cef5355d4cbcb11dba..76a0d2a47037f0ccd48fcfe9cb088ceb9e37aeaa 100644 --- a/crates/repl/src/notebook/notebook_ui.rs +++ b/crates/repl/src/notebook/notebook_ui.rs @@ -1117,10 +1117,11 @@ impl NotebookEditor { worktree_id, Button::new("kernel-selector", kernel_name.clone()) .label_size(LabelSize::Small) - .icon(status_icon) - .icon_size(IconSize::Small) - .icon_color(status_color) - .icon_position(IconPosition::Start), + .start_icon( + Icon::new(status_icon) + .size(IconSize::Small) + .color(status_color), + ), Tooltip::text(format!( "Kernel: {} ({}). Click to change.", kernel_name, diff --git a/crates/rules_library/src/rules_library.rs b/crates/rules_library/src/rules_library.rs index dd4bbcfaeb7a14ea4bda8c546f5cf2539734eb73..cb568c95627bd6a32dcd89b7cfd645f4dac65f59 100644 --- a/crates/rules_library/src/rules_library.rs +++ b/crates/rules_library/src/rules_library.rs @@ -1170,10 +1170,11 @@ impl RulesLibrary { Button::new("new-rule", "New Rule") .full_width() .style(ButtonStyle::Outlined) - .icon(IconName::Plus) - .icon_size(IconSize::Small) - .icon_position(IconPosition::Start) - .icon_color(Color::Muted) + .start_icon( + Icon::new(IconName::Plus) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(|_, window, cx| { window.dispatch_action(Box::new(NewRule), cx); }), diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 9b23c96259e4933bc1660af960b508c0678fe767..292dfd7e5fad4174ecd7dbe51bb28f3a1df98827 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1583,9 +1583,7 @@ impl ProjectSearchView { ) .child( Button::new("filter-paths", "Include/exclude specific paths") - .icon(IconName::Filter) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) + .start_icon(Icon::new(IconName::Filter).size(IconSize::Small)) .key_binding(KeyBinding::for_action_in(&ToggleFilters, &focus_handle, cx)) .on_click(|_event, window, cx| { window.dispatch_action(ToggleFilters.boxed_clone(), cx) @@ -1593,9 +1591,7 @@ impl ProjectSearchView { ) .child( Button::new("find-replace", "Find and replace") - .icon(IconName::Replace) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) + .start_icon(Icon::new(IconName::Replace).size(IconSize::Small)) .key_binding(KeyBinding::for_action_in(&ToggleReplace, &focus_handle, cx)) .on_click(|_event, window, cx| { window.dispatch_action(ToggleReplace.boxed_clone(), cx) @@ -1603,9 +1599,7 @@ impl ProjectSearchView { ) .child( Button::new("regex", "Match with regex") - .icon(IconName::Regex) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) + .start_icon(Icon::new(IconName::Regex).size(IconSize::Small)) .key_binding(KeyBinding::for_action_in(&ToggleRegex, &focus_handle, cx)) .on_click(|_event, window, cx| { window.dispatch_action(ToggleRegex.boxed_clone(), cx) @@ -1613,9 +1607,7 @@ impl ProjectSearchView { ) .child( Button::new("match-case", "Match case") - .icon(IconName::CaseSensitive) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) + .start_icon(Icon::new(IconName::CaseSensitive).size(IconSize::Small)) .key_binding(KeyBinding::for_action_in( &ToggleCaseSensitive, &focus_handle, @@ -1627,9 +1619,7 @@ impl ProjectSearchView { ) .child( Button::new("match-whole-words", "Match whole words") - .icon(IconName::WholeWord) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) + .start_icon(Icon::new(IconName::WholeWord).size(IconSize::Small)) .key_binding(KeyBinding::for_action_in( &ToggleWholeWord, &focus_handle, diff --git a/crates/settings_ui/src/pages/tool_permissions_setup.rs b/crates/settings_ui/src/pages/tool_permissions_setup.rs index c1c978efbb3da5dc57c8d40a45370a908698bd40..f5f1f0ea7eb71c7af41ba2c60a30b2ec5cb01a4d 100644 --- a/crates/settings_ui/src/pages/tool_permissions_setup.rs +++ b/crates/settings_ui/src/pages/tool_permissions_setup.rs @@ -275,10 +275,11 @@ fn render_tool_list_item( .tab_index(tool_index as isize) .style(ButtonStyle::OutlinedGhost) .size(ButtonSize::Medium) - .icon(IconName::ChevronRight) - .icon_position(IconPosition::End) - .icon_color(Color::Muted) - .icon_size(IconSize::Small) + .end_icon( + Icon::new(IconName::ChevronRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(cx.listener(move |this, _, window, cx| { this.push_dynamic_sub_page( tool_name, @@ -1090,9 +1091,7 @@ fn render_global_default_mode_section(current_mode: ToolPermissionMode) -> AnyEl .tab_index(0_isize) .style(ButtonStyle::Outlined) .size(ButtonSize::Medium) - .icon(IconName::ChevronDown) - .icon_position(IconPosition::End) - .icon_size(IconSize::Small), + .end_icon(Icon::new(IconName::ChevronDown).size(IconSize::Small)), ) .menu(move |window, cx| { Some(ContextMenu::build(window, cx, move |menu, _, _| { @@ -1141,9 +1140,7 @@ fn render_default_mode_section( .tab_index(0_isize) .style(ButtonStyle::Outlined) .size(ButtonSize::Medium) - .icon(IconName::ChevronDown) - .icon_position(IconPosition::End) - .icon_size(IconSize::Small), + .end_icon(Icon::new(IconName::ChevronDown).size(IconSize::Small)), ) .menu(move |window, cx| { let tool_id = tool_id_owned.clone(); diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index 9d7fe83736be8d1d9ed79d85708c5ed0574b7e3a..26417a5469955cd89a12564248e36be288004a15 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -925,9 +925,7 @@ impl SettingsPageItem { Button::new("error-warning", warning) .style(ButtonStyle::Outlined) .size(ButtonSize::Medium) - .icon(Some(IconName::Debug)) - .icon_position(IconPosition::Start) - .icon_color(Color::Error) + .start_icon(Icon::new(IconName::Debug).color(Color::Error)) .tab_index(0_isize) .tooltip(Tooltip::text(setting_item.field.type_name())) .into_any_element(), @@ -992,11 +990,12 @@ impl SettingsPageItem { ("sub-page".into(), sub_page_link.title.clone()), "Configure", ) - .icon(IconName::ChevronRight) .tab_index(0_isize) - .icon_position(IconPosition::End) - .icon_color(Color::Muted) - .icon_size(IconSize::Small) + .end_icon( + Icon::new(IconName::ChevronRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .style(ButtonStyle::OutlinedGhost) .size(ButtonSize::Medium) .on_click({ @@ -1125,11 +1124,12 @@ impl SettingsPageItem { ("action-link".into(), action_link.title.clone()), action_link.button_text.clone(), ) - .icon(IconName::ArrowUpRight) .tab_index(0_isize) - .icon_position(IconPosition::End) - .icon_color(Color::Muted) - .icon_size(IconSize::Small) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .style(ButtonStyle::OutlinedGhost) .size(ButtonSize::Medium) .on_click({ @@ -4174,10 +4174,11 @@ fn render_picker_trigger_button(id: SharedString, label: SharedString) -> Button .tab_index(0_isize) .style(ButtonStyle::Outlined) .size(ButtonSize::Medium) - .icon(IconName::ChevronUpDown) - .icon_color(Color::Muted) - .icon_size(IconSize::Small) - .icon_position(IconPosition::End) + .end_icon( + Icon::new(IconName::ChevronUpDown) + .size(IconSize::Small) + .color(Color::Muted), + ) } fn render_font_picker( diff --git a/crates/theme_selector/src/icon_theme_selector.rs b/crates/theme_selector/src/icon_theme_selector.rs index 2ea3436d43cd2d2a4bda392384ff51f962824143..1ddd6879405ad69a75e038da608d034f58bb5eff 100644 --- a/crates/theme_selector/src/icon_theme_selector.rs +++ b/crates/theme_selector/src/icon_theme_selector.rs @@ -311,10 +311,11 @@ impl PickerDelegate for IconThemeSelectorDelegate { .border_color(cx.theme().colors().border_variant) .child( Button::new("docs", "View Icon Theme Docs") - .icon(IconName::ArrowUpRight) - .icon_position(IconPosition::End) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(|_event, _window, cx| { cx.open_url("https://zed.dev/docs/icon-themes"); }), diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 74b242dd0b7c3a3ddbe6ca76d34a59f03560f14a..f3c32c8f2f50cbec820e043a701f382e6ac22d0a 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -497,10 +497,11 @@ impl PickerDelegate for ThemeSelectorDelegate { .border_color(cx.theme().colors().border_variant) .child( Button::new("docs", "View Theme Docs") - .icon(IconName::ArrowUpRight) - .icon_position(IconPosition::End) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Small) + .color(Color::Muted), + ) .on_click(cx.listener(|_, _, _, cx| { cx.open_url("https://zed.dev/docs/themes"); })), diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index 916d58426b76f020bce8a9bf69971f34bc3803a4..7fc86706a3eb0971b1f8539d76b8daf3b709537e 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -583,10 +583,11 @@ impl TitleBar { .style(ButtonStyle::Tinted(TintColor::Warning)) .label_size(LabelSize::Small) .color(Color::Warning) - .icon(IconName::Warning) - .icon_color(Color::Warning) - .icon_size(IconSize::Small) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::Warning) + .size(IconSize::Small) + .color(Color::Warning), + ) .tooltip(|_, cx| { Tooltip::with_meta( "You're in Restricted Mode", @@ -697,9 +698,11 @@ impl TitleBar { Button::new("project_name_trigger", display_name) .label_size(LabelSize::Small) .when(self.worktree_count(cx) > 1, |this| { - this.icon(IconName::ChevronDown) - .icon_color(Color::Muted) - .icon_size(IconSize::XSmall) + this.end_icon( + Icon::new(IconName::ChevronDown) + .size(IconSize::XSmall) + .color(Color::Muted), + ) }) .selected_style(ButtonStyle::Tinted(TintColor::Accent)) .when(!is_project_selected, |s| s.color(Color::Muted)), @@ -779,11 +782,9 @@ impl TitleBar { .color(Color::Muted) .when(settings.show_branch_icon, |branch_button| { let (icon, icon_color) = icon_info; - branch_button - .icon(icon) - .icon_position(IconPosition::Start) - .icon_color(icon_color) - .icon_size(IconSize::Indicator) + branch_button.start_icon( + Icon::new(icon).size(IconSize::Indicator).color(icon_color), + ) }), move |_window, cx| { Tooltip::with_meta( diff --git a/crates/ui/src/components/ai/configured_api_card.rs b/crates/ui/src/components/ai/configured_api_card.rs index 2104e816811a68776f69f3970b53636dbbd63e17..c9fd129a678d008d2ff0d6833e1497f61c73d989 100644 --- a/crates/ui/src/components/ai/configured_api_card.rs +++ b/crates/ui/src/components/ai/configured_api_card.rs @@ -133,10 +133,11 @@ impl RenderOnce for ConfiguredApiCard { elem.tab_index(tab_index) }) .label_size(LabelSize::Small) - .icon(IconName::Undo) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) + .start_icon( + Icon::new(IconName::Undo) + .size(IconSize::Small) + .color(Color::Muted), + ) .disabled(self.disabled) .when_some(self.tooltip_label, |this, label| { this.tooltip(Tooltip::text(label)) diff --git a/crates/ui/src/components/banner.rs b/crates/ui/src/components/banner.rs index 199c72113afae37ab97c96932f5b9e805c5628bd..19795c2c7c86045572ac4a031276a6552a1d68ee 100644 --- a/crates/ui/src/components/banner.rs +++ b/crates/ui/src/components/banner.rs @@ -8,16 +8,14 @@ use gpui::{AnyElement, IntoElement, ParentElement, Styled}; /// /// ``` /// use ui::prelude::*; -/// use ui::{Banner, Button, IconName, IconPosition, IconSize, Label, Severity}; +/// use ui::{Banner, Button, Icon, IconName, IconSize, Label, Severity}; /// /// Banner::new() /// .severity(Severity::Success) /// .children([Label::new("This is a success message")]) /// .action_slot( /// Button::new("learn-more", "Learn More") -/// .icon(IconName::ArrowUpRight) -/// .icon_size(IconSize::Small) -/// .icon_position(IconPosition::End) +/// .end_icon(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)), /// ); /// ``` #[derive(IntoElement, RegisterComponent)] @@ -151,9 +149,7 @@ impl Component for Banner { .child(Label::new("This is an informational message")) .action_slot( Button::new("learn-more", "Learn More") - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Small) - .icon_position(IconPosition::End), + .end_icon(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)), ) .into_any_element(), ), diff --git a/crates/ui/src/components/button.rs b/crates/ui/src/components/button.rs index 17c216ec7b000bd9b563b3e00d4ee9979ca5287f..bcec46e59ce66a242cbd96d840e4323751541f92 100644 --- a/crates/ui/src/components/button.rs +++ b/crates/ui/src/components/button.rs @@ -1,5 +1,4 @@ mod button; -mod button_icon; mod button_like; mod button_link; mod copy_button; diff --git a/crates/ui/src/components/button/button.rs b/crates/ui/src/components/button/button.rs index 2ac3b9ca13123a0d9330d71e8b73d034d65faf89..52ea9df14293e5aa25ab8de4487975019a6481ff 100644 --- a/crates/ui/src/components/button/button.rs +++ b/crates/ui/src/components/button/button.rs @@ -2,15 +2,12 @@ use crate::component_prelude::*; use gpui::{AnyElement, AnyView, DefiniteLength}; use ui_macros::RegisterComponent; -use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, IconName, IconSize, Label}; +use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, Label}; use crate::{ - Color, DynamicSpacing, ElevationIndex, IconPosition, KeyBinding, KeybindingPosition, TintColor, - prelude::*, + Color, DynamicSpacing, ElevationIndex, KeyBinding, KeybindingPosition, TintColor, prelude::*, }; -use super::button_icon::ButtonIcon; - -/// An element that creates a button with a label and an optional icon. +/// An element that creates a button with a label and optional icons. /// /// Common buttons: /// - Label, Icon + Label: [`Button`] (this component) @@ -42,7 +39,7 @@ use super::button_icon::ButtonIcon; /// use ui::prelude::*; /// /// Button::new("button_id", "Click me!") -/// .icon(IconName::Check) +/// .start_icon(Icon::new(IconName::Check)) /// .toggle_state(true) /// .on_click(|event, window, cx| { /// // Handle click event @@ -85,12 +82,8 @@ pub struct Button { label_size: Option, selected_label: Option, selected_label_color: Option, - icon: Option, - icon_position: Option, - icon_size: Option, - icon_color: Option, - selected_icon: Option, - selected_icon_color: Option, + start_icon: Option, + end_icon: Option, key_binding: Option, key_binding_position: KeybindingPosition, alpha: Option, @@ -112,12 +105,8 @@ impl Button { label_size: None, selected_label: None, selected_label_color: None, - icon: None, - icon_position: None, - icon_size: None, - icon_color: None, - selected_icon: None, - selected_icon_color: None, + start_icon: None, + end_icon: None, key_binding: None, key_binding_position: KeybindingPosition::default(), alpha: None, @@ -149,39 +138,19 @@ impl Button { self } - /// Assigns an icon to the button. - pub fn icon(mut self, icon: impl Into>) -> Self { - self.icon = icon.into(); - self - } - - /// Sets the position of the icon relative to the label. - pub fn icon_position(mut self, icon_position: impl Into>) -> Self { - self.icon_position = icon_position.into(); - self - } - - /// Specifies the size of the button's icon. - pub fn icon_size(mut self, icon_size: impl Into>) -> Self { - self.icon_size = icon_size.into(); - self - } - - /// Sets the color of the button's icon. - pub fn icon_color(mut self, icon_color: impl Into>) -> Self { - self.icon_color = icon_color.into(); - self - } - - /// Chooses an icon to display when the button is in a selected state. - pub fn selected_icon(mut self, icon: impl Into>) -> Self { - self.selected_icon = icon.into(); + /// Sets an icon to display at the start (left) of the button label. + /// + /// The icon's color will be overridden to `Color::Disabled` when the button is disabled. + pub fn start_icon(mut self, icon: impl Into>) -> Self { + self.start_icon = icon.into(); self } - /// Sets the icon color used when the button is in a selected state. - pub fn selected_icon_color(mut self, color: impl Into>) -> Self { - self.selected_icon_color = color.into(); + /// Sets an icon to display at the end (right) of the button label. + /// + /// The icon's color will be overridden to `Color::Disabled` when the button is disabled. + pub fn end_icon(mut self, icon: impl Into>) -> Self { + self.end_icon = icon.into(); self } @@ -219,22 +188,24 @@ impl Button { impl Toggleable for Button { /// Sets the selected state of the button. /// - /// This method allows the selection state of the button to be specified. - /// It modifies the button's appearance to reflect its selected state. - /// /// # Examples /// + /// Create a toggleable button that changes appearance when selected: + /// /// ``` /// use ui::prelude::*; + /// use ui::TintColor; /// - /// Button::new("button_id", "Click me!") - /// .toggle_state(true) + /// let selected = true; + /// + /// Button::new("toggle_button", "Toggle Me") + /// .start_icon(Icon::new(IconName::Check)) + /// .toggle_state(selected) + /// .selected_style(ButtonStyle::Tinted(TintColor::Accent)) /// .on_click(|event, window, cx| { - /// // Handle click event + /// // Toggle the selected state /// }); /// ``` - /// - /// Use [`selected_style`](Button::selected_style) to change the style of the button when it is selected. fn toggle_state(mut self, selected: bool) -> Self { self.base = self.base.toggle_state(selected); self @@ -242,22 +213,20 @@ impl Toggleable for Button { } impl SelectableButton for Button { - /// Sets the style for the button when selected. + /// Sets the style for the button in a selected state. /// /// # Examples /// + /// Customize the selected appearance of a button: + /// /// ``` /// use ui::prelude::*; /// use ui::TintColor; /// - /// Button::new("button_id", "Click me!") + /// Button::new("styled_button", "Styled Button") /// .toggle_state(true) - /// .selected_style(ButtonStyle::Tinted(TintColor::Accent)) - /// .on_click(|event, window, cx| { - /// // Handle click event - /// }); + /// .selected_style(ButtonStyle::Tinted(TintColor::Accent)); /// ``` - /// This results in a button with a blue tinted background when selected. fn selected_style(mut self, style: ButtonStyle) -> Self { self.base = self.base.selected_style(style); self @@ -265,36 +234,27 @@ impl SelectableButton for Button { } impl Disableable for Button { - /// Disables the button. + /// Disables the button, preventing interaction and changing its appearance. /// - /// This method allows the button to be disabled. When a button is disabled, - /// it doesn't react to user interactions and its appearance is updated to reflect this. + /// When disabled, the button's icon and label will use `Color::Disabled`. /// /// # Examples /// + /// Create a disabled button: + /// /// ``` /// use ui::prelude::*; /// - /// Button::new("button_id", "Click me!") - /// .disabled(true) - /// .on_click(|event, window, cx| { - /// // Handle click event - /// }); + /// Button::new("disabled_button", "Can't Click Me") + /// .disabled(true); /// ``` - /// - /// This results in a button that is disabled and does not respond to click events. fn disabled(mut self, disabled: bool) -> Self { self.base = self.base.disabled(disabled); - self.key_binding = self - .key_binding - .take() - .map(|binding| binding.disabled(disabled)); self } } impl Clickable for Button { - /// Sets the click event handler for the button. fn on_click( mut self, handler: impl Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static, @@ -310,44 +270,35 @@ impl Clickable for Button { } impl FixedWidth for Button { - /// Sets a fixed width for the button. - /// - /// This function allows a button to have a fixed width instead of automatically growing or shrinking. /// Sets a fixed width for the button. /// /// # Examples /// + /// Create a button with a fixed width of 100 pixels: + /// /// ``` /// use ui::prelude::*; /// - /// Button::new("button_id", "Click me!") - /// .width(px(100.)) - /// .on_click(|event, window, cx| { - /// // Handle click event - /// }); + /// Button::new("fixed_width_button", "Fixed Width") + /// .width(px(100.0)); /// ``` - /// - /// This sets the button's width to be exactly 100 pixels. fn width(mut self, width: impl Into) -> Self { self.base = self.base.width(width); self } - /// Sets the button to occupy the full width of its container. + /// Makes the button take up the full width of its container. /// /// # Examples /// + /// Create a button that takes up the full width of its container: + /// /// ``` /// use ui::prelude::*; /// - /// Button::new("button_id", "Click me!") - /// .full_width() - /// .on_click(|event, window, cx| { - /// // Handle click event - /// }); + /// Button::new("full_width_button", "Full Width") + /// .full_width(); /// ``` - /// - /// This stretches the button to the full width of its container. fn full_width(mut self) -> Self { self.base = self.base.full_width(); self @@ -355,43 +306,34 @@ impl FixedWidth for Button { } impl ButtonCommon for Button { - /// Sets the button's id. fn id(&self) -> &ElementId { self.base.id() } - /// Sets the visual style of the button using a [`ButtonStyle`]. + /// Sets the visual style of the button. fn style(mut self, style: ButtonStyle) -> Self { self.base = self.base.style(style); self } - /// Sets the button's size using a [`ButtonSize`]. + /// Sets the size of the button. fn size(mut self, size: ButtonSize) -> Self { self.base = self.base.size(size); self } - /// Sets a tooltip for the button. - /// - /// This method allows a tooltip to be set for the button. The tooltip is a function that - /// takes a mutable references to [`Window`] and [`App`], and returns an [`AnyView`]. The - /// tooltip is displayed when the user hovers over the button. + /// Sets a tooltip that appears on hover. /// /// # Examples /// - /// ``` - /// use ui::prelude::*; - /// use ui::Tooltip; + /// Add a tooltip to a button: /// - /// Button::new("button_id", "Click me!") - /// .tooltip(Tooltip::text("This is a tooltip")) - /// .on_click(|event, window, cx| { - /// // Handle click event - /// }); /// ``` + /// use ui::{Tooltip, prelude::*}; /// - /// This will create a button with a tooltip that displays "This is a tooltip" when hovered over. + /// Button::new("tooltip_button", "Hover Me") + /// .tooltip(Tooltip::text("This is a tooltip")); + /// ``` fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self { self.base = self.base.tooltip(tooltip); self @@ -436,16 +378,12 @@ impl RenderOnce for Button { h_flex() .when(self.truncate, |this| this.min_w_0().overflow_hidden()) .gap(DynamicSpacing::Base04.rems(cx)) - .when(self.icon_position == Some(IconPosition::Start), |this| { - this.children(self.icon.map(|icon| { - ButtonIcon::new(icon) - .disabled(is_disabled) - .toggle_state(is_selected) - .selected_icon(self.selected_icon) - .selected_icon_color(self.selected_icon_color) - .size(self.icon_size) - .color(self.icon_color) - })) + .when_some(self.start_icon, |this, icon| { + this.child(if is_disabled { + icon.color(Color::Disabled) + } else { + icon + }) }) .child( h_flex() @@ -465,16 +403,12 @@ impl RenderOnce for Button { ) .children(self.key_binding), ) - .when(self.icon_position != Some(IconPosition::Start), |this| { - this.children(self.icon.map(|icon| { - ButtonIcon::new(icon) - .disabled(is_disabled) - .toggle_state(is_selected) - .selected_icon(self.selected_icon) - .selected_icon_color(self.selected_icon_color) - .size(self.icon_size) - .color(self.icon_color) - })) + .when_some(self.end_icon, |this, icon| { + this.child(if is_disabled { + icon.color(Color::Disabled) + } else { + icon + }) }), ) } @@ -585,24 +519,28 @@ impl Component for Button { "Buttons with Icons", vec![ single_example( - "Icon Start", - Button::new("icon_start", "Icon Start") - .icon(IconName::Check) - .icon_position(IconPosition::Start) + "Start Icon", + Button::new("icon_start", "Start Icon") + .start_icon(Icon::new(IconName::Check)) + .into_any_element(), + ), + single_example( + "End Icon", + Button::new("icon_end", "End Icon") + .end_icon(Icon::new(IconName::Check)) .into_any_element(), ), single_example( - "Icon End", - Button::new("icon_end", "Icon End") - .icon(IconName::Check) - .icon_position(IconPosition::End) + "Both Icons", + Button::new("both_icons", "Both Icons") + .start_icon(Icon::new(IconName::Check)) + .end_icon(Icon::new(IconName::ChevronDown)) .into_any_element(), ), single_example( "Icon Color", Button::new("icon_color", "Icon Color") - .icon(IconName::Check) - .icon_color(Color::Accent) + .start_icon(Icon::new(IconName::Check).color(Color::Accent)) .into_any_element(), ), ], diff --git a/crates/ui/src/components/button/button_icon.rs b/crates/ui/src/components/button/button_icon.rs deleted file mode 100644 index 510c418714575112070e64e945da3e185f37ee3e..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/button/button_icon.rs +++ /dev/null @@ -1,199 +0,0 @@ -use crate::{Icon, IconName, IconSize, IconWithIndicator, Indicator, prelude::*}; -use gpui::Hsla; - -/// An icon that appears within a button. -/// -/// Can be used as either an icon alongside a label, like in [`Button`](crate::Button), -/// or as a standalone icon, like in [`IconButton`](crate::IconButton). -#[derive(IntoElement, RegisterComponent)] -pub(super) struct ButtonIcon { - icon: IconName, - size: IconSize, - color: Color, - disabled: bool, - selected: bool, - selected_icon: Option, - selected_icon_color: Option, - selected_style: Option, - indicator: Option, - indicator_border_color: Option, -} - -impl ButtonIcon { - pub fn new(icon: IconName) -> Self { - Self { - icon, - size: IconSize::default(), - color: Color::default(), - disabled: false, - selected: false, - selected_icon: None, - selected_icon_color: None, - selected_style: None, - indicator: None, - indicator_border_color: None, - } - } - - pub fn size(mut self, size: impl Into>) -> Self { - if let Some(size) = size.into() { - self.size = size; - } - self - } - - pub fn color(mut self, color: impl Into>) -> Self { - if let Some(color) = color.into() { - self.color = color; - } - self - } - - pub fn selected_icon(mut self, icon: impl Into>) -> Self { - self.selected_icon = icon.into(); - self - } - - pub fn selected_icon_color(mut self, color: impl Into>) -> Self { - self.selected_icon_color = color.into(); - self - } - - pub fn indicator(mut self, indicator: Indicator) -> Self { - self.indicator = Some(indicator); - self - } - - pub fn indicator_border_color(mut self, color: Option) -> Self { - self.indicator_border_color = color; - self - } -} - -impl Disableable for ButtonIcon { - fn disabled(mut self, disabled: bool) -> Self { - self.disabled = disabled; - self - } -} - -impl Toggleable for ButtonIcon { - fn toggle_state(mut self, selected: bool) -> Self { - self.selected = selected; - self - } -} - -impl SelectableButton for ButtonIcon { - fn selected_style(mut self, style: ButtonStyle) -> Self { - self.selected_style = Some(style); - self - } -} - -impl RenderOnce for ButtonIcon { - fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement { - let icon = self - .selected_icon - .filter(|_| self.selected) - .unwrap_or(self.icon); - - let icon_color = if self.disabled { - Color::Disabled - } else if self.selected_style.is_some() && self.selected { - self.selected_style.unwrap().into() - } else if self.selected { - self.selected_icon_color.unwrap_or(Color::Selected) - } else { - self.color - }; - - let icon = Icon::new(icon).size(self.size).color(icon_color); - - match self.indicator { - Some(indicator) => IconWithIndicator::new(icon, Some(indicator)) - .indicator_border_color(self.indicator_border_color) - .into_any_element(), - None => icon.into_any_element(), - } - } -} - -impl Component for ButtonIcon { - fn scope() -> ComponentScope { - ComponentScope::Input - } - - fn name() -> &'static str { - "ButtonIcon" - } - - fn description() -> Option<&'static str> { - Some("An icon component specifically designed for use within buttons.") - } - - fn preview(_window: &mut Window, _cx: &mut App) -> Option { - Some( - v_flex() - .gap_6() - .children(vec![ - example_group_with_title( - "Basic Usage", - vec![ - single_example( - "Default", - ButtonIcon::new(IconName::Star).into_any_element(), - ), - single_example( - "Custom Size", - ButtonIcon::new(IconName::Star) - .size(IconSize::Medium) - .into_any_element(), - ), - single_example( - "Custom Color", - ButtonIcon::new(IconName::Star) - .color(Color::Accent) - .into_any_element(), - ), - ], - ), - example_group_with_title( - "States", - vec![ - single_example( - "Selected", - ButtonIcon::new(IconName::Star) - .toggle_state(true) - .into_any_element(), - ), - single_example( - "Disabled", - ButtonIcon::new(IconName::Star) - .disabled(true) - .into_any_element(), - ), - ], - ), - example_group_with_title( - "With Indicator", - vec![ - single_example( - "Default Indicator", - ButtonIcon::new(IconName::Star) - .indicator(Indicator::dot()) - .into_any_element(), - ), - single_example( - "Custom Indicator", - ButtonIcon::new(IconName::Star) - .indicator(Indicator::dot().color(Color::Error)) - .into_any_element(), - ), - ], - ), - ]) - .into_any_element(), - ) - } -} diff --git a/crates/ui/src/components/button/icon_button.rs b/crates/ui/src/components/button/icon_button.rs index 961176ed6cee7e55c7a51cd52719c0eef8a8f181..a103ddf169a8ba3ed9d1b6bf6055ff84858aef7d 100644 --- a/crates/ui/src/components/button/icon_button.rs +++ b/crates/ui/src/components/button/icon_button.rs @@ -1,11 +1,11 @@ use gpui::{AnyView, DefiniteLength, Hsla}; use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle}; -use crate::{ElevationIndex, Indicator, SelectableButton, TintColor, prelude::*}; +use crate::{ + ElevationIndex, Icon, IconWithIndicator, Indicator, SelectableButton, TintColor, prelude::*, +}; use crate::{IconName, IconSize}; -use super::button_icon::ButtonIcon; - /// The shape of an [`IconButton`]. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] pub enum IconButtonShape { @@ -22,6 +22,7 @@ pub struct IconButton { icon_color: Color, selected_icon: Option, selected_icon_color: Option, + selected_style: Option, indicator: Option, indicator_border_color: Option, alpha: Option, @@ -37,6 +38,7 @@ impl IconButton { icon_color: Color::Default, selected_icon: None, selected_icon_color: None, + selected_style: None, indicator: None, indicator_border_color: None, alpha: None, @@ -112,6 +114,7 @@ impl Toggleable for IconButton { impl SelectableButton for IconButton { fn selected_style(mut self, style: ButtonStyle) -> Self { + self.selected_style = Some(style); self.base = self.base.selected_style(style); self } @@ -192,9 +195,25 @@ impl RenderOnce for IconButton { fn render(self, window: &mut Window, cx: &mut App) -> ButtonLike { let is_disabled = self.base.disabled; let is_selected = self.base.selected; - let selected_style = self.base.selected_style; - let color = self.icon_color.color(cx).opacity(self.alpha.unwrap_or(1.0)); + let icon = self + .selected_icon + .filter(|_| is_selected) + .unwrap_or(self.icon); + + let icon_color = if is_disabled { + Color::Disabled + } else if self.selected_style.is_some() && is_selected { + self.selected_style.unwrap().into() + } else if is_selected { + self.selected_icon_color.unwrap_or(Color::Selected) + } else { + let base_color = self.icon_color.color(cx); + Color::Custom(base_color.opacity(self.alpha.unwrap_or(1.0))) + }; + + let icon_element = Icon::new(icon).size(self.icon_size).color(icon_color); + self.base .map(|this| match self.shape { IconButtonShape::Square => { @@ -203,20 +222,12 @@ impl RenderOnce for IconButton { } IconButtonShape::Wide => this, }) - .child( - ButtonIcon::new(self.icon) - .disabled(is_disabled) - .toggle_state(is_selected) - .selected_icon(self.selected_icon) - .selected_icon_color(self.selected_icon_color) - .when_some(selected_style, |this, style| this.selected_style(style)) - .when_some(self.indicator, |this, indicator| { - this.indicator(indicator) - .indicator_border_color(self.indicator_border_color) - }) - .size(self.icon_size) - .color(Color::Custom(color)), - ) + .child(match self.indicator { + Some(indicator) => IconWithIndicator::new(icon_element, Some(indicator)) + .indicator_border_color(self.indicator_border_color) + .into_any_element(), + None => icon_element.into_any_element(), + }) } } diff --git a/crates/ui/src/components/dropdown_menu.rs b/crates/ui/src/components/dropdown_menu.rs index 7a1d3c7dfd77306b2d7b3b6786dae04d6eaee6b2..961608461c04971cda81cfdd64d9eb62577f07ed 100644 --- a/crates/ui/src/components/dropdown_menu.rs +++ b/crates/ui/src/components/dropdown_menu.rs @@ -163,11 +163,10 @@ impl RenderOnce for DropdownMenu { Some( Button::new(self.id.clone(), text) .style(button_style) - .when(self.chevron, |this| { - this.icon(self.trigger_icon) - .icon_position(IconPosition::End) - .icon_size(IconSize::XSmall) - .icon_color(Color::Muted) + .when_some(self.trigger_icon.filter(|_| self.chevron), |this, icon| { + this.end_icon( + Icon::new(icon).size(IconSize::XSmall).color(Color::Muted), + ) }) .when(full_width, |this| this.full_width()) .size(trigger_size) diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 9f4b5538ed67bde3f32969467828296485b7810f..29bb9d7b063ff6e4b9f472d708f354fb50f7a2e8 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -917,11 +917,11 @@ pub mod simple_message_notification { })); if let Some(icon) = self.primary_icon { - button = button - .icon(icon) - .icon_color(self.primary_icon_color.unwrap_or(Color::Muted)) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small); + button = button.start_icon( + Icon::new(icon) + .size(IconSize::Small) + .color(self.primary_icon_color.unwrap_or(Color::Muted)), + ); } button @@ -937,11 +937,11 @@ pub mod simple_message_notification { })); if let Some(icon) = self.secondary_icon { - button = button - .icon(icon) - .icon_position(IconPosition::Start) - .icon_size(IconSize::Small) - .icon_color(self.secondary_icon_color.unwrap_or(Color::Muted)); + button = button.start_icon( + Icon::new(icon) + .size(IconSize::Small) + .color(self.secondary_icon_color.unwrap_or(Color::Muted)), + ); } button @@ -955,9 +955,11 @@ pub mod simple_message_notification { let url = url.clone(); Button::new(message.clone(), message.clone()) .label_size(LabelSize::Small) - .icon(IconName::ArrowUpRight) - .icon_size(IconSize::Indicator) - .icon_color(Color::Muted) + .end_icon( + Icon::new(IconName::ArrowUpRight) + .size(IconSize::Indicator) + .color(Color::Muted), + ) .on_click(cx.listener(move |_, _, _, cx| { cx.open_url(&url); }))