linux: Add title bar for rules library (#33025)
Closes #30513 - Abstract away common wrapper component to `platform_title_bar`. - Use it in both zed and rules library. - For rules library, keep traffic like only style for macOS, and add custom title bar for Linux and Windows. Release Notes: - Added way to minimize, maximize, and close the rules library window for Linux.
This commit is contained in:
parent
c8d49408d3
commit
1bd49a77e0
5 changed files with 339 additions and 280 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -13607,6 +13607,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"settings",
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
|
"title_bar",
|
||||||
"ui",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
|
|
@ -27,6 +27,7 @@ rope.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
|
title_bar.workspace = true
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
workspace-hack.workspace = true
|
workspace-hack.workspace = true
|
||||||
|
|
|
@ -20,12 +20,13 @@ use std::sync::Arc;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
|
use title_bar::platform_title_bar::PlatformTitleBar;
|
||||||
use ui::{
|
use ui::{
|
||||||
Context, IconButtonShape, KeyBinding, ListItem, ListItemSpacing, ParentElement, Render,
|
Context, IconButtonShape, KeyBinding, ListItem, ListItemSpacing, ParentElement, Render,
|
||||||
SharedString, Styled, Tooltip, Window, div, prelude::*,
|
SharedString, Styled, Tooltip, Window, div, prelude::*,
|
||||||
};
|
};
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
use workspace::Workspace;
|
use workspace::{Workspace, client_side_decorations};
|
||||||
use zed_actions::assistant::InlineAssist;
|
use zed_actions::assistant::InlineAssist;
|
||||||
|
|
||||||
use prompt_store::*;
|
use prompt_store::*;
|
||||||
|
@ -110,15 +111,22 @@ pub fn open_rules_library(
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let app_id = ReleaseChannel::global(cx).app_id();
|
let app_id = ReleaseChannel::global(cx).app_id();
|
||||||
let bounds = Bounds::centered(None, size(px(1024.0), px(768.0)), cx);
|
let bounds = Bounds::centered(None, size(px(1024.0), px(768.0)), cx);
|
||||||
|
let window_decorations = match std::env::var("ZED_WINDOW_DECORATIONS") {
|
||||||
|
Ok(val) if val == "server" => gpui::WindowDecorations::Server,
|
||||||
|
Ok(val) if val == "client" => gpui::WindowDecorations::Client,
|
||||||
|
_ => gpui::WindowDecorations::Client,
|
||||||
|
};
|
||||||
cx.open_window(
|
cx.open_window(
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
titlebar: Some(TitlebarOptions {
|
titlebar: Some(TitlebarOptions {
|
||||||
title: Some("Rules Library".into()),
|
title: Some("Rules Library".into()),
|
||||||
appears_transparent: cfg!(target_os = "macos"),
|
appears_transparent: true,
|
||||||
traffic_light_position: Some(point(px(9.0), px(9.0))),
|
traffic_light_position: Some(point(px(9.0), px(9.0))),
|
||||||
}),
|
}),
|
||||||
app_id: Some(app_id.to_owned()),
|
app_id: Some(app_id.to_owned()),
|
||||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||||
|
window_background: cx.theme().window_background_appearance(),
|
||||||
|
window_decorations: Some(window_decorations),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|window, cx| {
|
|window, cx| {
|
||||||
|
@ -140,6 +148,7 @@ pub fn open_rules_library(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RulesLibrary {
|
pub struct RulesLibrary {
|
||||||
|
title_bar: Option<Entity<PlatformTitleBar>>,
|
||||||
store: Entity<PromptStore>,
|
store: Entity<PromptStore>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
rule_editors: HashMap<PromptId, RuleEditor>,
|
rule_editors: HashMap<PromptId, RuleEditor>,
|
||||||
|
@ -395,6 +404,11 @@ impl RulesLibrary {
|
||||||
picker
|
picker
|
||||||
});
|
});
|
||||||
Self {
|
Self {
|
||||||
|
title_bar: if !cfg!(target_os = "macos") {
|
||||||
|
Some(cx.new(|_| PlatformTitleBar::new("rules-library-title-bar")))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
store: store.clone(),
|
store: store.clone(),
|
||||||
language_registry,
|
language_registry,
|
||||||
rule_editors: HashMap::default(),
|
rule_editors: HashMap::default(),
|
||||||
|
@ -1225,75 +1239,90 @@ impl Render for RulesLibrary {
|
||||||
let ui_font = theme::setup_ui_font(window, cx);
|
let ui_font = theme::setup_ui_font(window, cx);
|
||||||
let theme = cx.theme().clone();
|
let theme = cx.theme().clone();
|
||||||
|
|
||||||
h_flex()
|
client_side_decorations(
|
||||||
.id("rules-library")
|
v_flex()
|
||||||
.key_context("PromptLibrary")
|
.id("rules-library")
|
||||||
.on_action(cx.listener(|this, &NewRule, window, cx| this.new_rule(window, cx)))
|
.key_context("PromptLibrary")
|
||||||
.on_action(
|
.on_action(cx.listener(|this, &NewRule, window, cx| this.new_rule(window, cx)))
|
||||||
cx.listener(|this, &DeleteRule, window, cx| this.delete_active_rule(window, cx)),
|
.on_action(
|
||||||
)
|
cx.listener(|this, &DeleteRule, window, cx| {
|
||||||
.on_action(cx.listener(|this, &DuplicateRule, window, cx| {
|
this.delete_active_rule(window, cx)
|
||||||
this.duplicate_active_rule(window, cx)
|
}),
|
||||||
}))
|
)
|
||||||
.on_action(cx.listener(|this, &ToggleDefaultRule, window, cx| {
|
.on_action(cx.listener(|this, &DuplicateRule, window, cx| {
|
||||||
this.toggle_default_for_active_rule(window, cx)
|
this.duplicate_active_rule(window, cx)
|
||||||
}))
|
}))
|
||||||
.size_full()
|
.on_action(cx.listener(|this, &ToggleDefaultRule, window, cx| {
|
||||||
.overflow_hidden()
|
this.toggle_default_for_active_rule(window, cx)
|
||||||
.font(ui_font)
|
}))
|
||||||
.text_color(theme.colors().text)
|
.size_full()
|
||||||
.child(self.render_rule_list(cx))
|
.overflow_hidden()
|
||||||
.map(|el| {
|
.font(ui_font)
|
||||||
if self.store.read(cx).prompt_count() == 0 {
|
.text_color(theme.colors().text)
|
||||||
el.child(
|
.children(self.title_bar.clone())
|
||||||
v_flex()
|
.child(
|
||||||
.w_2_3()
|
h_flex()
|
||||||
.h_full()
|
.flex_1()
|
||||||
.items_center()
|
.child(self.render_rule_list(cx))
|
||||||
.justify_center()
|
.map(|el| {
|
||||||
.gap_4()
|
if self.store.read(cx).prompt_count() == 0 {
|
||||||
.bg(cx.theme().colors().editor_background)
|
el.child(
|
||||||
.child(
|
v_flex()
|
||||||
h_flex()
|
.w_2_3()
|
||||||
.gap_2()
|
.h_full()
|
||||||
.child(
|
.items_center()
|
||||||
Icon::new(IconName::Book)
|
.justify_center()
|
||||||
.size(IconSize::Medium)
|
.gap_4()
|
||||||
.color(Color::Muted),
|
.bg(cx.theme().colors().editor_background)
|
||||||
)
|
.child(
|
||||||
.child(
|
h_flex()
|
||||||
Label::new("No rules yet")
|
.gap_2()
|
||||||
.size(LabelSize::Large)
|
.child(
|
||||||
.color(Color::Muted),
|
Icon::new(IconName::Book)
|
||||||
),
|
.size(IconSize::Medium)
|
||||||
)
|
.color(Color::Muted),
|
||||||
.child(
|
)
|
||||||
h_flex()
|
.child(
|
||||||
.child(h_flex())
|
Label::new("No rules yet")
|
||||||
.child(
|
.size(LabelSize::Large)
|
||||||
v_flex()
|
.color(Color::Muted),
|
||||||
.gap_1()
|
),
|
||||||
.child(Label::new("Create your first rule:"))
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("create-rule", "New Rule")
|
h_flex()
|
||||||
.full_width()
|
.child(h_flex())
|
||||||
.key_binding(KeyBinding::for_action(
|
.child(
|
||||||
&NewRule, window, cx,
|
v_flex()
|
||||||
))
|
.gap_1()
|
||||||
.on_click(|_, window, cx| {
|
.child(Label::new(
|
||||||
window.dispatch_action(
|
"Create your first rule:",
|
||||||
NewRule.boxed_clone(),
|
))
|
||||||
cx,
|
.child(
|
||||||
)
|
Button::new("create-rule", "New Rule")
|
||||||
}),
|
.full_width()
|
||||||
),
|
.key_binding(
|
||||||
)
|
KeyBinding::for_action(
|
||||||
.child(h_flex()),
|
&NewRule, window, cx,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
} else {
|
.on_click(|_, window, cx| {
|
||||||
el.child(self.render_active_rule(cx))
|
window.dispatch_action(
|
||||||
}
|
NewRule.boxed_clone(),
|
||||||
})
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(h_flex()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
el.child(self.render_active_rule(cx))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
169
crates/title_bar/src/platform_title_bar.rs
Normal file
169
crates/title_bar/src/platform_title_bar.rs
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
use gpui::{
|
||||||
|
AnyElement, Context, Decorations, InteractiveElement, IntoElement, MouseButton, ParentElement,
|
||||||
|
Pixels, StatefulInteractiveElement, Styled, Window, WindowControlArea, div, px,
|
||||||
|
};
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
use std::mem;
|
||||||
|
use ui::prelude::*;
|
||||||
|
|
||||||
|
use crate::platforms::{platform_linux, platform_mac, platform_windows};
|
||||||
|
|
||||||
|
pub struct PlatformTitleBar {
|
||||||
|
id: ElementId,
|
||||||
|
platform_style: PlatformStyle,
|
||||||
|
children: SmallVec<[AnyElement; 2]>,
|
||||||
|
should_move: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformTitleBar {
|
||||||
|
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||||
|
let platform_style = PlatformStyle::platform();
|
||||||
|
Self {
|
||||||
|
id: id.into(),
|
||||||
|
platform_style,
|
||||||
|
children: SmallVec::new(),
|
||||||
|
should_move: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub fn height(window: &mut Window) -> Pixels {
|
||||||
|
(1.75 * window.rem_size()).max(px(34.))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub fn height(_window: &mut Window) -> Pixels {
|
||||||
|
// todo(windows) instead of hard coded size report the actual size to the Windows platform API
|
||||||
|
px(32.)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_children<T>(&mut self, children: T)
|
||||||
|
where
|
||||||
|
T: IntoIterator<Item = AnyElement>,
|
||||||
|
{
|
||||||
|
self.children = children.into_iter().collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for PlatformTitleBar {
|
||||||
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let supported_controls = window.window_controls();
|
||||||
|
let decorations = window.window_decorations();
|
||||||
|
let height = Self::height(window);
|
||||||
|
let titlebar_color = if cfg!(any(target_os = "linux", target_os = "freebsd")) {
|
||||||
|
if window.is_window_active() && !self.should_move {
|
||||||
|
cx.theme().colors().title_bar_background
|
||||||
|
} else {
|
||||||
|
cx.theme().colors().title_bar_inactive_background
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cx.theme().colors().title_bar_background
|
||||||
|
};
|
||||||
|
let close_action = Box::new(workspace::CloseWindow);
|
||||||
|
let children = mem::take(&mut self.children);
|
||||||
|
|
||||||
|
h_flex()
|
||||||
|
.window_control_area(WindowControlArea::Drag)
|
||||||
|
.w_full()
|
||||||
|
.h(height)
|
||||||
|
.map(|this| {
|
||||||
|
if window.is_fullscreen() {
|
||||||
|
this.pl_2()
|
||||||
|
} else if self.platform_style == PlatformStyle::Mac {
|
||||||
|
this.pl(px(platform_mac::TRAFFIC_LIGHT_PADDING))
|
||||||
|
} else {
|
||||||
|
this.pl_2()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|el| match decorations {
|
||||||
|
Decorations::Server => el,
|
||||||
|
Decorations::Client { tiling, .. } => el
|
||||||
|
.when(!(tiling.top || tiling.right), |el| {
|
||||||
|
el.rounded_tr(theme::CLIENT_SIDE_DECORATION_ROUNDING)
|
||||||
|
})
|
||||||
|
.when(!(tiling.top || tiling.left), |el| {
|
||||||
|
el.rounded_tl(theme::CLIENT_SIDE_DECORATION_ROUNDING)
|
||||||
|
})
|
||||||
|
// this border is to avoid a transparent gap in the rounded corners
|
||||||
|
.mt(px(-1.))
|
||||||
|
.border(px(1.))
|
||||||
|
.border_color(titlebar_color),
|
||||||
|
})
|
||||||
|
.bg(titlebar_color)
|
||||||
|
.content_stretch()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.id(self.id.clone())
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.items_center()
|
||||||
|
.justify_between()
|
||||||
|
.w_full()
|
||||||
|
// Note: On Windows the title bar behavior is handled by the platform implementation.
|
||||||
|
.when(self.platform_style == PlatformStyle::Mac, |this| {
|
||||||
|
this.on_click(|event, window, _| {
|
||||||
|
if event.up.click_count == 2 {
|
||||||
|
window.titlebar_double_click();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.when(self.platform_style == PlatformStyle::Linux, |this| {
|
||||||
|
this.on_click(|event, window, _| {
|
||||||
|
if event.up.click_count == 2 {
|
||||||
|
window.zoom_window();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.children(children),
|
||||||
|
)
|
||||||
|
.when(!window.is_fullscreen(), |title_bar| {
|
||||||
|
match self.platform_style {
|
||||||
|
PlatformStyle::Mac => title_bar,
|
||||||
|
PlatformStyle::Linux => {
|
||||||
|
if matches!(decorations, Decorations::Client { .. }) {
|
||||||
|
title_bar
|
||||||
|
.child(platform_linux::LinuxWindowControls::new(close_action))
|
||||||
|
.when(supported_controls.window_menu, |titlebar| {
|
||||||
|
titlebar
|
||||||
|
.on_mouse_down(MouseButton::Right, move |ev, window, _| {
|
||||||
|
window.show_window_menu(ev.position)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.on_mouse_move(cx.listener(move |this, _ev, window, _| {
|
||||||
|
if this.should_move {
|
||||||
|
this.should_move = false;
|
||||||
|
window.start_window_move();
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.on_mouse_down_out(cx.listener(move |this, _ev, _window, _cx| {
|
||||||
|
this.should_move = false;
|
||||||
|
}))
|
||||||
|
.on_mouse_up(
|
||||||
|
MouseButton::Left,
|
||||||
|
cx.listener(move |this, _ev, _window, _cx| {
|
||||||
|
this.should_move = false;
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on_mouse_down(
|
||||||
|
MouseButton::Left,
|
||||||
|
cx.listener(move |this, _ev, _window, _cx| {
|
||||||
|
this.should_move = true;
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
title_bar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PlatformStyle::Windows => {
|
||||||
|
title_bar.child(platform_windows::WindowsWindowControls::new(height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParentElement for PlatformTitleBar {
|
||||||
|
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||||
|
self.children.extend(elements)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +1,32 @@
|
||||||
mod application_menu;
|
mod application_menu;
|
||||||
mod collab;
|
mod collab;
|
||||||
mod onboarding_banner;
|
mod onboarding_banner;
|
||||||
|
pub mod platform_title_bar;
|
||||||
mod platforms;
|
mod platforms;
|
||||||
mod title_bar_settings;
|
mod title_bar_settings;
|
||||||
|
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
mod stories;
|
mod stories;
|
||||||
|
|
||||||
use crate::application_menu::ApplicationMenu;
|
use crate::{application_menu::ApplicationMenu, platform_title_bar::PlatformTitleBar};
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
use crate::application_menu::{
|
use crate::application_menu::{
|
||||||
ActivateDirection, ActivateMenuLeft, ActivateMenuRight, OpenApplicationMenu,
|
ActivateDirection, ActivateMenuLeft, ActivateMenuRight, OpenApplicationMenu,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::platforms::{platform_linux, platform_mac, platform_windows};
|
|
||||||
use auto_update::AutoUpdateStatus;
|
use auto_update::AutoUpdateStatus;
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
use client::{Client, UserStore};
|
use client::{Client, UserStore};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, AnyElement, App, Context, Corner, Decorations, Element, Entity, InteractiveElement,
|
Action, AnyElement, App, Context, Corner, Element, Entity, InteractiveElement, IntoElement,
|
||||||
Interactivity, IntoElement, MouseButton, ParentElement, Render, Stateful,
|
MouseButton, ParentElement, Render, StatefulInteractiveElement, Styled, Subscription,
|
||||||
StatefulInteractiveElement, Styled, Subscription, WeakEntity, Window, WindowControlArea,
|
WeakEntity, Window, actions, div,
|
||||||
actions, div, px,
|
|
||||||
};
|
};
|
||||||
use onboarding_banner::OnboardingBanner;
|
use onboarding_banner::OnboardingBanner;
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use rpc::proto;
|
use rpc::proto;
|
||||||
use settings::Settings as _;
|
use settings::Settings as _;
|
||||||
use smallvec::SmallVec;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use title_bar_settings::TitleBarSettings;
|
use title_bar_settings::TitleBarSettings;
|
||||||
|
@ -111,14 +109,11 @@ pub fn init(cx: &mut App) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TitleBar {
|
pub struct TitleBar {
|
||||||
platform_style: PlatformStyle,
|
platform_titlebar: Entity<PlatformTitleBar>,
|
||||||
content: Stateful<Div>,
|
|
||||||
children: SmallVec<[AnyElement; 2]>,
|
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
user_store: Entity<UserStore>,
|
user_store: Entity<UserStore>,
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
should_move: bool,
|
|
||||||
application_menu: Option<Entity<ApplicationMenu>>,
|
application_menu: Option<Entity<ApplicationMenu>>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
banner: Entity<OnboardingBanner>,
|
banner: Entity<OnboardingBanner>,
|
||||||
|
@ -127,173 +122,69 @@ pub struct TitleBar {
|
||||||
impl Render for TitleBar {
|
impl Render for TitleBar {
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let title_bar_settings = *TitleBarSettings::get_global(cx);
|
let title_bar_settings = *TitleBarSettings::get_global(cx);
|
||||||
let close_action = Box::new(workspace::CloseWindow);
|
|
||||||
let height = Self::height(window);
|
|
||||||
let supported_controls = window.window_controls();
|
|
||||||
let decorations = window.window_decorations();
|
|
||||||
let titlebar_color = if cfg!(any(target_os = "linux", target_os = "freebsd")) {
|
|
||||||
if window.is_window_active() && !self.should_move {
|
|
||||||
cx.theme().colors().title_bar_background
|
|
||||||
} else {
|
|
||||||
cx.theme().colors().title_bar_inactive_background
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cx.theme().colors().title_bar_background
|
|
||||||
};
|
|
||||||
|
|
||||||
h_flex()
|
let mut children = Vec::new();
|
||||||
.id("titlebar")
|
|
||||||
.window_control_area(WindowControlArea::Drag)
|
children.push(
|
||||||
.w_full()
|
h_flex()
|
||||||
.h(height)
|
.gap_1()
|
||||||
.map(|this| {
|
.map(|title_bar| {
|
||||||
if window.is_fullscreen() {
|
let mut render_project_items = title_bar_settings.show_branch_name
|
||||||
this.pl_2()
|
|| title_bar_settings.show_project_items;
|
||||||
} else if self.platform_style == PlatformStyle::Mac {
|
title_bar
|
||||||
this.pl(px(platform_mac::TRAFFIC_LIGHT_PADDING))
|
.when_some(self.application_menu.clone(), |title_bar, menu| {
|
||||||
} else {
|
render_project_items &= !menu.read(cx).all_menus_shown();
|
||||||
this.pl_2()
|
title_bar.child(menu)
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(|el| match decorations {
|
|
||||||
Decorations::Server => el,
|
|
||||||
Decorations::Client { tiling, .. } => el
|
|
||||||
.when(!(tiling.top || tiling.right), |el| {
|
|
||||||
el.rounded_tr(theme::CLIENT_SIDE_DECORATION_ROUNDING)
|
|
||||||
})
|
|
||||||
.when(!(tiling.top || tiling.left), |el| {
|
|
||||||
el.rounded_tl(theme::CLIENT_SIDE_DECORATION_ROUNDING)
|
|
||||||
})
|
|
||||||
// this border is to avoid a transparent gap in the rounded corners
|
|
||||||
.mt(px(-1.))
|
|
||||||
.border(px(1.))
|
|
||||||
.border_color(titlebar_color),
|
|
||||||
})
|
|
||||||
.bg(titlebar_color)
|
|
||||||
.content_stretch()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.id("titlebar-content")
|
|
||||||
.flex()
|
|
||||||
.flex_row()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.w_full()
|
|
||||||
// Note: On Windows the title bar behavior is handled by the platform implementation.
|
|
||||||
.when(self.platform_style == PlatformStyle::Mac, |this| {
|
|
||||||
this.on_click(|event, window, _| {
|
|
||||||
if event.up.click_count == 2 {
|
|
||||||
window.titlebar_double_click();
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
.when(render_project_items, |title_bar| {
|
||||||
.when(self.platform_style == PlatformStyle::Linux, |this| {
|
|
||||||
this.on_click(|event, window, _| {
|
|
||||||
if event.up.click_count == 2 {
|
|
||||||
window.zoom_window();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.gap_1()
|
|
||||||
.map(|title_bar| {
|
|
||||||
let mut render_project_items = title_bar_settings.show_branch_name
|
|
||||||
|| title_bar_settings.show_project_items;
|
|
||||||
title_bar
|
|
||||||
.when_some(self.application_menu.clone(), |title_bar, menu| {
|
|
||||||
render_project_items &= !menu.read(cx).all_menus_shown();
|
|
||||||
title_bar.child(menu)
|
|
||||||
})
|
|
||||||
.when(render_project_items, |title_bar| {
|
|
||||||
title_bar
|
|
||||||
.when(
|
|
||||||
title_bar_settings.show_project_items,
|
|
||||||
|title_bar| {
|
|
||||||
title_bar
|
|
||||||
.children(self.render_project_host(cx))
|
|
||||||
.child(self.render_project_name(cx))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.when(
|
|
||||||
title_bar_settings.show_branch_name,
|
|
||||||
|title_bar| {
|
|
||||||
title_bar
|
|
||||||
.children(self.render_project_branch(cx))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()),
|
|
||||||
)
|
|
||||||
.child(self.render_collaborator_list(window, cx))
|
|
||||||
.when(title_bar_settings.show_onboarding_banner, |title_bar| {
|
|
||||||
title_bar.child(self.banner.clone())
|
|
||||||
})
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.gap_1()
|
|
||||||
.pr_1()
|
|
||||||
.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
|
|
||||||
.children(self.render_call_controls(window, cx))
|
|
||||||
.map(|el| {
|
|
||||||
let status = self.client.status();
|
|
||||||
let status = &*status.borrow();
|
|
||||||
if matches!(status, client::Status::Connected { .. }) {
|
|
||||||
el.child(self.render_user_menu_button(cx))
|
|
||||||
} else {
|
|
||||||
el.children(self.render_connection_status(status, cx))
|
|
||||||
.when(TitleBarSettings::get_global(cx).show_sign_in, |el| {
|
|
||||||
el.child(self.render_sign_in_button(cx))
|
|
||||||
})
|
|
||||||
.child(self.render_user_menu_button(cx))
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.when(!window.is_fullscreen(), |title_bar| {
|
|
||||||
match self.platform_style {
|
|
||||||
PlatformStyle::Mac => title_bar,
|
|
||||||
PlatformStyle::Linux => {
|
|
||||||
if matches!(decorations, Decorations::Client { .. }) {
|
|
||||||
title_bar
|
title_bar
|
||||||
.child(platform_linux::LinuxWindowControls::new(close_action))
|
.when(title_bar_settings.show_project_items, |title_bar| {
|
||||||
.when(supported_controls.window_menu, |titlebar| {
|
title_bar
|
||||||
titlebar.on_mouse_down(
|
.children(self.render_project_host(cx))
|
||||||
gpui::MouseButton::Right,
|
.child(self.render_project_name(cx))
|
||||||
move |ev, window, _| window.show_window_menu(ev.position),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.on_mouse_move(cx.listener(move |this, _ev, window, _| {
|
.when(title_bar_settings.show_branch_name, |title_bar| {
|
||||||
if this.should_move {
|
title_bar.children(self.render_project_branch(cx))
|
||||||
this.should_move = false;
|
})
|
||||||
window.start_window_move();
|
})
|
||||||
}
|
})
|
||||||
}))
|
.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
|
||||||
.on_mouse_down_out(cx.listener(move |this, _ev, _window, _cx| {
|
.into_any_element(),
|
||||||
this.should_move = false;
|
);
|
||||||
}))
|
|
||||||
.on_mouse_up(
|
children.push(self.render_collaborator_list(window, cx).into_any_element());
|
||||||
gpui::MouseButton::Left,
|
|
||||||
cx.listener(move |this, _ev, _window, _cx| {
|
if title_bar_settings.show_onboarding_banner {
|
||||||
this.should_move = false;
|
children.push(self.banner.clone().into_any_element())
|
||||||
}),
|
}
|
||||||
)
|
|
||||||
.on_mouse_down(
|
children.push(
|
||||||
gpui::MouseButton::Left,
|
h_flex()
|
||||||
cx.listener(move |this, _ev, _window, _cx| {
|
.gap_1()
|
||||||
this.should_move = true;
|
.pr_1()
|
||||||
}),
|
.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
|
||||||
)
|
.children(self.render_call_controls(window, cx))
|
||||||
} else {
|
.map(|el| {
|
||||||
title_bar
|
let status = self.client.status();
|
||||||
}
|
let status = &*status.borrow();
|
||||||
|
if matches!(status, client::Status::Connected { .. }) {
|
||||||
|
el.child(self.render_user_menu_button(cx))
|
||||||
|
} else {
|
||||||
|
el.children(self.render_connection_status(status, cx))
|
||||||
|
.when(TitleBarSettings::get_global(cx).show_sign_in, |el| {
|
||||||
|
el.child(self.render_sign_in_button(cx))
|
||||||
|
})
|
||||||
|
.child(self.render_user_menu_button(cx))
|
||||||
}
|
}
|
||||||
PlatformStyle::Windows => {
|
})
|
||||||
title_bar.child(platform_windows::WindowsWindowControls::new(height))
|
.into_any_element(),
|
||||||
}
|
);
|
||||||
}
|
|
||||||
})
|
self.platform_titlebar.update(cx, |this, _| {
|
||||||
|
this.set_children(children);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.platform_titlebar.clone().into_any_element()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,13 +236,12 @@ impl TitleBar {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let platform_titlebar = cx.new(|_| PlatformTitleBar::new(id));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
platform_style,
|
platform_titlebar,
|
||||||
content: div().id(id.into()),
|
|
||||||
children: SmallVec::new(),
|
|
||||||
application_menu,
|
application_menu,
|
||||||
workspace: workspace.weak_handle(),
|
workspace: workspace.weak_handle(),
|
||||||
should_move: false,
|
|
||||||
project,
|
project,
|
||||||
user_store,
|
user_store,
|
||||||
client,
|
client,
|
||||||
|
@ -360,23 +250,6 @@ impl TitleBar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
pub fn height(window: &mut Window) -> Pixels {
|
|
||||||
(1.75 * window.rem_size()).max(px(34.))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub fn height(_window: &mut Window) -> Pixels {
|
|
||||||
// todo(windows) instead of hard coded size report the actual size to the Windows platform API
|
|
||||||
px(32.)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the platform style.
|
|
||||||
pub fn platform_style(mut self, style: PlatformStyle) -> Self {
|
|
||||||
self.platform_style = style;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_ssh_project_host(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
|
fn render_ssh_project_host(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
|
||||||
let options = self.project.read(cx).ssh_connection_options(cx)?;
|
let options = self.project.read(cx).ssh_connection_options(cx)?;
|
||||||
let host: SharedString = options.connection_string().into();
|
let host: SharedString = options.connection_string().into();
|
||||||
|
@ -796,17 +669,3 @@ impl TitleBar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue