Simplify constructing tab content that is purely textual (#14695)

This PR adds a streamlined way to consistently construct tab content for
items that only have textual content in the tabs.

The `Item` trait now has a new `tab_content_text` method that can be
used to return the textual content for the tab.

The `tab_content` method now has a default implementation that—unless
overridden—will construct a `Label` out of the text. This default
implementation also takes care of setting the label color based on the
active state of the tab, something that previously had to be repeated in
each `tab_content` implementation.

The majority of our tabs are now using `tab_content_text`.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2024-07-17 20:11:05 -04:00 committed by GitHub
parent ca2976559e
commit 2c8ead4423
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 77 additions and 124 deletions

View File

@ -2023,18 +2023,8 @@ impl FocusableView for ContextEditor {
impl Item for ContextEditor {
type Event = editor::EditorEvent;
fn tab_content(&self, params: item::TabContentParams, cx: &WindowContext) -> AnyElement {
let color = if params.selected {
Color::Default
} else {
Color::Muted
};
Label::new(util::truncate_and_trailoff(
&self.title(cx),
Self::MAX_TAB_TITLE_LEN,
))
.color(color)
.into_any_element()
fn tab_content_text(&self, cx: &WindowContext) -> Option<SharedString> {
Some(util::truncate_and_trailoff(&self.title(cx), Self::MAX_TAB_TITLE_LEN).into())
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(item::ItemEvent)) {
@ -2531,13 +2521,8 @@ impl EventEmitter<()> for ContextHistory {}
impl Item for ContextHistory {
type Event = ();
fn tab_content(&self, params: item::TabContentParams, _: &WindowContext) -> AnyElement {
let color = if params.selected {
Color::Default
} else {
Color::Muted
};
Label::new("History").color(color).into_any_element()
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some("History".into())
}
}

View File

@ -11,20 +11,20 @@ use editor::{
EditorEvent,
};
use gpui::{
actions, AnyElement, AnyView, AppContext, ClipboardItem, Entity as _, EventEmitter,
FocusableView, IntoElement as _, Model, Pixels, Point, Render, Subscription, Task, View,
ViewContext, VisualContext as _, WeakView, WindowContext,
actions, AnyView, AppContext, ClipboardItem, Entity as _, EventEmitter, FocusableView, Model,
Pixels, Point, Render, Subscription, Task, View, ViewContext, VisualContext as _, WeakView,
WindowContext,
};
use project::Project;
use std::{
any::{Any, TypeId},
sync::Arc,
};
use ui::{prelude::*, Label};
use ui::prelude::*;
use util::ResultExt;
use workspace::{item::Dedup, notifications::NotificationId};
use workspace::{
item::{FollowableItem, Item, ItemEvent, ItemHandle, TabContentParams},
item::{FollowableItem, Item, ItemEvent, ItemHandle},
searchable::SearchableItemHandle,
ItemNavHistory, Pane, SaveIntent, Toast, ViewId, Workspace, WorkspaceId,
};
@ -385,7 +385,7 @@ impl Item for ChannelView {
}
}
fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
fn tab_content_text(&self, cx: &WindowContext) -> Option<SharedString> {
let label = if let Some(channel) = self.channel(cx) {
match (
self.channel_buffer.read(cx).buffer().read(cx).read_only(),
@ -398,13 +398,7 @@ impl Item for ChannelView {
} else {
"channel notes (disconnected)".to_string()
};
Label::new(label)
.color(if params.selected {
Color::Default
} else {
Color::Muted
})
.into_any_element()
Some(label.into())
}
fn telemetry_event_text(&self) -> Option<&'static str> {

View File

@ -14,7 +14,7 @@ use editor::{Editor, EditorElement, EditorStyle};
use extension::{ExtensionManifest, ExtensionOperation, ExtensionStore};
use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{
actions, uniform_list, AnyElement, AppContext, EventEmitter, Flatten, FocusableView, FontStyle,
actions, uniform_list, AppContext, EventEmitter, Flatten, FocusableView, FontStyle,
InteractiveElement, KeyContext, ParentElement, Render, Styled, Task, TextStyle,
UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
};
@ -25,7 +25,6 @@ use settings::Settings;
use theme::ThemeSettings;
use ui::{prelude::*, CheckboxWithLabel, ContextMenu, PopoverMenu, ToggleButton, Tooltip};
use vim::VimModeSetting;
use workspace::item::TabContentParams;
use workspace::{
item::{Item, ItemEvent},
Workspace, WorkspaceId,
@ -1135,14 +1134,8 @@ impl FocusableView for ExtensionsPage {
impl Item for ExtensionsPage {
type Event = ItemEvent;
fn tab_content(&self, params: TabContentParams, _: &WindowContext) -> AnyElement {
Label::new("Extensions")
.color(if params.selected {
Color::Default
} else {
Color::Muted
})
.into_any_element()
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some("Extensions".into())
}
fn telemetry_event_text(&self) -> Option<&'static str> {

View File

@ -3,9 +3,9 @@ use copilot::Copilot;
use editor::{actions::MoveToEnd, Editor, EditorEvent};
use futures::{channel::mpsc, StreamExt};
use gpui::{
actions, div, AnchorCorner, AnyElement, AppContext, Context, EventEmitter, FocusHandle,
FocusableView, IntoElement, Model, ModelContext, ParentElement, Render, Styled, Subscription,
View, ViewContext, VisualContext, WeakModel, WindowContext,
actions, div, AnchorCorner, AppContext, Context, EventEmitter, FocusHandle, FocusableView,
IntoElement, Model, ModelContext, ParentElement, Render, Styled, Subscription, View,
ViewContext, VisualContext, WeakModel, WindowContext,
};
use language::{LanguageServerId, LanguageServerName};
use lsp::{IoKind, LanguageServer};
@ -13,7 +13,7 @@ use project::{search::SearchQuery, Project};
use std::{borrow::Cow, sync::Arc};
use ui::{prelude::*, Button, Checkbox, ContextMenu, Label, PopoverMenu, Selection};
use workspace::{
item::{Item, ItemHandle, TabContentParams},
item::{Item, ItemHandle},
searchable::{SearchEvent, SearchableItem, SearchableItemHandle},
ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
};
@ -697,14 +697,8 @@ impl Item for LspLogView {
Editor::to_item_events(event, f)
}
fn tab_content(&self, params: TabContentParams, _: &WindowContext<'_>) -> AnyElement {
Label::new("LSP Logs")
.color(if params.selected {
Color::Default
} else {
Color::Muted
})
.into_any_element()
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some("LSP Logs".into())
}
fn telemetry_event_text(&self) -> Option<&'static str> {

View File

@ -1,8 +1,8 @@
use editor::{scroll::Autoscroll, Anchor, Editor, ExcerptId};
use gpui::{
actions, div, rems, uniform_list, AnyElement, AppContext, Div, EventEmitter, FocusHandle,
FocusableView, Hsla, InteractiveElement, IntoElement, Model, MouseButton, MouseDownEvent,
MouseMoveEvent, ParentElement, Render, Styled, UniformListScrollHandle, View, ViewContext,
actions, div, rems, uniform_list, AppContext, Div, EventEmitter, FocusHandle, FocusableView,
Hsla, InteractiveElement, IntoElement, Model, MouseButton, MouseDownEvent, MouseMoveEvent,
ParentElement, Render, SharedString, Styled, UniformListScrollHandle, View, ViewContext,
VisualContext, WeakView, WindowContext,
};
use language::{Buffer, OwnedSyntaxLayer};
@ -11,7 +11,7 @@ use theme::ActiveTheme;
use tree_sitter::{Node, TreeCursor};
use ui::{h_flex, ButtonLike, Color, ContextMenu, Label, LabelCommon, PopoverMenu};
use workspace::{
item::{Item, ItemHandle, TabContentParams},
item::{Item, ItemHandle},
SplitDirection, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
};
@ -380,14 +380,8 @@ impl Item for SyntaxTreeView {
fn to_item_events(_: &Self::Event, _: impl FnMut(workspace::item::ItemEvent)) {}
fn tab_content(&self, params: TabContentParams, _: &WindowContext<'_>) -> AnyElement {
Label::new("Syntax Tree")
.color(if params.selected {
Color::Default
} else {
Color::Muted
})
.into_any_element()
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some("Syntax Tree".into())
}
fn telemetry_event_text(&self) -> Option<&'static str> {

View File

@ -6,13 +6,13 @@ use anyhow::Result;
use editor::scroll::{Autoscroll, AutoscrollStrategy};
use editor::{Editor, EditorEvent};
use gpui::{
list, AnyElement, AppContext, ClickEvent, EventEmitter, FocusHandle, FocusableView,
InteractiveElement, IntoElement, ListState, ParentElement, Render, Styled, Subscription, Task,
View, ViewContext, WeakView,
list, AppContext, ClickEvent, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
IntoElement, ListState, ParentElement, Render, Styled, Subscription, Task, View, ViewContext,
WeakView,
};
use language::LanguageRegistry;
use ui::prelude::*;
use workspace::item::{Item, ItemHandle, TabContentParams};
use workspace::item::{Item, ItemHandle};
use workspace::{Pane, Workspace};
use crate::markdown_elements::ParsedMarkdownElement;
@ -456,18 +456,12 @@ impl Item for MarkdownPreviewView {
Some(Icon::new(IconName::FileDoc))
}
fn tab_content(&self, params: TabContentParams, _cx: &WindowContext) -> AnyElement {
Label::new(if let Some(description) = &self.tab_description {
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some(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> {

View File

@ -35,7 +35,7 @@ use ui::{
};
use util::paths::PathMatcher;
use workspace::{
item::{BreadcrumbText, Item, ItemEvent, ItemHandle, TabContentParams},
item::{BreadcrumbText, Item, ItemEvent, ItemHandle},
searchable::{Direction, SearchableItem, SearchableItemHandle},
DeploySearch, ItemNavHistory, NewSearch, ToolbarItemEvent, ToolbarItemLocation,
ToolbarItemView, Workspace, WorkspaceId,
@ -374,7 +374,7 @@ impl Item for ProjectSearchView {
Some(Icon::new(IconName::MagnifyingGlass))
}
fn tab_content(&self, params: TabContentParams, cx: &WindowContext<'_>) -> AnyElement {
fn tab_content_text(&self, cx: &WindowContext) -> Option<SharedString> {
let last_query: Option<SharedString> = self
.model
.read(cx)
@ -385,16 +385,11 @@ impl Item for ProjectSearchView {
let query_text = util::truncate_and_trailoff(&query, MAX_TAB_TITLE_LEN);
query_text.into()
});
let tab_name = last_query
.filter(|query| !query.is_empty())
.unwrap_or_else(|| "Project Search".into());
Label::new(tab_name)
.color(if params.selected {
Color::Default
} else {
Color::Muted
})
.into_any_element()
Some(
last_query
.filter(|query| !query.is_empty())
.unwrap_or_else(|| "Project Search".into()),
)
}
fn telemetry_event_text(&self) -> Option<&'static str> {

View File

@ -9,7 +9,7 @@ use settings::Settings;
use std::{path::Path, sync::Arc};
use theme::ThemeSettings;
use ui::prelude::*;
use workspace::item::{Item, TabContentParams};
use workspace::item::Item;
pub struct ProjectIndexDebugView {
index: Model<ProjectIndex>,
@ -271,14 +271,8 @@ impl EventEmitter<()> for ProjectIndexDebugView {}
impl Item for ProjectIndexDebugView {
type Event = ();
fn tab_content(&self, params: TabContentParams, _: &WindowContext<'_>) -> AnyElement {
Label::new("Project Index (Debug)")
.color(if params.selected {
Color::Default
} else {
Color::Muted
})
.into_any_element()
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some("Project Index (Debug)".into())
}
fn clone_on_split(

View File

@ -5,9 +5,9 @@ mod multibuffer_hint;
use client::{telemetry::Telemetry, TelemetrySettings};
use db::kvp::KEY_VALUE_STORE;
use gpui::{
actions, svg, AnyElement, AppContext, EventEmitter, FocusHandle, FocusableView,
InteractiveElement, ParentElement, Render, Styled, Subscription, Task, View, ViewContext,
VisualContext, WeakView, WindowContext,
actions, svg, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
ParentElement, Render, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView,
WindowContext,
};
use settings::{Settings, SettingsStore};
use std::sync::Arc;
@ -15,7 +15,7 @@ use ui::{prelude::*, CheckboxWithLabel};
use vim::VimModeSetting;
use workspace::{
dock::DockPosition,
item::{Item, ItemEvent, TabContentParams},
item::{Item, ItemEvent},
open_new, AppState, Welcome, Workspace, WorkspaceId,
};
@ -303,14 +303,8 @@ impl FocusableView for WelcomePage {
impl Item for WelcomePage {
type Event = ItemEvent;
fn tab_content(&self, params: TabContentParams, _: &WindowContext) -> AnyElement {
Label::new("Welcome to Zed!")
.color(if params.selected {
Color::Default
} else {
Color::Muted
})
.into_any_element()
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some("Welcome to Zed!".into())
}
fn telemetry_event_text(&self) -> Option<&'static str> {

View File

@ -31,7 +31,7 @@ use std::{
time::Duration,
};
use theme::Theme;
use ui::{Element as _, Icon};
use ui::{Color, Element as _, Icon, IntoElement, Label, LabelCommon};
use util::ResultExt;
pub const LEADER_UPDATE_THROTTLE: Duration = Duration::from_millis(200);
@ -144,8 +144,30 @@ pub struct TabContentParams {
pub trait Item: FocusableView + EventEmitter<Self::Event> {
type Event;
fn tab_content(&self, _params: TabContentParams, _cx: &WindowContext) -> AnyElement {
gpui::Empty.into_any()
/// Returns the tab contents.
///
/// By default this returns a [`Label`] that displays that text from
/// `tab_content_text`.
fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
let Some(text) = self.tab_content_text(cx) else {
return gpui::Empty.into_any();
};
let color = if params.selected {
Color::Default
} else {
Color::Muted
};
Label::new(text).color(color).into_any_element()
}
/// Returns the textual contents of the tab.
///
/// Use this if you don't need to customize the tab contents.
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
None
}
fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {

View File

@ -1,5 +1,5 @@
use crate::{
item::{Item, ItemEvent, TabContentParams},
item::{Item, ItemEvent},
ItemNavHistory, WorkspaceId,
};
use anyhow::Result;
@ -12,7 +12,7 @@ use gpui::{
WindowContext,
};
use std::sync::{Arc, Weak};
use ui::{prelude::*, Icon, IconName, Label};
use ui::{prelude::*, Icon, IconName};
pub enum Event {
Close,
@ -97,14 +97,8 @@ impl Item for SharedScreen {
Some(Icon::new(IconName::Screen))
}
fn tab_content(&self, params: TabContentParams, _: &WindowContext<'_>) -> gpui::AnyElement {
Label::new(format!("{}'s screen", self.user.github_login))
.color(if params.selected {
Color::Default
} else {
Color::Muted
})
.into_any_element()
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some(format!("{}'s screen", self.user.github_login).into())
}
fn telemetry_event_text(&self) -> Option<&'static str> {