diff --git a/crates/activity_indicator/src/activity_indicator.rs b/crates/activity_indicator/src/activity_indicator.rs index 6641db0805fed2fbade1e66cde143f58123dd3d4..b65d1472a7552d56ec319e12295088a2973796d5 100644 --- a/crates/activity_indicator/src/activity_indicator.rs +++ b/crates/activity_indicator/src/activity_indicator.rs @@ -1,11 +1,10 @@ use auto_update::{AutoUpdateStatus, AutoUpdater, DismissErrorMessage, VersionCheckType}; use editor::Editor; -use extension_host::ExtensionStore; +use extension_host::{ExtensionOperation, ExtensionStore}; use futures::StreamExt; use gpui::{ - Animation, AnimationExt as _, App, Context, CursorStyle, Entity, EventEmitter, - InteractiveElement as _, ParentElement as _, Render, SharedString, StatefulInteractiveElement, - Styled, Transformation, Window, actions, percentage, + App, Context, CursorStyle, Entity, EventEmitter, InteractiveElement as _, ParentElement as _, + Render, SharedString, StatefulInteractiveElement, Styled, Window, actions, }; use language::{ BinaryStatus, LanguageRegistry, LanguageServerId, LanguageServerName, @@ -25,7 +24,10 @@ use std::{ sync::Arc, time::{Duration, Instant}, }; -use ui::{ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*}; +use ui::{ + ButtonLike, CommonAnimationExt, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip, + prelude::*, +}; use util::truncate_and_trailoff; use workspace::{StatusItemView, Workspace, item::ItemHandle}; @@ -405,13 +407,7 @@ impl ActivityIndicator { icon: Some( Icon::new(IconName::ArrowCircle) .size(IconSize::Small) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| { - icon.transform(Transformation::rotate(percentage(delta))) - }, - ) + .with_rotate_animation(2) .into_any_element(), ), message, @@ -433,11 +429,7 @@ impl ActivityIndicator { icon: Some( Icon::new(IconName::ArrowCircle) .size(IconSize::Small) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ) + .with_rotate_animation(2) .into_any_element(), ), message: format!("Debug: {}", session.read(cx).adapter()), @@ -460,11 +452,7 @@ impl ActivityIndicator { icon: Some( Icon::new(IconName::ArrowCircle) .size(IconSize::Small) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ) + .with_rotate_animation(2) .into_any_element(), ), message: job_info.message.into(), @@ -671,8 +659,9 @@ impl ActivityIndicator { } // Show any application auto-update info. - if let Some(updater) = &self.auto_updater { - return match &updater.read(cx).status() { + self.auto_updater + .as_ref() + .and_then(|updater| match &updater.read(cx).status() { AutoUpdateStatus::Checking => Some(Content { icon: Some( Icon::new(IconName::Download) @@ -728,28 +717,49 @@ impl ActivityIndicator { tooltip_message: None, }), AutoUpdateStatus::Idle => None, - }; - } - - if let Some(extension_store) = - ExtensionStore::try_global(cx).map(|extension_store| extension_store.read(cx)) - && let Some(extension_id) = extension_store.outstanding_operations().keys().next() - { - return Some(Content { - icon: Some( - Icon::new(IconName::Download) - .size(IconSize::Small) - .into_any_element(), - ), - message: format!("Updating {extension_id} extension…"), - on_click: Some(Arc::new(|this, window, cx| { - this.dismiss_error_message(&DismissErrorMessage, window, cx) - })), - tooltip_message: None, - }); - } + }) + .or_else(|| { + if let Some(extension_store) = + ExtensionStore::try_global(cx).map(|extension_store| extension_store.read(cx)) + && let Some((extension_id, operation)) = + extension_store.outstanding_operations().iter().next() + { + let (message, icon, rotate) = match operation { + ExtensionOperation::Install => ( + format!("Installing {extension_id} extension…"), + IconName::LoadCircle, + true, + ), + ExtensionOperation::Upgrade => ( + format!("Updating {extension_id} extension…"), + IconName::Download, + false, + ), + ExtensionOperation::Remove => ( + format!("Removing {extension_id} extension…"), + IconName::LoadCircle, + true, + ), + }; - None + Some(Content { + icon: Some(Icon::new(icon).size(IconSize::Small).map(|this| { + if rotate { + this.with_rotate_animation(3).into_any_element() + } else { + this.into_any_element() + } + })), + message, + on_click: Some(Arc::new(|this, window, cx| { + this.dismiss_error_message(&Default::default(), window, cx) + })), + tooltip_message: None, + }) + } else { + None + } + }) } fn version_tooltip_message(version: &VersionCheckType) -> String { diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index c3bf7219b4919c7e479f22f327b73d90b0b1dad7..a9421723d125d27f3eb13c43fb17936f9078dae8 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -23,9 +23,9 @@ use gpui::{ Action, Animation, AnimationExt, AnyView, App, BorderStyle, ClickEvent, ClipboardItem, CursorStyle, EdgesRefinement, ElementId, Empty, Entity, FocusHandle, Focusable, Hsla, Length, ListOffset, ListState, MouseButton, PlatformDisplay, SharedString, Stateful, StyleRefinement, - Subscription, Task, TextStyle, TextStyleRefinement, Transformation, UnderlineStyle, WeakEntity, - Window, WindowHandle, div, ease_in_out, linear_color_stop, linear_gradient, list, percentage, - point, prelude::*, pulsating_between, + Subscription, Task, TextStyle, TextStyleRefinement, UnderlineStyle, WeakEntity, Window, + WindowHandle, div, ease_in_out, linear_color_stop, linear_gradient, list, point, prelude::*, + pulsating_between, }; use language::Buffer; @@ -45,8 +45,8 @@ use terminal_view::terminal_panel::TerminalPanel; use text::Anchor; use theme::ThemeSettings; use ui::{ - Callout, Disclosure, Divider, DividerColor, ElevationIndex, KeyBinding, PopoverMenuHandle, - Scrollbar, ScrollbarState, SpinnerLabel, Tooltip, prelude::*, + Callout, CommonAnimationExt, Disclosure, Divider, DividerColor, ElevationIndex, KeyBinding, + PopoverMenuHandle, Scrollbar, ScrollbarState, SpinnerLabel, Tooltip, prelude::*, }; use util::{ResultExt, size::format_file_size, time::duration_alt_display}; use workspace::{CollaboratorId, Workspace}; @@ -2515,13 +2515,7 @@ impl AcpThreadView { Icon::new(IconName::ArrowCircle) .size(IconSize::XSmall) .color(Color::Info) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| { - icon.transform(Transformation::rotate(percentage(delta))) - }, - ), + .with_rotate_animation(2) ) }) .child( @@ -2948,16 +2942,7 @@ impl AcpThreadView { Icon::new(IconName::ArrowCircle) .size(IconSize::Small) .color(Color::Muted) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| { - icon.transform(Transformation::rotate(percentage( - delta, - ))) - }, - ) - .into_any_element(), + .with_rotate_animation(2) ) .child(Label::new("Authenticating…").size(LabelSize::Small)), ) @@ -3270,13 +3255,7 @@ impl AcpThreadView { acp::PlanEntryStatus::InProgress => Icon::new(IconName::TodoProgress) .size(IconSize::Small) .color(Color::Accent) - .with_animation( - "running", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| { - icon.transform(Transformation::rotate(percentage(delta))) - }, - ) + .with_rotate_animation(2) .into_any_element(), acp::PlanEntryStatus::Completed => Icon::new(IconName::TodoComplete) .size(IconSize::Small) @@ -5000,11 +4979,7 @@ fn loading_contents_spinner(size: IconSize) -> AnyElement { Icon::new(IconName::LoadCircle) .size(size) .color(Color::Accent) - .with_animation( - "load_context_circle", - Animation::new(Duration::from_secs(3)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ) + .with_rotate_animation(3) .into_any_element() } diff --git a/crates/agent_ui/src/active_thread.rs b/crates/agent_ui/src/active_thread.rs index e0cecad6e2e8b37d649a9dbc0d91268096670365..371a59e7eb9eb88dc5200251f971ef851162b630 100644 --- a/crates/agent_ui/src/active_thread.rs +++ b/crates/agent_ui/src/active_thread.rs @@ -23,9 +23,8 @@ use gpui::{ AbsoluteLength, Animation, AnimationExt, AnyElement, App, ClickEvent, ClipboardEntry, ClipboardItem, DefiniteLength, EdgesRefinement, Empty, Entity, EventEmitter, Focusable, Hsla, ListAlignment, ListOffset, ListState, MouseButton, PlatformDisplay, ScrollHandle, Stateful, - StyleRefinement, Subscription, Task, TextStyle, TextStyleRefinement, Transformation, - UnderlineStyle, WeakEntity, WindowHandle, linear_color_stop, linear_gradient, list, percentage, - pulsating_between, + StyleRefinement, Subscription, Task, TextStyle, TextStyleRefinement, UnderlineStyle, + WeakEntity, WindowHandle, linear_color_stop, linear_gradient, list, pulsating_between, }; use language::{Buffer, Language, LanguageRegistry}; use language_model::{ @@ -46,8 +45,8 @@ use std::time::Duration; use text::ToPoint; use theme::ThemeSettings; use ui::{ - Banner, Disclosure, KeyBinding, PopoverMenuHandle, Scrollbar, ScrollbarState, TextSize, - Tooltip, prelude::*, + Banner, CommonAnimationExt, Disclosure, KeyBinding, PopoverMenuHandle, Scrollbar, + ScrollbarState, TextSize, Tooltip, prelude::*, }; use util::ResultExt as _; use util::markdown::MarkdownCodeBlock; @@ -2647,15 +2646,7 @@ impl ActiveThread { Icon::new(IconName::ArrowCircle) .color(Color::Accent) .size(IconSize::Small) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| { - icon.transform(Transformation::rotate( - percentage(delta), - )) - }, - ) + .with_rotate_animation(2) }), ), ) @@ -2831,17 +2822,11 @@ impl ActiveThread { } ToolUseStatus::Pending | ToolUseStatus::InputStillStreaming - | ToolUseStatus::Running => { - let icon = Icon::new(IconName::ArrowCircle) - .color(Color::Accent) - .size(IconSize::Small); - icon.with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ) - .into_any_element() - } + | ToolUseStatus::Running => Icon::new(IconName::ArrowCircle) + .color(Color::Accent) + .size(IconSize::Small) + .with_rotate_animation(2) + .into_any_element(), ToolUseStatus::Finished(_) => div().w_0().into_any_element(), ToolUseStatus::Error(_) => { let icon = Icon::new(IconName::Close) @@ -2930,15 +2915,7 @@ impl ActiveThread { Icon::new(IconName::ArrowCircle) .size(IconSize::Small) .color(Color::Accent) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| { - icon.transform(Transformation::rotate(percentage( - delta, - ))) - }, - ), + .with_rotate_animation(2), ) .child( Label::new("Running…") diff --git a/crates/agent_ui/src/agent_configuration.rs b/crates/agent_ui/src/agent_configuration.rs index 5f0b6f33c38b0b064fcb8b287a901a33e9e7186b..5981a3c52bf52ff4549b2f73a6322e308725750d 100644 --- a/crates/agent_ui/src/agent_configuration.rs +++ b/crates/agent_ui/src/agent_configuration.rs @@ -3,7 +3,7 @@ mod configure_context_server_modal; mod manage_profiles_modal; mod tool_picker; -use std::{ops::Range, sync::Arc, time::Duration}; +use std::{ops::Range, sync::Arc}; use agent_servers::{AgentServerCommand, AllAgentServersSettings, CustomAgentServerSettings}; use agent_settings::AgentSettings; @@ -17,9 +17,8 @@ use extension::ExtensionManifest; use extension_host::ExtensionStore; use fs::Fs; use gpui::{ - Action, Animation, AnimationExt as _, AnyView, App, AsyncWindowContext, Corner, Entity, - EventEmitter, FocusHandle, Focusable, Hsla, ScrollHandle, Subscription, Task, Transformation, - WeakEntity, percentage, + Action, AnyView, App, AsyncWindowContext, Corner, Entity, EventEmitter, FocusHandle, Focusable, + Hsla, ScrollHandle, Subscription, Task, WeakEntity, }; use language::LanguageRegistry; use language_model::{ @@ -32,8 +31,9 @@ use project::{ }; use settings::{Settings, SettingsStore, update_settings_file}; use ui::{ - Chip, ContextMenu, Disclosure, Divider, DividerColor, ElevationIndex, Indicator, PopoverMenu, - Scrollbar, ScrollbarState, Switch, SwitchColor, SwitchField, Tooltip, prelude::*, + Chip, CommonAnimationExt, ContextMenu, Disclosure, Divider, DividerColor, ElevationIndex, + Indicator, PopoverMenu, Scrollbar, ScrollbarState, Switch, SwitchColor, SwitchField, Tooltip, + prelude::*, }; use util::ResultExt as _; use workspace::{Workspace, create_and_open_local_file}; @@ -670,10 +670,9 @@ impl AgentConfiguration { Icon::new(IconName::LoadCircle) .size(IconSize::XSmall) .color(Color::Accent) - .with_animation( - SharedString::from(format!("{}-starting", context_server_id.0,)), - Animation::new(Duration::from_secs(3)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), + .with_keyed_rotate_animation( + SharedString::from(format!("{}-starting", context_server_id.0)), + 3, ) .into_any_element(), "Server is starting.", 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 c898a5acb5b8d0a45780efb383ece19b4cfe289d..e5027b876ac0f996e1f4df2a61af1477c6490c10 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 @@ -1,16 +1,14 @@ use std::{ path::PathBuf, sync::{Arc, Mutex}, - time::Duration, }; use anyhow::{Context as _, Result}; use context_server::{ContextServerCommand, ContextServerId}; use editor::{Editor, EditorElement, EditorStyle}; use gpui::{ - Animation, AnimationExt as _, AsyncWindowContext, DismissEvent, Entity, EventEmitter, - FocusHandle, Focusable, Task, TextStyle, TextStyleRefinement, Transformation, UnderlineStyle, - WeakEntity, percentage, prelude::*, + AsyncWindowContext, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Task, + TextStyle, TextStyleRefinement, UnderlineStyle, WeakEntity, prelude::*, }; use language::{Language, LanguageRegistry}; use markdown::{Markdown, MarkdownElement, MarkdownStyle}; @@ -24,7 +22,9 @@ use project::{ }; use settings::{Settings as _, update_settings_file}; use theme::ThemeSettings; -use ui::{KeyBinding, Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*}; +use ui::{ + CommonAnimationExt, KeyBinding, Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*, +}; use util::ResultExt as _; use workspace::{ModalView, Workspace}; @@ -638,11 +638,7 @@ impl ConfigureContextServerModal { Icon::new(IconName::ArrowCircle) .size(IconSize::XSmall) .color(Color::Info) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ) + .with_rotate_animation(2) .into_any_element(), ) .child( diff --git a/crates/agent_ui/src/agent_diff.rs b/crates/agent_ui/src/agent_diff.rs index 4bd525e9d0461a7a180cccc1748e7f8983c0b665..74bcb266d52ac25c91f3243c3e76f1e1f25d770e 100644 --- a/crates/agent_ui/src/agent_diff.rs +++ b/crates/agent_ui/src/agent_diff.rs @@ -14,9 +14,8 @@ use editor::{ scroll::Autoscroll, }; use gpui::{ - Action, Animation, AnimationExt, AnyElement, AnyView, App, AppContext, Empty, Entity, - EventEmitter, FocusHandle, Focusable, Global, SharedString, Subscription, Task, Transformation, - WeakEntity, Window, percentage, prelude::*, + Action, AnyElement, AnyView, App, AppContext, Empty, Entity, EventEmitter, FocusHandle, + Focusable, Global, SharedString, Subscription, Task, WeakEntity, Window, prelude::*, }; use language::{Buffer, Capability, DiskState, OffsetRangeExt, Point}; @@ -29,9 +28,8 @@ use std::{ collections::hash_map::Entry, ops::Range, sync::Arc, - time::Duration, }; -use ui::{IconButtonShape, KeyBinding, Tooltip, prelude::*, vertical_divider}; +use ui::{CommonAnimationExt, IconButtonShape, KeyBinding, Tooltip, prelude::*, vertical_divider}; use util::ResultExt; use workspace::{ Item, ItemHandle, ItemNavHistory, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, @@ -1084,11 +1082,7 @@ impl Render for AgentDiffToolbar { Icon::new(IconName::LoadCircle) .size(IconSize::Small) .color(Color::Accent) - .with_animation( - "load_circle", - Animation::new(Duration::from_secs(3)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ), + .with_rotate_animation(3), ) .into_any(); diff --git a/crates/agent_ui/src/text_thread_editor.rs b/crates/agent_ui/src/text_thread_editor.rs index 70ec94beeadb1ae84839bab6747715223f2540c9..d979db5e0468b696d32ed755aec1ef47e2fd3df3 100644 --- a/crates/agent_ui/src/text_thread_editor.rs +++ b/crates/agent_ui/src/text_thread_editor.rs @@ -25,8 +25,8 @@ use gpui::{ Action, Animation, AnimationExt, AnyElement, AnyView, App, ClipboardEntry, ClipboardItem, Empty, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, Global, InteractiveElement, IntoElement, ParentElement, Pixels, Render, RenderImage, SharedString, Size, - StatefulInteractiveElement, Styled, Subscription, Task, Transformation, WeakEntity, actions, - div, img, percentage, point, prelude::*, pulsating_between, size, + StatefulInteractiveElement, Styled, Subscription, Task, WeakEntity, actions, div, img, point, + prelude::*, pulsating_between, size, }; use language::{ BufferSnapshot, LspAdapterDelegate, ToOffset, @@ -53,8 +53,8 @@ use std::{ }; use text::SelectionGoal; use ui::{ - ButtonLike, Disclosure, ElevationIndex, KeyBinding, PopoverMenuHandle, TintColor, Tooltip, - prelude::*, + ButtonLike, CommonAnimationExt, Disclosure, ElevationIndex, KeyBinding, PopoverMenuHandle, + TintColor, Tooltip, prelude::*, }; use util::{ResultExt, maybe}; use workspace::{ @@ -1061,15 +1061,7 @@ impl TextThreadEditor { Icon::new(IconName::ArrowCircle) .size(IconSize::XSmall) .color(Color::Info) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| { - icon.transform(Transformation::rotate( - percentage(delta), - )) - }, - ) + .with_rotate_animation(2) .into_any_element(), ); note = Some(Self::esc_kbd(cx).into_any_element()); @@ -2790,11 +2782,7 @@ fn invoked_slash_command_fold_placeholder( .child(Label::new(format!("/{}", command.name))) .map(|parent| match &command.status { InvokedSlashCommandStatus::Running(_) => { - parent.child(Icon::new(IconName::ArrowCircle).with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(4)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - )) + parent.child(Icon::new(IconName::ArrowCircle).with_rotate_animation(4)) } InvokedSlashCommandStatus::Error(message) => parent.child( Label::new(format!("error: {message}")) diff --git a/crates/ai_onboarding/src/ai_upsell_card.rs b/crates/ai_onboarding/src/ai_upsell_card.rs index 106dcb0aef0ee35836b2c7c576d7c68799ea988a..efe6e4165e445c4cd92f4d08dfc0c1e1947acd55 100644 --- a/crates/ai_onboarding/src/ai_upsell_card.rs +++ b/crates/ai_onboarding/src/ai_upsell_card.rs @@ -1,12 +1,9 @@ -use std::{sync::Arc, time::Duration}; +use std::sync::Arc; use client::{Client, UserStore, zed_urls}; use cloud_llm_client::Plan; -use gpui::{ - Animation, AnimationExt, AnyElement, App, Entity, IntoElement, RenderOnce, Transformation, - Window, percentage, -}; -use ui::{Divider, Vector, VectorName, prelude::*}; +use gpui::{AnyElement, App, Entity, IntoElement, RenderOnce, Window}; +use ui::{CommonAnimationExt, Divider, Vector, VectorName, prelude::*}; use crate::{SignInStatus, YoungAccountBanner, plan_definitions::PlanDefinitions}; @@ -147,11 +144,7 @@ impl RenderOnce for AiUpsellCard { rems_from_px(72.), ) .color(Color::Custom(cx.theme().colors().text_accent.alpha(0.3))) - .with_animation( - "loading_stamp", - Animation::new(Duration::from_secs(10)).repeat(), - |this, delta| this.transform(Transformation::rotate(percentage(delta))), - ), + .with_rotate_animation(10), ); let pro_trial_stamp = div() diff --git a/crates/assistant_tools/src/edit_file_tool.rs b/crates/assistant_tools/src/edit_file_tool.rs index 7b208ccc7768c9c0df2904573e2d47504a8eb61f..d13f9891c3af1933ee49428c223d3e6737871047 100644 --- a/crates/assistant_tools/src/edit_file_tool.rs +++ b/crates/assistant_tools/src/edit_file_tool.rs @@ -17,7 +17,7 @@ use editor::{ use futures::StreamExt; use gpui::{ Animation, AnimationExt, AnyWindowHandle, App, AppContext, AsyncApp, Entity, Task, - TextStyleRefinement, Transformation, WeakEntity, percentage, pulsating_between, px, + TextStyleRefinement, WeakEntity, pulsating_between, px, }; use indoc::formatdoc; use language::{ @@ -44,7 +44,7 @@ use std::{ time::Duration, }; use theme::ThemeSettings; -use ui::{Disclosure, Tooltip, prelude::*}; +use ui::{CommonAnimationExt, Disclosure, Tooltip, prelude::*}; use util::ResultExt; use workspace::Workspace; @@ -939,11 +939,7 @@ impl ToolCard for EditFileToolCard { Icon::new(IconName::ArrowCircle) .size(IconSize::XSmall) .color(Color::Info) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ), + .with_rotate_animation(2), ) }) .when_some(error_message, |header, error_message| { diff --git a/crates/assistant_tools/src/terminal_tool.rs b/crates/assistant_tools/src/terminal_tool.rs index 774f32426540e077e5bde72081db789329f86262..1605003671621b90e58a5f62e521c0aba2c990c6 100644 --- a/crates/assistant_tools/src/terminal_tool.rs +++ b/crates/assistant_tools/src/terminal_tool.rs @@ -8,8 +8,8 @@ use anyhow::{Context as _, Result, anyhow}; use assistant_tool::{Tool, ToolCard, ToolResult, ToolUseStatus}; use futures::{FutureExt as _, future::Shared}; use gpui::{ - Animation, AnimationExt, AnyWindowHandle, App, AppContext, Empty, Entity, EntityId, Task, - TextStyleRefinement, Transformation, WeakEntity, Window, percentage, + AnyWindowHandle, App, AppContext, Empty, Entity, EntityId, Task, TextStyleRefinement, + WeakEntity, Window, }; use language::LineEnding; use language_model::{LanguageModel, LanguageModelRequest, LanguageModelToolSchemaFormat}; @@ -28,7 +28,7 @@ use std::{ }; use terminal_view::TerminalView; use theme::ThemeSettings; -use ui::{Disclosure, Tooltip, prelude::*}; +use ui::{CommonAnimationExt, Disclosure, Tooltip, prelude::*}; use util::{ ResultExt, get_system_shell, markdown::MarkdownInlineCode, size::format_file_size, time::duration_alt_display, @@ -522,11 +522,7 @@ impl ToolCard for TerminalToolCard { Icon::new(IconName::ArrowCircle) .size(IconSize::XSmall) .color(Color::Info) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ), + .with_rotate_animation(2), ) }) .when(tool_failed || command_failed, |header| { diff --git a/crates/debugger_ui/src/dropdown_menus.rs b/crates/debugger_ui/src/dropdown_menus.rs index c5399f6f69648dcfa775a6dd6da62bd637124f2c..c611d5d44f36b4eafb578a400da615bbd96b4cd2 100644 --- a/crates/debugger_ui/src/dropdown_menus.rs +++ b/crates/debugger_ui/src/dropdown_menus.rs @@ -1,9 +1,9 @@ -use std::{rc::Rc, time::Duration}; +use std::rc::Rc; use collections::HashMap; -use gpui::{Animation, AnimationExt as _, Entity, Transformation, WeakEntity, percentage}; +use gpui::{Entity, WeakEntity}; use project::debugger::session::{ThreadId, ThreadStatus}; -use ui::{ContextMenu, DropdownMenu, DropdownStyle, Indicator, prelude::*}; +use ui::{CommonAnimationExt, ContextMenu, DropdownMenu, DropdownStyle, Indicator, prelude::*}; use util::{maybe, truncate_and_trailoff}; use crate::{ @@ -152,11 +152,7 @@ impl DebugPanel { Icon::new(IconName::ArrowCircle) .size(IconSize::Small) .color(Color::Muted) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ) + .with_rotate_animation(2) .into_any_element() } else { match running_state.thread_status(cx).unwrap_or_default() { diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 4ecb4a8829659ca9a25152db8d1eff529cfff2b1..64163b0ebc33f908de5c5cd8c97a24418bf4ba43 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -31,11 +31,11 @@ use git::{ UnstageAll, }; use gpui::{ - Action, Animation, AnimationExt as _, AsyncApp, AsyncWindowContext, Axis, ClickEvent, Corner, - DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, KeyContext, - ListHorizontalSizingBehavior, ListSizingBehavior, MouseButton, MouseDownEvent, Point, - PromptLevel, ScrollStrategy, Subscription, Task, Transformation, UniformListScrollHandle, - WeakEntity, actions, anchored, deferred, percentage, uniform_list, + Action, AsyncApp, AsyncWindowContext, Axis, ClickEvent, Corner, DismissEvent, Entity, + EventEmitter, FocusHandle, Focusable, KeyContext, ListHorizontalSizingBehavior, + ListSizingBehavior, MouseButton, MouseDownEvent, Point, PromptLevel, ScrollStrategy, + Subscription, Task, UniformListScrollHandle, WeakEntity, actions, anchored, deferred, + uniform_list, }; use itertools::Itertools; use language::{Buffer, File}; @@ -63,8 +63,8 @@ use std::{collections::HashSet, sync::Arc, time::Duration, usize}; use strum::{IntoEnumIterator, VariantNames}; use time::OffsetDateTime; use ui::{ - Checkbox, ContextMenu, ElevationIndex, IconPosition, Label, LabelSize, PopoverMenu, Scrollbar, - ScrollbarState, SplitButton, Tooltip, prelude::*, + Checkbox, CommonAnimationExt, ContextMenu, ElevationIndex, IconPosition, Label, LabelSize, + PopoverMenu, Scrollbar, ScrollbarState, SplitButton, Tooltip, prelude::*, }; use util::{ResultExt, TryFutureExt, maybe}; use workspace::SERIALIZATION_THROTTLE_TIME; @@ -3088,13 +3088,7 @@ impl GitPanel { Icon::new(IconName::ArrowCircle) .size(IconSize::XSmall) .color(Color::Info) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| { - icon.transform(Transformation::rotate(percentage(delta))) - }, - ), + .with_rotate_animation(2), ) .child( Label::new("Generating Commit...") diff --git a/crates/gpui/src/elements/animation.rs b/crates/gpui/src/elements/animation.rs index 11dd19e260c20e49b87e05137771be73a3f816ea..e72fb00456d14dec74ffc56e040511c189af1d18 100644 --- a/crates/gpui/src/elements/animation.rs +++ b/crates/gpui/src/elements/animation.rs @@ -87,7 +87,7 @@ pub trait AnimationExt { } } -impl AnimationExt for E {} +impl AnimationExt for E {} /// A GPUI element that applies an animation to another element pub struct AnimationElement { diff --git a/crates/language_models/src/provider/copilot_chat.rs b/crates/language_models/src/provider/copilot_chat.rs index bd284eb72b207dee90048f06dc44a8e21ae8d34f..071424eabe3c1ad3436de201860d6220ab664a06 100644 --- a/crates/language_models/src/provider/copilot_chat.rs +++ b/crates/language_models/src/provider/copilot_chat.rs @@ -14,10 +14,7 @@ use copilot::{Copilot, Status}; use futures::future::BoxFuture; use futures::stream::BoxStream; use futures::{FutureExt, Stream, StreamExt}; -use gpui::{ - Action, Animation, AnimationExt, AnyView, App, AsyncApp, Entity, Render, Subscription, Task, - Transformation, percentage, svg, -}; +use gpui::{Action, AnyView, App, AsyncApp, Entity, Render, Subscription, Task, svg}; use language::language_settings::all_language_settings; use language_model::{ AuthenticateError, LanguageModel, LanguageModelCompletionError, LanguageModelCompletionEvent, @@ -28,8 +25,7 @@ use language_model::{ StopReason, TokenUsage, }; use settings::SettingsStore; -use std::time::Duration; -use ui::prelude::*; +use ui::{CommonAnimationExt, prelude::*}; use util::debug_panic; use crate::provider::x_ai::count_xai_tokens; @@ -672,11 +668,7 @@ impl Render for ConfigurationView { }), ) } else { - let loading_icon = Icon::new(IconName::ArrowCircle).with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(4)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ); + let loading_icon = Icon::new(IconName::ArrowCircle).with_rotate_animation(4); const ERROR_LABEL: &str = "Copilot Chat requires an active GitHub Copilot subscription. Please ensure Copilot is configured and try again, or use a different Assistant provider."; diff --git a/crates/recent_projects/src/remote_connections.rs b/crates/recent_projects/src/remote_connections.rs index e543bf219ff0bc8226e819798a9ea74a098d0f98..a7f915301f42850b03be951f596a8542842a6877 100644 --- a/crates/recent_projects/src/remote_connections.rs +++ b/crates/recent_projects/src/remote_connections.rs @@ -1,5 +1,5 @@ use std::collections::BTreeSet; -use std::{path::PathBuf, sync::Arc, time::Duration}; +use std::{path::PathBuf, sync::Arc}; use anyhow::{Context as _, Result}; use auto_update::AutoUpdater; @@ -7,9 +7,9 @@ use editor::Editor; use extension_host::ExtensionStore; use futures::channel::oneshot; use gpui::{ - Animation, AnimationExt, AnyWindowHandle, App, AsyncApp, DismissEvent, Entity, EventEmitter, - Focusable, FontFeatures, ParentElement as _, PromptLevel, Render, SemanticVersion, - SharedString, Task, TextStyleRefinement, Transformation, WeakEntity, percentage, + AnyWindowHandle, App, AsyncApp, DismissEvent, Entity, EventEmitter, Focusable, FontFeatures, + ParentElement as _, PromptLevel, Render, SemanticVersion, SharedString, Task, + TextStyleRefinement, WeakEntity, }; use language::CursorShape; @@ -24,8 +24,8 @@ use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources, SettingsUi}; use theme::ThemeSettings; use ui::{ - ActiveTheme, Color, Context, Icon, IconName, IconSize, InteractiveElement, IntoElement, Label, - LabelCommon, Styled, Window, prelude::*, + ActiveTheme, Color, CommonAnimationExt, Context, Icon, IconName, IconSize, InteractiveElement, + IntoElement, Label, LabelCommon, Styled, Window, prelude::*, }; use util::serde::default_true; use workspace::{AppState, ModalView, Workspace}; @@ -268,13 +268,7 @@ impl Render for RemoteConnectionPrompt { .child( Icon::new(IconName::ArrowCircle) .size(IconSize::Medium) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| { - icon.transform(Transformation::rotate(percentage(delta))) - }, - ), + .with_rotate_animation(2), ) .child( div() diff --git a/crates/repl/src/outputs.rs b/crates/repl/src/outputs.rs index 767b103435e1f80b2b6802bdc2525fcd992931bc..2cd6494d66be1b615e10e537c139e4b2e22af863 100644 --- a/crates/repl/src/outputs.rs +++ b/crates/repl/src/outputs.rs @@ -33,16 +33,13 @@ //! This module is designed to work with Jupyter message protocols, //! interpreting and displaying various types of Jupyter output. -use std::time::Duration; - use editor::{Editor, MultiBuffer}; -use gpui::{ - Animation, AnimationExt, AnyElement, ClipboardItem, Entity, Render, Transformation, WeakEntity, - percentage, -}; +use gpui::{AnyElement, ClipboardItem, Entity, Render, WeakEntity}; use language::Buffer; use runtimelib::{ExecutionState, JupyterMessageContent, MimeBundle, MimeType}; -use ui::{Context, IntoElement, Styled, Tooltip, Window, div, prelude::*, v_flex}; +use ui::{ + CommonAnimationExt, Context, IntoElement, Styled, Tooltip, Window, div, prelude::*, v_flex, +}; mod image; use image::ImageView; @@ -481,11 +478,7 @@ impl Render for ExecutionView { Icon::new(IconName::ArrowCircle) .size(IconSize::Small) .color(Color::Muted) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(3)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ), + .with_rotate_animation(3), ) .child(Label::new("Executing...").color(Color::Muted)) .into_any_element(), diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index bc0ec462e9fbb964abc1e305933ce759ddde0ebc..8f7ef41108afd22a7f932e8ab6ed1b74078244ec 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -9,6 +9,7 @@ use gpui::{AnimationElement, AnyElement, Hsla, IntoElement, Rems, Transformation pub use icon_decoration::*; pub use icons::*; +use crate::traits::transformable::Transformable; use crate::{Indicator, prelude::*}; #[derive(IntoElement)] @@ -180,8 +181,10 @@ impl Icon { self.size = size; self } +} - pub fn transform(mut self, transformation: Transformation) -> Self { +impl Transformable for Icon { + fn transform(mut self, transformation: Transformation) -> Self { self.transformation = transformation; self } diff --git a/crates/ui/src/components/image.rs b/crates/ui/src/components/image.rs index 6e552ddcee83e20d3812f78c67270c0291c2c0e7..8a14cffd3b2de2e184fd87a9212775c470e3118d 100644 --- a/crates/ui/src/components/image.rs +++ b/crates/ui/src/components/image.rs @@ -7,6 +7,7 @@ use strum::{EnumIter, EnumString, IntoStaticStr}; use crate::Color; use crate::prelude::*; +use crate::traits::transformable::Transformable; #[derive( Debug, PartialEq, Eq, Copy, Clone, EnumIter, EnumString, IntoStaticStr, Serialize, Deserialize, @@ -74,8 +75,10 @@ impl Vector { self.size = size; self } +} - pub fn transform(mut self, transformation: Transformation) -> Self { +impl Transformable for Vector { + fn transform(mut self, transformation: Transformation) -> Self { self.transformation = transformation; self } diff --git a/crates/ui/src/traits.rs b/crates/ui/src/traits.rs index 628c76aaddecaa291b3cfad2e6d16ccd6478c767..9627f6d6ad275dbcd4281cd6a85741d932b688fe 100644 --- a/crates/ui/src/traits.rs +++ b/crates/ui/src/traits.rs @@ -1,6 +1,8 @@ +pub mod animation_ext; pub mod clickable; pub mod disableable; pub mod fixed; pub mod styled_ext; pub mod toggleable; +pub mod transformable; pub mod visible_on_hover; diff --git a/crates/ui/src/traits/animation_ext.rs b/crates/ui/src/traits/animation_ext.rs new file mode 100644 index 0000000000000000000000000000000000000000..4907c71ff2ad29104e3e9a3f408c1c9b69af8d44 --- /dev/null +++ b/crates/ui/src/traits/animation_ext.rs @@ -0,0 +1,42 @@ +use std::time::Duration; + +use gpui::{Animation, AnimationElement, AnimationExt, Transformation, percentage}; + +use crate::{prelude::*, traits::transformable::Transformable}; + +/// An extension trait for adding common animations to animatable components. +pub trait CommonAnimationExt: AnimationExt { + /// Render this component as rotating over the given duration. + /// + /// NOTE: This method uses the location of the caller to generate an ID for this state. + /// If this is not sufficient to identify your state (e.g. you're rendering a list item), + /// you can provide a custom ElementID using the `use_keyed_rotate_animation` method. + #[track_caller] + fn with_rotate_animation(self, duration: u64) -> AnimationElement + where + Self: Transformable + Sized, + { + self.with_keyed_rotate_animation( + ElementId::CodeLocation(*std::panic::Location::caller()), + duration, + ) + } + + /// Render this component as rotating with the given element ID over the given duration. + fn with_keyed_rotate_animation( + self, + id: impl Into, + duration: u64, + ) -> AnimationElement + where + Self: Transformable + Sized, + { + self.with_animation( + id, + Animation::new(Duration::from_secs(duration)).repeat(), + |component, delta| component.transform(Transformation::rotate(percentage(delta))), + ) + } +} + +impl CommonAnimationExt for T {} diff --git a/crates/ui/src/traits/transformable.rs b/crates/ui/src/traits/transformable.rs new file mode 100644 index 0000000000000000000000000000000000000000..f52141f304d51807e45f97d5091f5a5087467679 --- /dev/null +++ b/crates/ui/src/traits/transformable.rs @@ -0,0 +1,7 @@ +use gpui::Transformation; + +/// A trait for components that can be transformed. +pub trait Transformable { + /// Sets the transformation for the element. + fn transform(self, transformation: Transformation) -> Self; +} diff --git a/crates/ui/src/ui.rs b/crates/ui/src/ui.rs index dadc5ecdd12d6c3b7e3431977f1606d56c456cfa..17e707f11b3a43392bff755f1ba7904b61b02f92 100644 --- a/crates/ui/src/ui.rs +++ b/crates/ui/src/ui.rs @@ -17,3 +17,4 @@ pub mod utils; pub use components::*; pub use prelude::*; pub use styles::*; +pub use traits::animation_ext::*; diff --git a/crates/zed/src/zed/quick_action_bar/repl_menu.rs b/crates/zed/src/zed/quick_action_bar/repl_menu.rs index eaa989f88dc9e3e3e969841f02fa334a8f6f594e..82eb82de1e2807346eb3ade2ced8a7946413f0a4 100644 --- a/crates/zed/src/zed/quick_action_bar/repl_menu.rs +++ b/crates/zed/src/zed/quick_action_bar/repl_menu.rs @@ -1,7 +1,5 @@ -use std::time::Duration; - use gpui::ElementId; -use gpui::{Animation, AnimationExt, AnyElement, Entity, Transformation, percentage}; +use gpui::{AnyElement, Entity}; use picker::Picker; use repl::{ ExecutionState, JupyterSettings, Kernel, KernelSpecification, KernelStatus, Session, @@ -10,8 +8,8 @@ use repl::{ worktree_id_for_editor, }; use ui::{ - ButtonLike, ContextMenu, IconWithIndicator, Indicator, IntoElement, PopoverMenu, - PopoverMenuHandle, Tooltip, prelude::*, + ButtonLike, CommonAnimationExt, ContextMenu, IconWithIndicator, Indicator, IntoElement, + PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*, }; use util::ResultExt; @@ -224,11 +222,7 @@ impl QuickActionBar { .child(if menu_state.icon_is_animating { Icon::new(menu_state.icon) .color(menu_state.icon_color) - .with_animation( - "arrow-circle", - Animation::new(Duration::from_secs(5)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ) + .with_rotate_animation(5) .into_any_element() } else { IconWithIndicator::new(