From 00c3c02f7d6352b2faf1f0922a4519ca3e703eca Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 17 Jul 2024 16:59:41 -0400 Subject: [PATCH] Render other tab icons in the start slot (#14683) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR reworks the rendering for tab icons to allow us to render all of the tab icons—not just file icons—in the tab's start slot. The `Item` trait now has a separate `tab_icon` method that can be used to indicate what icon should be shown for the tab. Release Notes: - N/A --- Cargo.lock | 2 +- crates/editor/Cargo.toml | 1 + crates/editor/src/items.rs | 15 ++++++++ .../src/markdown_preview_view.rs | 35 ++++++++----------- crates/search/src/project_search.rs | 30 +++++++--------- crates/terminal_view/src/terminal_view.rs | 14 ++++---- crates/workspace/Cargo.toml | 1 - crates/workspace/src/item.rs | 12 ++++++- crates/workspace/src/pane.rs | 24 ++++--------- crates/workspace/src/shared_screen.rs | 26 ++++++-------- 10 files changed, 80 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 411e89664f..d1a451075d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3601,6 +3601,7 @@ dependencies = [ "db", "emojis", "env_logger", + "file_icons", "futures 0.3.28", "fuzzy", "git", @@ -13336,7 +13337,6 @@ dependencies = [ "derive_more", "dev_server_projects", "env_logger", - "file_icons", "fs", "futures 0.3.28", "gpui", diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 3dae65a5db..d5c7c2cded 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -37,6 +37,7 @@ collections.workspace = true convert_case = "0.6.0" db.workspace = true emojis.workspace = true +file_icons.workspace = true futures.workspace = true fuzzy.workspace = true git.workspace = true diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 5f545f7a36..a2fe0922ab 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -5,6 +5,7 @@ use crate::{ }; use anyhow::{anyhow, Context as _, Result}; use collections::HashSet; +use file_icons::FileIcons; use futures::future::try_join_all; use git::repository::GitFileStatus; use gpui::{ @@ -590,6 +591,20 @@ impl Item for Editor { Some(path.to_string_lossy().to_string().into()) } + fn tab_icon(&self, cx: &WindowContext) -> Option { + ItemSettings::get_global(cx) + .file_icons + .then(|| { + self.buffer + .read(cx) + .as_singleton() + .and_then(|buffer| buffer.read(cx).project_path(cx)) + .and_then(|path| FileIcons::get_icon(path.path.as_ref(), cx)) + }) + .flatten() + .map(|icon| Icon::from_path(icon)) + } + fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement { let label_color = if ItemSettings::get_global(cx).git_status { self.buffer() diff --git a/crates/markdown_preview/src/markdown_preview_view.rs b/crates/markdown_preview/src/markdown_preview_view.rs index 08851a4173..55f1cc5a5b 100644 --- a/crates/markdown_preview/src/markdown_preview_view.rs +++ b/crates/markdown_preview/src/markdown_preview_view.rs @@ -452,27 +452,22 @@ impl EventEmitter for MarkdownPreviewView {} impl Item for MarkdownPreviewView { type Event = PreviewEvent; + fn tab_icon(&self, _cx: &WindowContext) -> Option { + Some(Icon::new(IconName::FileDoc)) + } + fn tab_content(&self, params: TabContentParams, _cx: &WindowContext) -> AnyElement { - h_flex() - .gap_2() - .child(Icon::new(IconName::FileDoc).color(if params.selected { - Color::Default - } else { - Color::Muted - })) - .child( - Label::new(if let Some(description) = &self.tab_description { - description.clone().into() - } else { - self.fallback_tab_description.clone() - }) - .color(if params.selected { - Color::Default - } else { - Color::Muted - }), - ) - .into_any() + Label::new(if let Some(description) = &self.tab_description { + description.clone().into() + } else { + self.fallback_tab_description.clone() + }) + .color(if params.selected { + Color::Default + } else { + Color::Muted + }) + .into_any_element() } fn telemetry_event_text(&self) -> Option<&'static str> { diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 90fe261ba1..aa4d19eb24 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -12,11 +12,11 @@ use editor::{ MAX_TAB_TITLE_LEN, }; use gpui::{ - actions, div, Action, AnyElement, AnyView, AppContext, Context as _, Element, EntityId, - EventEmitter, FocusHandle, FocusableView, FontStyle, Global, Hsla, InteractiveElement, - IntoElement, KeyContext, Model, ModelContext, ParentElement, Point, Render, SharedString, - Styled, Subscription, Task, TextStyle, UpdateGlobal, View, ViewContext, VisualContext, - WeakModel, WhiteSpace, WindowContext, + actions, div, Action, AnyElement, AnyView, AppContext, Context as _, EntityId, EventEmitter, + FocusHandle, FocusableView, FontStyle, Global, Hsla, InteractiveElement, IntoElement, + KeyContext, Model, ModelContext, ParentElement, Point, Render, SharedString, Styled, + Subscription, Task, TextStyle, UpdateGlobal, View, ViewContext, VisualContext, WeakModel, + WhiteSpace, WindowContext, }; use menu::Confirm; use project::{search::SearchQuery, search_history::SearchHistoryCursor, Project, ProjectPath}; @@ -370,6 +370,10 @@ impl Item for ProjectSearchView { .update(cx, |editor, cx| editor.deactivated(cx)); } + fn tab_icon(&self, _cx: &WindowContext) -> Option { + Some(Icon::new(IconName::MagnifyingGlass)) + } + fn tab_content(&self, params: TabContentParams, cx: &WindowContext<'_>) -> AnyElement { let last_query: Option = self .model @@ -384,21 +388,13 @@ impl Item for ProjectSearchView { let tab_name = last_query .filter(|query| !query.is_empty()) .unwrap_or_else(|| "Project Search".into()); - h_flex() - .gap_2() - .child( - Icon::new(IconName::MagnifyingGlass).color(if params.selected { - Color::Default - } else { - Color::Muted - }), - ) - .child(Label::new(tab_name).color(if params.selected { + Label::new(tab_name) + .color(if params.selected { Color::Default } else { Color::Muted - })) - .into_any() + }) + .into_any_element() } fn telemetry_event_text(&self) -> Option<&'static str> { diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 326bbc243b..2bf633cf2f 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -943,13 +943,13 @@ impl Item for TerminalView { let terminal = self.terminal().read(cx); let title = terminal.title(true); - let (icon, icon_color, rerun_btn) = match terminal.task() { + let (icon, icon_color, rerun_button) = match terminal.task() { Some(terminal_task) => match &terminal_task.status { TaskStatus::Unknown => (IconName::ExclamationTriangle, Color::Warning, None), TaskStatus::Running => (IconName::Play, Color::Disabled, None), TaskStatus::Completed { success } => { let task_id = terminal_task.id.clone(); - let rerun_btn = IconButton::new("rerun-icon", IconName::Rerun) + let rerun_button = IconButton::new("rerun-icon", IconName::Rerun) .icon_size(IconSize::Small) .size(ButtonSize::Compact) .icon_color(Color::Default) @@ -963,9 +963,9 @@ impl Item for TerminalView { }); if *success { - (IconName::Check, Color::Success, Some(rerun_btn)) + (IconName::Check, Color::Success, Some(rerun_button)) } else { - (IconName::XCircle, Color::Error, Some(rerun_btn)) + (IconName::XCircle, Color::Error, Some(rerun_button)) } } }, @@ -980,17 +980,17 @@ impl Item for TerminalView { .group("term-tab-icon") .child( div() - .when(rerun_btn.is_some(), |this| { + .when(rerun_button.is_some(), |this| { this.hover(|style| style.invisible().w_0()) }) .child(Icon::new(icon).color(icon_color)), ) - .when_some(rerun_btn, |this, rerun_btn| { + .when_some(rerun_button, |this, rerun_button| { this.child( div() .absolute() .visible_on_hover("term-tab-icon") - .child(rerun_btn), + .child(rerun_button), ) }), ) diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index e04449f14b..2b178d50dd 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -36,7 +36,6 @@ clock.workspace = true collections.workspace = true db.workspace = true derive_more.workspace = true -file_icons.workspace = true fs.workspace = true futures.workspace = true gpui.workspace = true diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 74d8892e86..d898d775c3 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -31,7 +31,7 @@ use std::{ time::Duration, }; use theme::Theme; -use ui::Element as _; +use ui::{Element as _, Icon}; use util::ResultExt; pub const LEADER_UPDATE_THROTTLE: Duration = Duration::from_millis(200); @@ -147,6 +147,11 @@ pub trait Item: FocusableView + EventEmitter { fn tab_content(&self, _params: TabContentParams, _cx: &WindowContext) -> AnyElement { gpui::Empty.into_any() } + + fn tab_icon(&self, _cx: &WindowContext) -> Option { + None + } + fn to_item_events(_event: &Self::Event, _f: impl FnMut(ItemEvent)) {} fn deactivated(&mut self, _: &mut ViewContext) {} @@ -330,6 +335,7 @@ pub trait ItemHandle: 'static + Send { fn tab_tooltip_text(&self, cx: &AppContext) -> Option; fn tab_description(&self, detail: usize, cx: &AppContext) -> Option; fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement; + fn tab_icon(&self, cx: &WindowContext) -> Option; fn telemetry_event_text(&self, cx: &WindowContext) -> Option<&'static str>; fn dragged_tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement; fn project_path(&self, cx: &AppContext) -> Option; @@ -441,6 +447,10 @@ impl ItemHandle for View { self.read(cx).tab_content(params, cx) } + fn tab_icon(&self, cx: &WindowContext) -> Option { + self.read(cx).tab_icon(cx) + } + fn dragged_tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement { self.read(cx).tab_content( TabContentParams { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 4c9faaa1ec..979aa2c9bd 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -10,7 +10,6 @@ use crate::{ }; use anyhow::Result; use collections::{BTreeSet, HashMap, HashSet, VecDeque}; -use file_icons::FileIcons; use futures::{stream::FuturesUnordered, StreamExt}; use gpui::{ actions, anchored, deferred, impl_actions, prelude::*, Action, AnchorCorner, AnyElement, @@ -1590,6 +1589,7 @@ impl Pane { }, cx, ); + let icon = item.tab_icon(cx); let close_side = &ItemSettings::get_global(cx).close_position; let indicator = render_item_indicator(item.boxed_clone(), cx); let item_id = item.item_id(); @@ -1597,14 +1597,6 @@ impl Pane { let is_last_item = ix == self.items.len() - 1; let position_relative_to_active_item = ix.cmp(&self.active_item_index); - let file_icon = ItemSettings::get_global(cx) - .file_icons - .then(|| { - item.project_path(cx) - .and_then(|path| FileIcons::get_icon(path.path.as_ref(), cx)) - }) - .flatten(); - let tab = Tab::new(ix) .position(if is_first_item { TabPosition::First @@ -1675,14 +1667,12 @@ impl Pane { }) .map(|tab| match indicator { Some(indicator) => tab.start_slot(indicator), - None => tab.start_slot::(file_icon.map(|icon| { - Icon::from_path(icon.to_string()) - .size(IconSize::XSmall) - .color(if is_active { - Color::Default - } else { - Color::Muted - }) + None => tab.start_slot::(icon.map(|icon| { + icon.size(IconSize::XSmall).color(if is_active { + Color::Default + } else { + Color::Muted + }) })), }) .end_slot( diff --git a/crates/workspace/src/shared_screen.rs b/crates/workspace/src/shared_screen.rs index 305d3e0aa0..6784a75fb5 100644 --- a/crates/workspace/src/shared_screen.rs +++ b/crates/workspace/src/shared_screen.rs @@ -7,12 +7,12 @@ use call::participant::{Frame, RemoteVideoTrack}; use client::{proto::PeerId, User}; use futures::StreamExt; use gpui::{ - div, img, AppContext, Element, EventEmitter, FocusHandle, FocusableView, InteractiveElement, + div, img, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement, ParentElement, Render, SharedString, Styled, Task, View, ViewContext, VisualContext, WindowContext, }; use std::sync::{Arc, Weak}; -use ui::{h_flex, prelude::*, Icon, IconName, Label}; +use ui::{prelude::*, Icon, IconName, Label}; pub enum Event { Close, @@ -93,24 +93,18 @@ impl Item for SharedScreen { } } + fn tab_icon(&self, _cx: &WindowContext) -> Option { + Some(Icon::new(IconName::Screen)) + } + fn tab_content(&self, params: TabContentParams, _: &WindowContext<'_>) -> gpui::AnyElement { - h_flex() - .gap_1() - .child(Icon::new(IconName::Screen).color(if params.selected { + Label::new(format!("{}'s screen", self.user.github_login)) + .color(if params.selected { Color::Default } else { Color::Muted - })) - .child( - Label::new(format!("{}'s screen", self.user.github_login)).color( - if params.selected { - Color::Default - } else { - Color::Muted - }, - ), - ) - .into_any() + }) + .into_any_element() } fn telemetry_event_text(&self) -> Option<&'static str> {