mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-16 17:07:14 +03:00
Extract title_bar
crate (#13597)
This PR extracts a singular title bar (`title_bar::TitleBar`) from `ui::TitleBar` and `collab_ui::collab_titlebar_item::CollabTitlebarItem`. This is a first step towards organizing title bar things into one place, and standardizing platform titlebar/window control implementations. Release Notes: - N/A
This commit is contained in:
parent
7652a8ae23
commit
0b57df5deb
44
Cargo.lock
generated
44
Cargo.lock
generated
@ -2551,18 +2551,13 @@ name = "collab_ui"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"auto_update",
|
|
||||||
"call",
|
"call",
|
||||||
"channel",
|
"channel",
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
"command_palette",
|
|
||||||
"db",
|
"db",
|
||||||
"dev_server_projects",
|
|
||||||
"editor",
|
"editor",
|
||||||
"emojis",
|
"emojis",
|
||||||
"extensions_ui",
|
|
||||||
"feedback",
|
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui",
|
"gpui",
|
||||||
@ -2575,7 +2570,6 @@ dependencies = [
|
|||||||
"picker",
|
"picker",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"project",
|
"project",
|
||||||
"recent_projects",
|
|
||||||
"release_channel",
|
"release_channel",
|
||||||
"rich_text",
|
"rich_text",
|
||||||
"rpc",
|
"rpc",
|
||||||
@ -2587,15 +2581,14 @@ dependencies = [
|
|||||||
"smallvec",
|
"smallvec",
|
||||||
"story",
|
"story",
|
||||||
"theme",
|
"theme",
|
||||||
"theme_selector",
|
|
||||||
"time",
|
"time",
|
||||||
"time_format",
|
"time_format",
|
||||||
|
"title_bar",
|
||||||
"tree-sitter-markdown",
|
"tree-sitter-markdown",
|
||||||
"ui",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"vcs_menu",
|
"vcs_menu",
|
||||||
"workspace",
|
"workspace",
|
||||||
"zed_actions",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -11046,6 +11039,41 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "title_bar"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"auto_update",
|
||||||
|
"call",
|
||||||
|
"client",
|
||||||
|
"collections",
|
||||||
|
"command_palette",
|
||||||
|
"dev_server_projects",
|
||||||
|
"editor",
|
||||||
|
"extensions_ui",
|
||||||
|
"feedback",
|
||||||
|
"gpui",
|
||||||
|
"http 0.1.0",
|
||||||
|
"notifications",
|
||||||
|
"pretty_assertions",
|
||||||
|
"project",
|
||||||
|
"recent_projects",
|
||||||
|
"rpc",
|
||||||
|
"serde",
|
||||||
|
"settings",
|
||||||
|
"smallvec",
|
||||||
|
"story",
|
||||||
|
"theme",
|
||||||
|
"theme_selector",
|
||||||
|
"tree-sitter-markdown",
|
||||||
|
"ui",
|
||||||
|
"util",
|
||||||
|
"vcs_menu",
|
||||||
|
"windows 0.57.0",
|
||||||
|
"workspace",
|
||||||
|
"zed_actions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.37.0"
|
version = "1.37.0"
|
||||||
|
30
Cargo.toml
30
Cargo.toml
@ -21,6 +21,7 @@ members = [
|
|||||||
"crates/command_palette_hooks",
|
"crates/command_palette_hooks",
|
||||||
"crates/copilot",
|
"crates/copilot",
|
||||||
"crates/db",
|
"crates/db",
|
||||||
|
"crates/dev_server_projects",
|
||||||
"crates/diagnostics",
|
"crates/diagnostics",
|
||||||
"crates/editor",
|
"crates/editor",
|
||||||
"crates/extension",
|
"crates/extension",
|
||||||
@ -77,14 +78,11 @@ members = [
|
|||||||
"crates/refineable",
|
"crates/refineable",
|
||||||
"crates/refineable/derive_refineable",
|
"crates/refineable/derive_refineable",
|
||||||
"crates/release_channel",
|
"crates/release_channel",
|
||||||
"crates/dev_server_projects",
|
|
||||||
"crates/repl",
|
"crates/repl",
|
||||||
"crates/rich_text",
|
"crates/rich_text",
|
||||||
"crates/rope",
|
"crates/rope",
|
||||||
"crates/rpc",
|
"crates/rpc",
|
||||||
"crates/rustdoc",
|
"crates/rustdoc",
|
||||||
"crates/task",
|
|
||||||
"crates/tasks_ui",
|
|
||||||
"crates/search",
|
"crates/search",
|
||||||
"crates/semantic_index",
|
"crates/semantic_index",
|
||||||
"crates/semantic_version",
|
"crates/semantic_version",
|
||||||
@ -95,17 +93,20 @@ members = [
|
|||||||
"crates/story",
|
"crates/story",
|
||||||
"crates/storybook",
|
"crates/storybook",
|
||||||
"crates/sum_tree",
|
"crates/sum_tree",
|
||||||
"crates/tab_switcher",
|
|
||||||
"crates/supermaven",
|
"crates/supermaven",
|
||||||
"crates/supermaven_api",
|
"crates/supermaven_api",
|
||||||
|
"crates/tab_switcher",
|
||||||
|
"crates/task",
|
||||||
|
"crates/tasks_ui",
|
||||||
|
"crates/telemetry_events",
|
||||||
"crates/terminal",
|
"crates/terminal",
|
||||||
"crates/terminal_view",
|
"crates/terminal_view",
|
||||||
"crates/text",
|
"crates/text",
|
||||||
"crates/theme",
|
"crates/theme",
|
||||||
"crates/theme_importer",
|
"crates/theme_importer",
|
||||||
"crates/theme_selector",
|
"crates/theme_selector",
|
||||||
"crates/telemetry_events",
|
|
||||||
"crates/time_format",
|
"crates/time_format",
|
||||||
|
"crates/title_bar",
|
||||||
"crates/ui",
|
"crates/ui",
|
||||||
"crates/ui_text_field",
|
"crates/ui_text_field",
|
||||||
"crates/util",
|
"crates/util",
|
||||||
@ -175,6 +176,7 @@ command_palette_hooks = { path = "crates/command_palette_hooks" }
|
|||||||
copilot = { path = "crates/copilot" }
|
copilot = { path = "crates/copilot" }
|
||||||
dashmap = "5.5.3"
|
dashmap = "5.5.3"
|
||||||
db = { path = "crates/db" }
|
db = { path = "crates/db" }
|
||||||
|
dev_server_projects = { path = "crates/dev_server_projects" }
|
||||||
diagnostics = { path = "crates/diagnostics" }
|
diagnostics = { path = "crates/diagnostics" }
|
||||||
editor = { path = "crates/editor" }
|
editor = { path = "crates/editor" }
|
||||||
extension = { path = "crates/extension" }
|
extension = { path = "crates/extension" }
|
||||||
@ -195,9 +197,9 @@ gpui_macros = { path = "crates/gpui_macros" }
|
|||||||
headless = { path = "crates/headless" }
|
headless = { path = "crates/headless" }
|
||||||
html_to_markdown = { path = "crates/html_to_markdown" }
|
html_to_markdown = { path = "crates/html_to_markdown" }
|
||||||
http = { path = "crates/http" }
|
http = { path = "crates/http" }
|
||||||
install_cli = { path = "crates/install_cli" }
|
|
||||||
image_viewer = { path = "crates/image_viewer" }
|
image_viewer = { path = "crates/image_viewer" }
|
||||||
inline_completion_button = { path = "crates/inline_completion_button" }
|
inline_completion_button = { path = "crates/inline_completion_button" }
|
||||||
|
install_cli = { path = "crates/install_cli" }
|
||||||
journal = { path = "crates/journal" }
|
journal = { path = "crates/journal" }
|
||||||
language = { path = "crates/language" }
|
language = { path = "crates/language" }
|
||||||
language_selector = { path = "crates/language_selector" }
|
language_selector = { path = "crates/language_selector" }
|
||||||
@ -223,21 +225,17 @@ plugin = { path = "crates/plugin" }
|
|||||||
plugin_macros = { path = "crates/plugin_macros" }
|
plugin_macros = { path = "crates/plugin_macros" }
|
||||||
prettier = { path = "crates/prettier" }
|
prettier = { path = "crates/prettier" }
|
||||||
project = { path = "crates/project" }
|
project = { path = "crates/project" }
|
||||||
proto = { path = "crates/proto" }
|
|
||||||
worktree = { path = "crates/worktree" }
|
|
||||||
project_panel = { path = "crates/project_panel" }
|
project_panel = { path = "crates/project_panel" }
|
||||||
project_symbols = { path = "crates/project_symbols" }
|
project_symbols = { path = "crates/project_symbols" }
|
||||||
|
proto = { path = "crates/proto" }
|
||||||
quick_action_bar = { path = "crates/quick_action_bar" }
|
quick_action_bar = { path = "crates/quick_action_bar" }
|
||||||
recent_projects = { path = "crates/recent_projects" }
|
recent_projects = { path = "crates/recent_projects" }
|
||||||
release_channel = { path = "crates/release_channel" }
|
release_channel = { path = "crates/release_channel" }
|
||||||
dev_server_projects = { path = "crates/dev_server_projects" }
|
|
||||||
repl = { path = "crates/repl" }
|
repl = { path = "crates/repl" }
|
||||||
rich_text = { path = "crates/rich_text" }
|
rich_text = { path = "crates/rich_text" }
|
||||||
rope = { path = "crates/rope" }
|
rope = { path = "crates/rope" }
|
||||||
rpc = { path = "crates/rpc" }
|
rpc = { path = "crates/rpc" }
|
||||||
rustdoc = { path = "crates/rustdoc" }
|
rustdoc = { path = "crates/rustdoc" }
|
||||||
task = { path = "crates/task" }
|
|
||||||
tasks_ui = { path = "crates/tasks_ui" }
|
|
||||||
search = { path = "crates/search" }
|
search = { path = "crates/search" }
|
||||||
semantic_index = { path = "crates/semantic_index" }
|
semantic_index = { path = "crates/semantic_index" }
|
||||||
semantic_version = { path = "crates/semantic_version" }
|
semantic_version = { path = "crates/semantic_version" }
|
||||||
@ -245,20 +243,23 @@ settings = { path = "crates/settings" }
|
|||||||
snippet = { path = "crates/snippet" }
|
snippet = { path = "crates/snippet" }
|
||||||
sqlez = { path = "crates/sqlez" }
|
sqlez = { path = "crates/sqlez" }
|
||||||
sqlez_macros = { path = "crates/sqlez_macros" }
|
sqlez_macros = { path = "crates/sqlez_macros" }
|
||||||
supermaven = { path = "crates/supermaven" }
|
|
||||||
supermaven_api = { path = "crates/supermaven_api" }
|
|
||||||
story = { path = "crates/story" }
|
story = { path = "crates/story" }
|
||||||
storybook = { path = "crates/storybook" }
|
storybook = { path = "crates/storybook" }
|
||||||
sum_tree = { path = "crates/sum_tree" }
|
sum_tree = { path = "crates/sum_tree" }
|
||||||
|
supermaven = { path = "crates/supermaven" }
|
||||||
|
supermaven_api = { path = "crates/supermaven_api" }
|
||||||
tab_switcher = { path = "crates/tab_switcher" }
|
tab_switcher = { path = "crates/tab_switcher" }
|
||||||
|
task = { path = "crates/task" }
|
||||||
|
tasks_ui = { path = "crates/tasks_ui" }
|
||||||
|
telemetry_events = { path = "crates/telemetry_events" }
|
||||||
terminal = { path = "crates/terminal" }
|
terminal = { path = "crates/terminal" }
|
||||||
terminal_view = { path = "crates/terminal_view" }
|
terminal_view = { path = "crates/terminal_view" }
|
||||||
text = { path = "crates/text" }
|
text = { path = "crates/text" }
|
||||||
theme = { path = "crates/theme" }
|
theme = { path = "crates/theme" }
|
||||||
theme_importer = { path = "crates/theme_importer" }
|
theme_importer = { path = "crates/theme_importer" }
|
||||||
theme_selector = { path = "crates/theme_selector" }
|
theme_selector = { path = "crates/theme_selector" }
|
||||||
telemetry_events = { path = "crates/telemetry_events" }
|
|
||||||
time_format = { path = "crates/time_format" }
|
time_format = { path = "crates/time_format" }
|
||||||
|
title_bar = { path = "crates/title_bar" }
|
||||||
ui = { path = "crates/ui" }
|
ui = { path = "crates/ui" }
|
||||||
ui_text_field = { path = "crates/ui_text_field" }
|
ui_text_field = { path = "crates/ui_text_field" }
|
||||||
util = { path = "crates/util" }
|
util = { path = "crates/util" }
|
||||||
@ -266,6 +267,7 @@ vcs_menu = { path = "crates/vcs_menu" }
|
|||||||
vim = { path = "crates/vim" }
|
vim = { path = "crates/vim" }
|
||||||
welcome = { path = "crates/welcome" }
|
welcome = { path = "crates/welcome" }
|
||||||
workspace = { path = "crates/workspace" }
|
workspace = { path = "crates/workspace" }
|
||||||
|
worktree = { path = "crates/worktree" }
|
||||||
zed = { path = "crates/zed" }
|
zed = { path = "crates/zed" }
|
||||||
zed_actions = { path = "crates/zed_actions" }
|
zed_actions = { path = "crates/zed_actions" }
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ use std::{
|
|||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{
|
use ui::{
|
||||||
div, prelude::*, IconButtonShape, ListItem, ListItemSpacing, ParentElement, Render,
|
div, prelude::*, IconButtonShape, ListItem, ListItemSpacing, ParentElement, Render,
|
||||||
SharedString, Styled, TitleBar, Tooltip, ViewContext, VisualContext,
|
SharedString, Styled, Tooltip, ViewContext, VisualContext,
|
||||||
};
|
};
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -751,7 +751,7 @@ impl PromptLibrary {
|
|||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.p(Spacing::Small.rems(cx))
|
.p(Spacing::Small.rems(cx))
|
||||||
.h(TitleBar::height(cx))
|
.h_9()
|
||||||
.w_full()
|
.w_full()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.justify_end()
|
.justify_end()
|
||||||
|
@ -30,17 +30,13 @@ test-support = [
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
auto_update.workspace = true
|
|
||||||
call.workspace = true
|
call.workspace = true
|
||||||
channel.workspace = true
|
channel.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
command_palette.workspace = true
|
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
emojis.workspace = true
|
emojis.workspace = true
|
||||||
extensions_ui.workspace = true
|
|
||||||
feedback.workspace = true
|
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
@ -51,8 +47,6 @@ notifications.workspace = true
|
|||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
picker.workspace = true
|
picker.workspace = true
|
||||||
project.workspace = true
|
project.workspace = true
|
||||||
recent_projects.workspace = true
|
|
||||||
dev_server_projects.workspace = true
|
|
||||||
release_channel.workspace = true
|
release_channel.workspace = true
|
||||||
rich_text.workspace = true
|
rich_text.workspace = true
|
||||||
rpc.workspace = true
|
rpc.workspace = true
|
||||||
@ -64,14 +58,13 @@ settings.workspace = true
|
|||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
story = { workspace = true, optional = true }
|
story = { workspace = true, optional = true }
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
theme_selector.workspace = true
|
|
||||||
time_format.workspace = true
|
time_format.workspace = true
|
||||||
time.workspace = true
|
time.workspace = true
|
||||||
|
title_bar.workspace = true
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
vcs_menu.workspace = true
|
vcs_menu.workspace = true
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
zed_actions.workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
call = { workspace = true, features = ["test-support"] }
|
call = { workspace = true, features = ["test-support"] }
|
||||||
|
@ -2,10 +2,7 @@ mod channel_modal;
|
|||||||
mod contact_finder;
|
mod contact_finder;
|
||||||
|
|
||||||
use self::channel_modal::ChannelModal;
|
use self::channel_modal::ChannelModal;
|
||||||
use crate::{
|
use crate::{channel_view::ChannelView, chat_panel::ChatPanel, CollaborationPanelSettings};
|
||||||
channel_view::ChannelView, chat_panel::ChatPanel, face_pile::FacePile,
|
|
||||||
CollaborationPanelSettings,
|
|
||||||
};
|
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
use channel::{Channel, ChannelEvent, ChannelStore};
|
use channel::{Channel, ChannelEvent, ChannelStore};
|
||||||
use client::{ChannelId, Client, Contact, ProjectId, User, UserStore};
|
use client::{ChannelId, Client, Contact, ProjectId, User, UserStore};
|
||||||
@ -34,7 +31,8 @@ use std::{mem, sync::Arc};
|
|||||||
use theme::{ActiveTheme, ThemeSettings};
|
use theme::{ActiveTheme, ThemeSettings};
|
||||||
use ui::{
|
use ui::{
|
||||||
prelude::*, tooltip_container, Avatar, AvatarAvailabilityIndicator, Button, Color, ContextMenu,
|
prelude::*, tooltip_container, Avatar, AvatarAvailabilityIndicator, Button, Color, ContextMenu,
|
||||||
Icon, IconButton, IconName, IconSize, Indicator, Label, ListHeader, ListItem, Tooltip,
|
Facepile, Icon, IconButton, IconName, IconSize, Indicator, Label, ListHeader, ListItem,
|
||||||
|
Tooltip,
|
||||||
};
|
};
|
||||||
use util::{maybe, ResultExt, TryFutureExt};
|
use util::{maybe, ResultExt, TryFutureExt};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
@ -2542,7 +2540,7 @@ impl CollabPanel {
|
|||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let extra_count = participants.len().saturating_sub(FACEPILE_LIMIT);
|
let extra_count = participants.len().saturating_sub(FACEPILE_LIMIT);
|
||||||
let result = FacePile::new(
|
let result = Facepile::new(
|
||||||
participants
|
participants
|
||||||
.iter()
|
.iter()
|
||||||
.map(|user| Avatar::new(user.avatar_uri.clone()).into_any_element())
|
.map(|user| Avatar::new(user.avatar_uri.clone()).into_any_element())
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
pub mod channel_view;
|
pub mod channel_view;
|
||||||
pub mod chat_panel;
|
pub mod chat_panel;
|
||||||
pub mod collab_panel;
|
pub mod collab_panel;
|
||||||
mod collab_titlebar_item;
|
|
||||||
mod face_pile;
|
|
||||||
pub mod notification_panel;
|
pub mod notification_panel;
|
||||||
pub mod notifications;
|
pub mod notifications;
|
||||||
mod panel_settings;
|
mod panel_settings;
|
||||||
|
|
||||||
use std::{rc::Rc, sync::Arc};
|
use std::{rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use call::{report_call_event_for_room, ActiveCall};
|
|
||||||
pub use collab_panel::CollabPanel;
|
pub use collab_panel::CollabPanel;
|
||||||
pub use collab_titlebar_item::CollabTitlebarItem;
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, point, AppContext, Pixels, PlatformDisplay, Size, Task, WindowBackgroundAppearance,
|
point, AppContext, Pixels, PlatformDisplay, Size, WindowBackgroundAppearance, WindowBounds,
|
||||||
WindowBounds, WindowContext, WindowKind, WindowOptions,
|
WindowKind, WindowOptions,
|
||||||
};
|
};
|
||||||
use panel_settings::MessageEditorSettings;
|
use panel_settings::MessageEditorSettings;
|
||||||
pub use panel_settings::{
|
pub use panel_settings::{
|
||||||
@ -23,12 +19,7 @@ pub use panel_settings::{
|
|||||||
use release_channel::ReleaseChannel;
|
use release_channel::ReleaseChannel;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use ui::px;
|
use ui::px;
|
||||||
use workspace::{notifications::DetachAndPromptErr, AppState};
|
use workspace::AppState;
|
||||||
|
|
||||||
actions!(
|
|
||||||
collab,
|
|
||||||
[ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall]
|
|
||||||
);
|
|
||||||
|
|
||||||
pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
||||||
CollaborationPanelSettings::register(cx);
|
CollaborationPanelSettings::register(cx);
|
||||||
@ -36,63 +27,13 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
|||||||
NotificationPanelSettings::register(cx);
|
NotificationPanelSettings::register(cx);
|
||||||
MessageEditorSettings::register(cx);
|
MessageEditorSettings::register(cx);
|
||||||
|
|
||||||
vcs_menu::init(cx);
|
|
||||||
collab_titlebar_item::init(cx);
|
|
||||||
collab_panel::init(cx);
|
|
||||||
channel_view::init(cx);
|
channel_view::init(cx);
|
||||||
chat_panel::init(cx);
|
chat_panel::init(cx);
|
||||||
|
collab_panel::init(cx);
|
||||||
notification_panel::init(cx);
|
notification_panel::init(cx);
|
||||||
notifications::init(&app_state, cx);
|
notifications::init(&app_state, cx);
|
||||||
}
|
title_bar::init(cx);
|
||||||
|
vcs_menu::init(cx);
|
||||||
pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut WindowContext) {
|
|
||||||
let call = ActiveCall::global(cx).read(cx);
|
|
||||||
if let Some(room) = call.room().cloned() {
|
|
||||||
let client = call.client();
|
|
||||||
let toggle_screen_sharing = room.update(cx, |room, cx| {
|
|
||||||
if room.is_screen_sharing() {
|
|
||||||
report_call_event_for_room(
|
|
||||||
"disable screen share",
|
|
||||||
room.id(),
|
|
||||||
room.channel_id(),
|
|
||||||
&client,
|
|
||||||
);
|
|
||||||
Task::ready(room.unshare_screen(cx))
|
|
||||||
} else {
|
|
||||||
report_call_event_for_room(
|
|
||||||
"enable screen share",
|
|
||||||
room.id(),
|
|
||||||
room.channel_id(),
|
|
||||||
&client,
|
|
||||||
);
|
|
||||||
room.share_screen(cx)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
toggle_screen_sharing.detach_and_prompt_err("Sharing Screen Failed", cx, |e, _| Some(format!("{:?}\n\nPlease check that you have given Zed permissions to record your screen in Settings.", e)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
|
|
||||||
let call = ActiveCall::global(cx).read(cx);
|
|
||||||
if let Some(room) = call.room().cloned() {
|
|
||||||
let client = call.client();
|
|
||||||
room.update(cx, |room, cx| {
|
|
||||||
let operation = if room.is_muted() {
|
|
||||||
"enable microphone"
|
|
||||||
} else {
|
|
||||||
"disable microphone"
|
|
||||||
};
|
|
||||||
report_call_event_for_room(operation, room.id(), room.channel_id(), &client);
|
|
||||||
|
|
||||||
room.toggle_mute(cx)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
|
|
||||||
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
|
|
||||||
room.update(cx, |room, cx| room.toggle_deafen(cx));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notification_window_options(
|
fn notification_window_options(
|
||||||
|
@ -35,7 +35,7 @@ pub enum ComponentStory {
|
|||||||
Tab,
|
Tab,
|
||||||
TabBar,
|
TabBar,
|
||||||
Text,
|
Text,
|
||||||
TitleBar,
|
// TitleBar,
|
||||||
ToggleButton,
|
ToggleButton,
|
||||||
ToolStrip,
|
ToolStrip,
|
||||||
ViewportUnits,
|
ViewportUnits,
|
||||||
@ -69,7 +69,7 @@ impl ComponentStory {
|
|||||||
Self::Text => TextStory::view(cx).into(),
|
Self::Text => TextStory::view(cx).into(),
|
||||||
Self::Tab => cx.new_view(|_| ui::TabStory).into(),
|
Self::Tab => cx.new_view(|_| ui::TabStory).into(),
|
||||||
Self::TabBar => cx.new_view(|_| ui::TabBarStory).into(),
|
Self::TabBar => cx.new_view(|_| ui::TabBarStory).into(),
|
||||||
Self::TitleBar => cx.new_view(|_| ui::TitleBarStory).into(),
|
// Self::TitleBar => cx.new_view(|_| title_bar::TitleBarStory).into(),
|
||||||
Self::ToggleButton => cx.new_view(|_| ui::ToggleButtonStory).into(),
|
Self::ToggleButton => cx.new_view(|_| ui::ToggleButtonStory).into(),
|
||||||
Self::ToolStrip => cx.new_view(|_| ui::ToolStripStory).into(),
|
Self::ToolStrip => cx.new_view(|_| ui::ToolStripStory).into(),
|
||||||
Self::ViewportUnits => cx.new_view(|_| crate::stories::ViewportUnitsStory).into(),
|
Self::ViewportUnits => cx.new_view(|_| crate::stories::ViewportUnitsStory).into(),
|
||||||
|
73
crates/title_bar/Cargo.toml
Normal file
73
crates/title_bar/Cargo.toml
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
[package]
|
||||||
|
name = "title_bar"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/title_bar.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
stories = ["dep:story"]
|
||||||
|
test-support = [
|
||||||
|
"call/test-support",
|
||||||
|
"client/test-support",
|
||||||
|
"collections/test-support",
|
||||||
|
"editor/test-support",
|
||||||
|
"gpui/test-support",
|
||||||
|
"http/test-support",
|
||||||
|
"project/test-support",
|
||||||
|
"settings/test-support",
|
||||||
|
"util/test-support",
|
||||||
|
"workspace/test-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
auto_update.workspace = true
|
||||||
|
call.workspace = true
|
||||||
|
client.workspace = true
|
||||||
|
command_palette.workspace = true
|
||||||
|
dev_server_projects.workspace = true
|
||||||
|
extensions_ui.workspace = true
|
||||||
|
feedback.workspace = true
|
||||||
|
gpui.workspace = true
|
||||||
|
notifications.workspace = true
|
||||||
|
project.workspace = true
|
||||||
|
recent_projects.workspace = true
|
||||||
|
rpc.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
settings.workspace = true
|
||||||
|
smallvec.workspace = true
|
||||||
|
story = { workspace = true, optional = true }
|
||||||
|
theme.workspace = true
|
||||||
|
theme_selector.workspace = true
|
||||||
|
ui.workspace = true
|
||||||
|
util.workspace = true
|
||||||
|
vcs_menu.workspace = true
|
||||||
|
workspace.workspace = true
|
||||||
|
zed_actions.workspace = true
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
windows.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
call = { workspace = true, features = ["test-support"] }
|
||||||
|
client = { workspace = true, features = ["test-support"] }
|
||||||
|
collections = { workspace = true, features = ["test-support"] }
|
||||||
|
editor = { workspace = true, features = ["test-support"] }
|
||||||
|
gpui = { workspace = true, features = ["test-support"] }
|
||||||
|
http = { workspace = true, features = ["test-support"] }
|
||||||
|
notifications = { workspace = true, features = ["test-support"] }
|
||||||
|
pretty_assertions.workspace = true
|
||||||
|
project = { workspace = true, features = ["test-support"] }
|
||||||
|
rpc = { workspace = true, features = ["test-support"] }
|
||||||
|
settings = { workspace = true, features = ["test-support"] }
|
||||||
|
tree-sitter-markdown.workspace = true
|
||||||
|
util = { workspace = true, features = ["test-support"] }
|
||||||
|
workspace = { workspace = true, features = ["test-support"] }
|
1
crates/title_bar/LICENSE-GPL
Symbolic link
1
crates/title_bar/LICENSE-GPL
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../LICENSE-GPL
|
1
crates/title_bar/LICENSE_-GPL
Symbolic link
1
crates/title_bar/LICENSE_-GPL
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../LICENSE_-GPL
|
58
crates/title_bar/src/call_controls.rs
Normal file
58
crates/title_bar/src/call_controls.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use call::{report_call_event_for_room, ActiveCall};
|
||||||
|
use gpui::{actions, AppContext, Task, WindowContext};
|
||||||
|
use workspace::notifications::DetachAndPromptErr;
|
||||||
|
|
||||||
|
actions!(
|
||||||
|
collab,
|
||||||
|
[ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall]
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut WindowContext) {
|
||||||
|
let call = ActiveCall::global(cx).read(cx);
|
||||||
|
if let Some(room) = call.room().cloned() {
|
||||||
|
let client = call.client();
|
||||||
|
let toggle_screen_sharing = room.update(cx, |room, cx| {
|
||||||
|
if room.is_screen_sharing() {
|
||||||
|
report_call_event_for_room(
|
||||||
|
"disable screen share",
|
||||||
|
room.id(),
|
||||||
|
room.channel_id(),
|
||||||
|
&client,
|
||||||
|
);
|
||||||
|
Task::ready(room.unshare_screen(cx))
|
||||||
|
} else {
|
||||||
|
report_call_event_for_room(
|
||||||
|
"enable screen share",
|
||||||
|
room.id(),
|
||||||
|
room.channel_id(),
|
||||||
|
&client,
|
||||||
|
);
|
||||||
|
room.share_screen(cx)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toggle_screen_sharing.detach_and_prompt_err("Sharing Screen Failed", cx, |e, _| Some(format!("{:?}\n\nPlease check that you have given Zed permissions to record your screen in Settings.", e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
|
||||||
|
let call = ActiveCall::global(cx).read(cx);
|
||||||
|
if let Some(room) = call.room().cloned() {
|
||||||
|
let client = call.client();
|
||||||
|
room.update(cx, |room, cx| {
|
||||||
|
let operation = if room.is_muted() {
|
||||||
|
"enable microphone"
|
||||||
|
} else {
|
||||||
|
"disable microphone"
|
||||||
|
};
|
||||||
|
report_call_event_for_room(operation, room.id(), room.channel_id(), &client);
|
||||||
|
|
||||||
|
room.toggle_mute(cx)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
|
||||||
|
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
|
||||||
|
room.update(cx, |room, cx| room.toggle_deafen(cx));
|
||||||
|
}
|
||||||
|
}
|
126
crates/title_bar/src/collab.rs
Normal file
126
crates/title_bar/src/collab.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
use crate::TitleBar;
|
||||||
|
use call::Room;
|
||||||
|
use client::{proto::PeerId, User};
|
||||||
|
use gpui::{canvas, point, Hsla, IntoElement, Path, Styled};
|
||||||
|
use rpc::proto::{self};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use theme::ActiveTheme;
|
||||||
|
use ui::{prelude::*, Avatar, AvatarAudioStatusIndicator, Facepile, Tooltip};
|
||||||
|
|
||||||
|
pub(crate) fn render_color_ribbon(color: Hsla) -> impl Element {
|
||||||
|
canvas(
|
||||||
|
move |_, _| {},
|
||||||
|
move |bounds, _, cx| {
|
||||||
|
let height = bounds.size.height;
|
||||||
|
let horizontal_offset = height;
|
||||||
|
let vertical_offset = px(height.0 / 2.0);
|
||||||
|
let mut path = Path::new(bounds.lower_left());
|
||||||
|
path.curve_to(
|
||||||
|
bounds.origin + point(horizontal_offset, vertical_offset),
|
||||||
|
bounds.origin + point(px(0.0), vertical_offset),
|
||||||
|
);
|
||||||
|
path.line_to(bounds.upper_right() + point(-horizontal_offset, vertical_offset));
|
||||||
|
path.curve_to(
|
||||||
|
bounds.lower_right(),
|
||||||
|
bounds.upper_right() + point(px(0.0), vertical_offset),
|
||||||
|
);
|
||||||
|
path.line_to(bounds.lower_left());
|
||||||
|
cx.paint_path(path, color);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.h_1()
|
||||||
|
.w_full()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TitleBar {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub(crate) fn render_collaborator(
|
||||||
|
&self,
|
||||||
|
user: &Arc<User>,
|
||||||
|
peer_id: PeerId,
|
||||||
|
is_present: bool,
|
||||||
|
is_speaking: bool,
|
||||||
|
is_muted: bool,
|
||||||
|
leader_selection_color: Option<Hsla>,
|
||||||
|
room: &Room,
|
||||||
|
project_id: Option<u64>,
|
||||||
|
current_user: &Arc<User>,
|
||||||
|
cx: &ViewContext<Self>,
|
||||||
|
) -> Option<Div> {
|
||||||
|
if room.role_for_user(user.id) == Some(proto::ChannelRole::Guest) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FACEPILE_LIMIT: usize = 3;
|
||||||
|
let followers = project_id.map_or(&[] as &[_], |id| room.followers_for(peer_id, id));
|
||||||
|
let extra_count = followers.len().saturating_sub(FACEPILE_LIMIT);
|
||||||
|
|
||||||
|
Some(
|
||||||
|
div()
|
||||||
|
.m_0p5()
|
||||||
|
.p_0p5()
|
||||||
|
// When the collaborator is not followed, still draw this wrapper div, but leave
|
||||||
|
// it transparent, so that it does not shift the layout when following.
|
||||||
|
.when_some(leader_selection_color, |div, color| {
|
||||||
|
div.rounded_md().bg(color)
|
||||||
|
})
|
||||||
|
.child(
|
||||||
|
Facepile::empty()
|
||||||
|
.child(
|
||||||
|
Avatar::new(user.avatar_uri.clone())
|
||||||
|
.grayscale(!is_present)
|
||||||
|
.border_color(if is_speaking {
|
||||||
|
cx.theme().status().info
|
||||||
|
} else {
|
||||||
|
// We draw the border in a transparent color rather to avoid
|
||||||
|
// the layout shift that would come with adding/removing the border.
|
||||||
|
gpui::transparent_black()
|
||||||
|
})
|
||||||
|
.when(is_muted, |avatar| {
|
||||||
|
avatar.indicator(
|
||||||
|
AvatarAudioStatusIndicator::new(ui::AudioStatus::Muted)
|
||||||
|
.tooltip({
|
||||||
|
let github_login = user.github_login.clone();
|
||||||
|
move |cx| {
|
||||||
|
Tooltip::text(
|
||||||
|
format!("{} is muted", github_login),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.children(followers.iter().take(FACEPILE_LIMIT).filter_map(
|
||||||
|
|follower_peer_id| {
|
||||||
|
let follower = room
|
||||||
|
.remote_participants()
|
||||||
|
.values()
|
||||||
|
.find_map(|p| {
|
||||||
|
(p.peer_id == *follower_peer_id).then_some(&p.user)
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
(self.client.peer_id() == Some(*follower_peer_id))
|
||||||
|
.then_some(current_user)
|
||||||
|
})?
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
Some(div().mt(-px(4.)).child(
|
||||||
|
Avatar::new(follower.avatar_uri.clone()).size(rems(0.75)),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.children(if extra_count > 0 {
|
||||||
|
Some(
|
||||||
|
div()
|
||||||
|
.ml_1()
|
||||||
|
.child(Label::new(format!("+{extra_count}")))
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
3
crates/title_bar/src/platforms.rs
Normal file
3
crates/title_bar/src/platforms.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod platform_linux;
|
||||||
|
pub mod platform_mac;
|
||||||
|
pub mod platform_windows;
|
@ -1,6 +1,6 @@
|
|||||||
use gpui::{prelude::*, Action, Rgba, WindowAppearance};
|
use gpui::{prelude::*, Action, Rgba, WindowAppearance};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use ui::prelude::*;
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct LinuxWindowControls {
|
pub struct LinuxWindowControls {
|
6
crates/title_bar/src/platforms/platform_mac.rs
Normal file
6
crates/title_bar/src/platforms/platform_mac.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/// Use pixels here instead of a rem-based size because the macOS traffic
|
||||||
|
/// lights are a static size, and don't scale with the rest of the UI.
|
||||||
|
///
|
||||||
|
/// Magic number: There is one extra pixel of padding on the left side due to
|
||||||
|
/// the 1px border around the window on macOS apps.
|
||||||
|
pub const TRAFFIC_LIGHT_PADDING: f32 = 71.;
|
@ -1,6 +1,6 @@
|
|||||||
use gpui::{prelude::*, Rgba, WindowAppearance};
|
use gpui::{prelude::*, Rgba, WindowAppearance};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use ui::prelude::*;
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct WindowsWindowControls {
|
pub struct WindowsWindowControls {
|
@ -1,13 +1,13 @@
|
|||||||
use gpui::{NoAction, Render};
|
use gpui::{NoAction, Render};
|
||||||
use story::{StoryContainer, StoryItem, StorySection};
|
use story::{StoryContainer, StoryItem, StorySection};
|
||||||
|
|
||||||
use crate::{prelude::*, PlatformStyle, TitleBar};
|
use crate::{prelude::*, PlatformStyle, UiTitleBar};
|
||||||
|
|
||||||
pub struct TitleBarStory;
|
pub struct TitleBarStory;
|
||||||
|
|
||||||
impl Render for TitleBarStory {
|
impl Render for TitleBarStory {
|
||||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
fn add_sample_children(titlebar: TitleBar) -> TitleBar {
|
fn add_sample_children(titlebar: UiTitleBar) -> UiTitleBar {
|
||||||
titlebar
|
titlebar
|
||||||
.child(div().size_2().bg(gpui::red()))
|
.child(div().size_2().bg(gpui::red()))
|
||||||
.child(div().size_2().bg(gpui::blue()))
|
.child(div().size_2().bg(gpui::blue()))
|
||||||
@ -19,7 +19,7 @@ impl Render for TitleBarStory {
|
|||||||
StorySection::new().child(
|
StorySection::new().child(
|
||||||
StoryItem::new(
|
StoryItem::new(
|
||||||
"Default (macOS)",
|
"Default (macOS)",
|
||||||
TitleBar::new("macos", Box::new(NoAction))
|
UiTitleBar::new("macos", Box::new(NoAction))
|
||||||
.platform_style(PlatformStyle::Mac)
|
.platform_style(PlatformStyle::Mac)
|
||||||
.map(add_sample_children),
|
.map(add_sample_children),
|
||||||
)
|
)
|
||||||
@ -31,7 +31,7 @@ impl Render for TitleBarStory {
|
|||||||
StorySection::new().child(
|
StorySection::new().child(
|
||||||
StoryItem::new(
|
StoryItem::new(
|
||||||
"Default (Linux)",
|
"Default (Linux)",
|
||||||
TitleBar::new("linux", Box::new(NoAction))
|
UiTitleBar::new("linux", Box::new(NoAction))
|
||||||
.platform_style(PlatformStyle::Linux)
|
.platform_style(PlatformStyle::Linux)
|
||||||
.map(add_sample_children),
|
.map(add_sample_children),
|
||||||
)
|
)
|
||||||
@ -43,7 +43,7 @@ impl Render for TitleBarStory {
|
|||||||
StorySection::new().child(
|
StorySection::new().child(
|
||||||
StoryItem::new(
|
StoryItem::new(
|
||||||
"Default (Windows)",
|
"Default (Windows)",
|
||||||
TitleBar::new("windows", Box::new(NoAction))
|
UiTitleBar::new("windows", Box::new(NoAction))
|
||||||
.platform_style(PlatformStyle::Windows)
|
.platform_style(PlatformStyle::Windows)
|
||||||
.map(add_sample_children),
|
.map(add_sample_children),
|
||||||
)
|
)
|
@ -1,21 +1,27 @@
|
|||||||
use crate::face_pile::FacePile;
|
mod call_controls;
|
||||||
|
mod collab;
|
||||||
|
mod platforms;
|
||||||
|
|
||||||
|
use crate::platforms::{platform_linux, platform_mac, platform_windows};
|
||||||
use auto_update::AutoUpdateStatus;
|
use auto_update::AutoUpdateStatus;
|
||||||
use call::{ActiveCall, ParticipantLocation, Room};
|
use call::{ActiveCall, ParticipantLocation};
|
||||||
use client::{proto::PeerId, Client, User, UserStore};
|
use client::{Client, UserStore};
|
||||||
|
use collab::render_color_ribbon;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, canvas, div, point, px, Action, AnyElement, AppContext, Element, Hsla,
|
actions, div, px, Action, AnyElement, AppContext, Element, InteractiveElement, Interactivity,
|
||||||
InteractiveElement, IntoElement, Model, ParentElement, Path, Render,
|
IntoElement, Model, ParentElement, Render, Stateful, StatefulInteractiveElement, Styled,
|
||||||
StatefulInteractiveElement, Styled, Subscription, ViewContext, VisualContext, WeakView,
|
Subscription, ViewContext, VisualContext, WeakView,
|
||||||
};
|
};
|
||||||
use project::{Project, RepositoryEntry};
|
use project::{Project, RepositoryEntry};
|
||||||
use recent_projects::RecentProjects;
|
use recent_projects::RecentProjects;
|
||||||
use rpc::proto::{self, DevServerStatus};
|
use rpc::proto::DevServerStatus;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme::{ActiveTheme, ThemeSettings};
|
use theme::{ActiveTheme, ThemeSettings};
|
||||||
use ui::{
|
use ui::{
|
||||||
h_flex, prelude::*, Avatar, AvatarAudioStatusIndicator, Button, ButtonLike, ButtonStyle,
|
h_flex, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon, IconButton,
|
||||||
ContextMenu, Icon, IconButton, IconName, Indicator, PopoverMenu, TintColor, TitleBar, Tooltip,
|
IconName, Indicator, PopoverMenu, TintColor, Tooltip,
|
||||||
};
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use vcs_menu::{BranchList, OpenRecent as ToggleVcsMenu};
|
use vcs_menu::{BranchList, OpenRecent as ToggleVcsMenu};
|
||||||
@ -37,13 +43,16 @@ actions!(
|
|||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.observe_new_views(|workspace: &mut Workspace, cx| {
|
cx.observe_new_views(|workspace: &mut Workspace, cx| {
|
||||||
let titlebar_item = cx.new_view(|cx| CollabTitlebarItem::new(workspace, cx));
|
let item = cx.new_view(|cx| TitleBar::new("title-bar", workspace, cx));
|
||||||
workspace.set_titlebar_item(titlebar_item.into(), cx)
|
workspace.set_titlebar_item(item.into(), cx)
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CollabTitlebarItem {
|
pub struct TitleBar {
|
||||||
|
platform_style: PlatformStyle,
|
||||||
|
content: Stateful<Div>,
|
||||||
|
children: SmallVec<[AnyElement; 2]>,
|
||||||
project: Model<Project>,
|
project: Model<Project>,
|
||||||
user_store: Model<UserStore>,
|
user_store: Model<UserStore>,
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
@ -51,17 +60,41 @@ pub struct CollabTitlebarItem {
|
|||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for CollabTitlebarItem {
|
impl Render for TitleBar {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
let room = ActiveCall::global(cx).read(cx).room().cloned();
|
let room = ActiveCall::global(cx).read(cx).room().cloned();
|
||||||
let current_user = self.user_store.read(cx).current_user();
|
let current_user = self.user_store.read(cx).current_user();
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
let project_id = self.project.read(cx).remote_id();
|
let project_id = self.project.read(cx).remote_id();
|
||||||
let workspace = self.workspace.upgrade();
|
let workspace = self.workspace.upgrade();
|
||||||
|
let close_action = Box::new(workspace::CloseWindow);
|
||||||
|
|
||||||
let platform_supported = cfg!(target_os = "macos");
|
let platform_supported = cfg!(target_os = "macos");
|
||||||
|
let height = Self::height(cx);
|
||||||
|
|
||||||
TitleBar::new("collab-titlebar", Box::new(workspace::CloseWindow))
|
h_flex()
|
||||||
|
.id("titlebar")
|
||||||
|
.w_full()
|
||||||
|
.pt(Self::top_padding(cx))
|
||||||
|
.h(height + Self::top_padding(cx))
|
||||||
|
.map(|this| {
|
||||||
|
if cx.is_fullscreen() {
|
||||||
|
this.pl_2()
|
||||||
|
} else if self.platform_style == PlatformStyle::Mac {
|
||||||
|
this.pl(px(platform_mac::TRAFFIC_LIGHT_PADDING))
|
||||||
|
} else {
|
||||||
|
this.pl_2()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.bg(cx.theme().colors().title_bar_background)
|
||||||
|
.content_stretch()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.id("titlebar-content")
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.justify_between()
|
||||||
|
.w_full()
|
||||||
// note: on windows titlebar behaviour is handled by the platform implementation
|
// note: on windows titlebar behaviour is handled by the platform implementation
|
||||||
.when(cfg!(not(windows)), |this| {
|
.when(cfg!(not(windows)), |this| {
|
||||||
this.on_click(|event, cx| {
|
this.on_click(|event, cx| {
|
||||||
@ -127,7 +160,7 @@ impl Render for CollabTitlebarItem {
|
|||||||
== ParticipantLocation::SharedProject { project_id }
|
== ParticipantLocation::SharedProject { project_id }
|
||||||
});
|
});
|
||||||
|
|
||||||
let face_pile = self.render_collaborator(
|
let facepile = self.render_collaborator(
|
||||||
&collaborator.user,
|
&collaborator.user,
|
||||||
collaborator.peer_id,
|
collaborator.peer_id,
|
||||||
is_present,
|
is_present,
|
||||||
@ -143,7 +176,7 @@ impl Render for CollabTitlebarItem {
|
|||||||
Some(
|
Some(
|
||||||
v_flex()
|
v_flex()
|
||||||
.id(("collaborator", collaborator.user.id))
|
.id(("collaborator", collaborator.user.id))
|
||||||
.child(face_pile)
|
.child(facepile)
|
||||||
.child(render_color_ribbon(player_color.cursor))
|
.child(render_color_ribbon(player_color.cursor))
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.on_click({
|
.on_click({
|
||||||
@ -262,7 +295,9 @@ impl Render for CollabTitlebarItem {
|
|||||||
.selected(platform_supported && is_muted)
|
.selected(platform_supported && is_muted)
|
||||||
.disabled(!platform_supported)
|
.disabled(!platform_supported)
|
||||||
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
|
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
|
||||||
.on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
|
.on_click(move |_, cx| {
|
||||||
|
call_controls::toggle_mute(&Default::default(), cx);
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
@ -293,7 +328,9 @@ impl Render for CollabTitlebarItem {
|
|||||||
Tooltip::text("Deafen Audio", cx)
|
Tooltip::text("Deafen Audio", cx)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on_click(move |_, cx| crate::toggle_deafen(&Default::default(), cx)),
|
.on_click(move |_, cx| {
|
||||||
|
call_controls::toggle_deafen(&Default::default(), cx)
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.when(can_share_projects, |this| {
|
.when(can_share_projects, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
@ -316,7 +353,7 @@ impl Render for CollabTitlebarItem {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.on_click(move |_, cx| {
|
.on_click(move |_, cx| {
|
||||||
crate::toggle_screen_sharing(&Default::default(), cx)
|
call_controls::toggle_screen_sharing(&Default::default(), cx)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -334,36 +371,37 @@ impl Render for CollabTitlebarItem {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
.when(
|
||||||
|
self.platform_style == PlatformStyle::Windows && !cx.is_fullscreen(),
|
||||||
|
|title_bar| title_bar.child(platform_windows::WindowsWindowControls::new(height)),
|
||||||
|
)
|
||||||
|
.when(
|
||||||
|
self.platform_style == PlatformStyle::Linux
|
||||||
|
&& !cx.is_fullscreen()
|
||||||
|
&& cx.should_render_window_controls(),
|
||||||
|
|title_bar| {
|
||||||
|
title_bar
|
||||||
|
.child(platform_linux::LinuxWindowControls::new(height, close_action))
|
||||||
|
.on_mouse_down(gpui::MouseButton::Right, move |ev, cx| {
|
||||||
|
cx.show_window_menu(ev.position)
|
||||||
|
})
|
||||||
|
.on_mouse_move(move |ev, cx| {
|
||||||
|
if ev.dragging() {
|
||||||
|
cx.start_system_move();
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
fn render_color_ribbon(color: Hsla) -> impl Element {
|
|
||||||
canvas(
|
|
||||||
move |_, _| {},
|
|
||||||
move |bounds, _, cx| {
|
|
||||||
let height = bounds.size.height;
|
|
||||||
let horizontal_offset = height;
|
|
||||||
let vertical_offset = px(height.0 / 2.0);
|
|
||||||
let mut path = Path::new(bounds.lower_left());
|
|
||||||
path.curve_to(
|
|
||||||
bounds.origin + point(horizontal_offset, vertical_offset),
|
|
||||||
bounds.origin + point(px(0.0), vertical_offset),
|
|
||||||
);
|
|
||||||
path.line_to(bounds.upper_right() + point(-horizontal_offset, vertical_offset));
|
|
||||||
path.curve_to(
|
|
||||||
bounds.lower_right(),
|
|
||||||
bounds.upper_right() + point(px(0.0), vertical_offset),
|
|
||||||
);
|
|
||||||
path.line_to(bounds.lower_left());
|
|
||||||
cx.paint_path(path, color);
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.h_1()
|
}
|
||||||
.w_full()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CollabTitlebarItem {
|
impl TitleBar {
|
||||||
pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(
|
||||||
|
id: impl Into<ElementId>,
|
||||||
|
workspace: &Workspace,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Self {
|
||||||
let project = workspace.project().clone();
|
let project = workspace.project().clone();
|
||||||
let user_store = workspace.app_state().user_store.clone();
|
let user_store = workspace.app_state().user_store.clone();
|
||||||
let client = workspace.app_state().client.clone();
|
let client = workspace.app_state().client.clone();
|
||||||
@ -380,6 +418,9 @@ impl CollabTitlebarItem {
|
|||||||
subscriptions.push(cx.observe(&user_store, |_, _, cx| cx.notify()));
|
subscriptions.push(cx.observe(&user_store, |_, _, cx| cx.notify()));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
platform_style: PlatformStyle::platform(),
|
||||||
|
content: div().id(id.into()),
|
||||||
|
children: SmallVec::new(),
|
||||||
workspace: workspace.weak_handle(),
|
workspace: workspace.weak_handle(),
|
||||||
project,
|
project,
|
||||||
user_store,
|
user_store,
|
||||||
@ -388,6 +429,45 @@ impl CollabTitlebarItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub fn height(cx: &mut WindowContext) -> Pixels {
|
||||||
|
(1.75 * cx.rem_size()).max(px(34.))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub fn height(_cx: &mut WindowContext) -> Pixels {
|
||||||
|
// todo(windows) instead of hard coded size report the actual size to the Windows platform API
|
||||||
|
px(32.)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn top_padding(_cx: &WindowContext) -> Pixels {
|
||||||
|
px(0.)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn top_padding(cx: &WindowContext) -> Pixels {
|
||||||
|
use windows::Win32::UI::{
|
||||||
|
HiDpi::GetSystemMetricsForDpi,
|
||||||
|
WindowsAndMessaging::{SM_CXPADDEDBORDER, USER_DEFAULT_SCREEN_DPI},
|
||||||
|
};
|
||||||
|
|
||||||
|
// This top padding is not dependent on the title bar style and is instead a quirk of maximized windows on Windows:
|
||||||
|
// https://devblogs.microsoft.com/oldnewthing/20150304-00/?p=44543
|
||||||
|
let padding = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, USER_DEFAULT_SCREEN_DPI) };
|
||||||
|
if cx.is_maximized() {
|
||||||
|
px((padding * 2) as f32)
|
||||||
|
} else {
|
||||||
|
px(0.)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the platform style.
|
||||||
|
pub fn platform_style(mut self, style: PlatformStyle) -> Self {
|
||||||
|
self.platform_style = style;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_application_menu(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
pub fn render_application_menu(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
||||||
cfg!(not(target_os = "macos")).then(|| {
|
cfg!(not(target_os = "macos")).then(|| {
|
||||||
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
|
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
|
||||||
@ -712,97 +792,6 @@ impl CollabTitlebarItem {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn render_collaborator(
|
|
||||||
&self,
|
|
||||||
user: &Arc<User>,
|
|
||||||
peer_id: PeerId,
|
|
||||||
is_present: bool,
|
|
||||||
is_speaking: bool,
|
|
||||||
is_muted: bool,
|
|
||||||
leader_selection_color: Option<Hsla>,
|
|
||||||
room: &Room,
|
|
||||||
project_id: Option<u64>,
|
|
||||||
current_user: &Arc<User>,
|
|
||||||
cx: &ViewContext<Self>,
|
|
||||||
) -> Option<Div> {
|
|
||||||
if room.role_for_user(user.id) == Some(proto::ChannelRole::Guest) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FACEPILE_LIMIT: usize = 3;
|
|
||||||
let followers = project_id.map_or(&[] as &[_], |id| room.followers_for(peer_id, id));
|
|
||||||
let extra_count = followers.len().saturating_sub(FACEPILE_LIMIT);
|
|
||||||
|
|
||||||
Some(
|
|
||||||
div()
|
|
||||||
.m_0p5()
|
|
||||||
.p_0p5()
|
|
||||||
// When the collaborator is not followed, still draw this wrapper div, but leave
|
|
||||||
// it transparent, so that it does not shift the layout when following.
|
|
||||||
.when_some(leader_selection_color, |div, color| {
|
|
||||||
div.rounded_md().bg(color)
|
|
||||||
})
|
|
||||||
.child(
|
|
||||||
FacePile::empty()
|
|
||||||
.child(
|
|
||||||
Avatar::new(user.avatar_uri.clone())
|
|
||||||
.grayscale(!is_present)
|
|
||||||
.border_color(if is_speaking {
|
|
||||||
cx.theme().status().info
|
|
||||||
} else {
|
|
||||||
// We draw the border in a transparent color rather to avoid
|
|
||||||
// the layout shift that would come with adding/removing the border.
|
|
||||||
gpui::transparent_black()
|
|
||||||
})
|
|
||||||
.when(is_muted, |avatar| {
|
|
||||||
avatar.indicator(
|
|
||||||
AvatarAudioStatusIndicator::new(ui::AudioStatus::Muted)
|
|
||||||
.tooltip({
|
|
||||||
let github_login = user.github_login.clone();
|
|
||||||
move |cx| {
|
|
||||||
Tooltip::text(
|
|
||||||
format!("{} is muted", github_login),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.children(followers.iter().take(FACEPILE_LIMIT).filter_map(
|
|
||||||
|follower_peer_id| {
|
|
||||||
let follower = room
|
|
||||||
.remote_participants()
|
|
||||||
.values()
|
|
||||||
.find_map(|p| {
|
|
||||||
(p.peer_id == *follower_peer_id).then_some(&p.user)
|
|
||||||
})
|
|
||||||
.or_else(|| {
|
|
||||||
(self.client.peer_id() == Some(*follower_peer_id))
|
|
||||||
.then_some(current_user)
|
|
||||||
})?
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
Some(div().mt(-px(4.)).child(
|
|
||||||
Avatar::new(follower.avatar_uri.clone()).size(rems(0.75)),
|
|
||||||
))
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.children(if extra_count > 0 {
|
|
||||||
Some(
|
|
||||||
div()
|
|
||||||
.ml_1()
|
|
||||||
.child(Label::new(format!("+{extra_count}")))
|
|
||||||
.into_any_element(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn window_activation_changed(&mut self, cx: &mut ViewContext<Self>) {
|
fn window_activation_changed(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
if cx.is_window_active() {
|
if cx.is_window_active() {
|
||||||
ActiveCall::global(cx)
|
ActiveCall::global(cx)
|
||||||
@ -960,3 +949,17 @@ impl CollabTitlebarItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl InteractiveElement for TitleBar {
|
||||||
|
fn interactivity(&mut self) -> &mut Interactivity {
|
||||||
|
self.content.interactivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatefulInteractiveElement for TitleBar {}
|
||||||
|
|
||||||
|
impl ParentElement for TitleBar {
|
||||||
|
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||||
|
self.children.extend(elements)
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ mod context_menu;
|
|||||||
mod disclosure;
|
mod disclosure;
|
||||||
mod divider;
|
mod divider;
|
||||||
mod dropdown_menu;
|
mod dropdown_menu;
|
||||||
|
mod facepile;
|
||||||
mod icon;
|
mod icon;
|
||||||
mod indicator;
|
mod indicator;
|
||||||
mod keybinding;
|
mod keybinding;
|
||||||
@ -19,7 +20,6 @@ mod setting;
|
|||||||
mod stack;
|
mod stack;
|
||||||
mod tab;
|
mod tab;
|
||||||
mod tab_bar;
|
mod tab_bar;
|
||||||
mod title_bar;
|
|
||||||
mod tool_strip;
|
mod tool_strip;
|
||||||
mod tooltip;
|
mod tooltip;
|
||||||
|
|
||||||
@ -33,6 +33,7 @@ pub use context_menu::*;
|
|||||||
pub use disclosure::*;
|
pub use disclosure::*;
|
||||||
pub use divider::*;
|
pub use divider::*;
|
||||||
use dropdown_menu::*;
|
use dropdown_menu::*;
|
||||||
|
pub use facepile::*;
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
pub use indicator::*;
|
pub use indicator::*;
|
||||||
pub use keybinding::*;
|
pub use keybinding::*;
|
||||||
@ -47,7 +48,6 @@ pub use setting::*;
|
|||||||
pub use stack::*;
|
pub use stack::*;
|
||||||
pub use tab::*;
|
pub use tab::*;
|
||||||
pub use tab_bar::*;
|
pub use tab_bar::*;
|
||||||
pub use title_bar::*;
|
|
||||||
pub use tool_strip::*;
|
pub use tool_strip::*;
|
||||||
pub use tooltip::*;
|
pub use tooltip::*;
|
||||||
|
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
use gpui::AnyElement;
|
use gpui::AnyElement;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use ui::prelude::*;
|
|
||||||
|
|
||||||
|
/// A facepile is a collection of faces stacked horizontally–
|
||||||
|
/// always with the leftmost face on top and descending in z-index
|
||||||
|
///
|
||||||
|
/// Facepiles are used to display a group of people or things,
|
||||||
|
/// such as a list of participants in a collaboration session.
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct FacePile {
|
pub struct Facepile {
|
||||||
base: Div,
|
base: Div,
|
||||||
faces: SmallVec<[AnyElement; 2]>,
|
faces: SmallVec<[AnyElement; 2]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FacePile {
|
impl Facepile {
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self::new(SmallVec::new())
|
Self::new(SmallVec::new())
|
||||||
}
|
}
|
||||||
@ -18,7 +23,7 @@ impl FacePile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderOnce for FacePile {
|
impl RenderOnce for Facepile {
|
||||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||||
// Lay the faces out in reverse so they overlap in the desired order (left to right, front to back)
|
// Lay the faces out in reverse so they overlap in the desired order (left to right, front to back)
|
||||||
self.base
|
self.base
|
||||||
@ -36,13 +41,13 @@ impl RenderOnce for FacePile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParentElement for FacePile {
|
impl ParentElement for Facepile {
|
||||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||||
self.faces.extend(elements);
|
self.faces.extend(elements);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Styled for FacePile {
|
impl Styled for Facepile {
|
||||||
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
||||||
self.base.style()
|
self.base.style()
|
||||||
}
|
}
|
@ -13,7 +13,6 @@ mod list_item;
|
|||||||
mod setting;
|
mod setting;
|
||||||
mod tab;
|
mod tab;
|
||||||
mod tab_bar;
|
mod tab_bar;
|
||||||
mod title_bar;
|
|
||||||
mod toggle_button;
|
mod toggle_button;
|
||||||
mod tool_strip;
|
mod tool_strip;
|
||||||
|
|
||||||
@ -32,6 +31,5 @@ pub use list_item::*;
|
|||||||
pub use setting::*;
|
pub use setting::*;
|
||||||
pub use tab::*;
|
pub use tab::*;
|
||||||
pub use tab_bar::*;
|
pub use tab_bar::*;
|
||||||
pub use title_bar::*;
|
|
||||||
pub use toggle_button::*;
|
pub use toggle_button::*;
|
||||||
pub use tool_strip::*;
|
pub use tool_strip::*;
|
||||||
|
@ -1,135 +0,0 @@
|
|||||||
use gpui::{Action, AnyElement, Interactivity, Stateful};
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
|
|
||||||
use crate::components::title_bar::linux_window_controls::LinuxWindowControls;
|
|
||||||
use crate::components::title_bar::windows_window_controls::WindowsWindowControls;
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
pub struct TitleBar {
|
|
||||||
platform_style: PlatformStyle,
|
|
||||||
content: Stateful<Div>,
|
|
||||||
children: SmallVec<[AnyElement; 2]>,
|
|
||||||
close_window_action: Box<dyn Action>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TitleBar {
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
pub fn height(cx: &mut WindowContext) -> Pixels {
|
|
||||||
(1.75 * cx.rem_size()).max(px(34.))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub fn height(_cx: &mut WindowContext) -> Pixels {
|
|
||||||
// todo(windows) instead of hard coded size report the actual size to the Windows platform API
|
|
||||||
px(32.)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
fn top_padding(_cx: &WindowContext) -> Pixels {
|
|
||||||
px(0.)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
fn top_padding(cx: &WindowContext) -> Pixels {
|
|
||||||
use windows::Win32::UI::{
|
|
||||||
HiDpi::GetSystemMetricsForDpi,
|
|
||||||
WindowsAndMessaging::{SM_CXPADDEDBORDER, USER_DEFAULT_SCREEN_DPI},
|
|
||||||
};
|
|
||||||
|
|
||||||
// This top padding is not dependent on the title bar style and is instead a quirk of maximized windows on Windows:
|
|
||||||
// https://devblogs.microsoft.com/oldnewthing/20150304-00/?p=44543
|
|
||||||
let padding = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, USER_DEFAULT_SCREEN_DPI) };
|
|
||||||
if cx.is_maximized() {
|
|
||||||
px((padding * 2) as f32)
|
|
||||||
} else {
|
|
||||||
px(0.)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(id: impl Into<ElementId>, close_window_action: Box<dyn Action>) -> Self {
|
|
||||||
Self {
|
|
||||||
platform_style: PlatformStyle::platform(),
|
|
||||||
content: div().id(id.into()),
|
|
||||||
children: SmallVec::new(),
|
|
||||||
close_window_action,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the platform style.
|
|
||||||
pub fn platform_style(mut self, style: PlatformStyle) -> Self {
|
|
||||||
self.platform_style = style;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InteractiveElement for TitleBar {
|
|
||||||
fn interactivity(&mut self) -> &mut Interactivity {
|
|
||||||
self.content.interactivity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StatefulInteractiveElement for TitleBar {}
|
|
||||||
|
|
||||||
impl ParentElement for TitleBar {
|
|
||||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
|
||||||
self.children.extend(elements)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for TitleBar {
|
|
||||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
|
||||||
let height = Self::height(cx);
|
|
||||||
h_flex()
|
|
||||||
.id("titlebar")
|
|
||||||
.w_full()
|
|
||||||
.pt(Self::top_padding(cx))
|
|
||||||
.h(height + Self::top_padding(cx))
|
|
||||||
.map(|this| {
|
|
||||||
if cx.is_fullscreen() {
|
|
||||||
this.pl_2()
|
|
||||||
} else if self.platform_style == PlatformStyle::Mac {
|
|
||||||
// Use pixels here instead of a rem-based size because the macOS traffic
|
|
||||||
// lights are a static size, and don't scale with the rest of the UI.
|
|
||||||
//
|
|
||||||
// Magic number: There is one extra pixel of padding on the left side due to
|
|
||||||
// the 1px border around the window on macOS apps.
|
|
||||||
this.pl(px(71.))
|
|
||||||
} else {
|
|
||||||
this.pl_2()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.bg(cx.theme().colors().title_bar_background)
|
|
||||||
.content_stretch()
|
|
||||||
.child(
|
|
||||||
self.content
|
|
||||||
.id("titlebar-content")
|
|
||||||
.flex()
|
|
||||||
.flex_row()
|
|
||||||
.justify_between()
|
|
||||||
.w_full()
|
|
||||||
.children(self.children),
|
|
||||||
)
|
|
||||||
.when(
|
|
||||||
self.platform_style == PlatformStyle::Windows && !cx.is_fullscreen(),
|
|
||||||
|title_bar| title_bar.child(WindowsWindowControls::new(height)),
|
|
||||||
)
|
|
||||||
.when(
|
|
||||||
self.platform_style == PlatformStyle::Linux
|
|
||||||
&& !cx.is_fullscreen()
|
|
||||||
&& cx.should_render_window_controls(),
|
|
||||||
|title_bar| {
|
|
||||||
title_bar
|
|
||||||
.child(LinuxWindowControls::new(height, self.close_window_action))
|
|
||||||
.on_mouse_down(gpui::MouseButton::Right, move |ev, cx| {
|
|
||||||
cx.show_window_menu(ev.position)
|
|
||||||
})
|
|
||||||
.on_mouse_move(move |ev, cx| {
|
|
||||||
if ev.dragging() {
|
|
||||||
cx.start_system_move();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user