Release Notes: - N/A > Any user interaction that is equivalent to a click, such as pressing the Space key or Enter key while the element is focused. Note that this only applies to elements with a default key event handler, and therefore, excludes other elements that have been made focusable by setting the [tabindex](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/tabindex) attribute. https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event --------- Co-authored-by: Anthony <anthony@zed.dev> Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com> Co-authored-by: Umesh Yadav <23421535+imumesh18@users.noreply.github.com>
174 lines
6.8 KiB
Rust
174 lines
6.8 KiB
Rust
use gpui::{
|
|
AnyElement, Context, Decorations, Hsla, 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 title_bar_color(&self, window: &mut Window, cx: &mut Context<Self>) -> Hsla {
|
|
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
|
|
}
|
|
}
|
|
|
|
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 = self.title_bar_color(window, cx);
|
|
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.click_count() == 2 {
|
|
window.titlebar_double_click();
|
|
}
|
|
})
|
|
})
|
|
.when(self.platform_style == PlatformStyle::Linux, |this| {
|
|
this.on_click(|event, window, _| {
|
|
if event.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)
|
|
}
|
|
}
|