This PR adds support for full client side decorations on X11 and Wayland TODO: - [x] Adjust GPUI APIs to expose CSD related information - [x] Implement remaining CSD features (Resizing, window border, window shadow) - [x] Integrate with existing background appearance and window transparency - [x] Figure out how to check if the window is tiled on X11 - [x] Implement in Zed - [x] Repeatedly maximizing and unmaximizing can panic - [x] Resizing is strangely slow - [x] X11 resizing and movement doesn't work for this: https://discord.com/channels/869392257814519848/1204679850208657418/1256816908519604305 - [x] The top corner can clip with current styling - [x] Pressing titlebar buttons doesn't work - [x] Not showing maximize / unmaximize buttons - [x] Noisy transparency logs / surface transparency problem https://github.com/zed-industries/zed/pull/13611#issuecomment-2201685030 - [x] Strange offsets when dragging the project panel https://github.com/zed-industries/zed/pull/13611#pullrequestreview-2154606261 - [x] Shadow inset with `_GTK_FRAME_EXTENTS` doesn't respect tiling on X11 (observe by snapping an X11 window in any direction) Release Notes: - N/A --------- Co-authored-by: conrad <conrad@zed.dev> Co-authored-by: Owen Law <81528246+someone13574@users.noreply.github.com> Co-authored-by: apricotbucket28 <71973804+apricotbucket28@users.noreply.github.com> Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
165 lines
4.5 KiB
Rust
165 lines
4.5 KiB
Rust
use gpui::{svg, Action, Hsla};
|
|
use ui::prelude::*;
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
|
pub enum WindowControlType {
|
|
Minimize,
|
|
Restore,
|
|
Maximize,
|
|
Close,
|
|
}
|
|
|
|
impl WindowControlType {
|
|
/// Returns the icon name for the window control type.
|
|
///
|
|
/// Will take a [PlatformStyle] in the future to return a different
|
|
/// icon name based on the platform.
|
|
pub fn icon(&self) -> IconName {
|
|
match self {
|
|
WindowControlType::Minimize => IconName::GenericMinimize,
|
|
WindowControlType::Restore => IconName::GenericRestore,
|
|
WindowControlType::Maximize => IconName::GenericMaximize,
|
|
WindowControlType::Close => IconName::GenericClose,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(unused)]
|
|
pub struct WindowControlStyle {
|
|
background: Hsla,
|
|
background_hover: Hsla,
|
|
icon: Hsla,
|
|
icon_hover: Hsla,
|
|
}
|
|
|
|
impl WindowControlStyle {
|
|
pub fn default(cx: &WindowContext) -> Self {
|
|
let colors = cx.theme().colors();
|
|
|
|
Self {
|
|
background: colors.ghost_element_background,
|
|
background_hover: colors.ghost_element_hover,
|
|
icon: colors.icon,
|
|
icon_hover: colors.icon_muted,
|
|
}
|
|
}
|
|
|
|
#[allow(unused)]
|
|
/// Sets the background color of the control.
|
|
pub fn background(mut self, color: impl Into<Hsla>) -> Self {
|
|
self.background = color.into();
|
|
self
|
|
}
|
|
|
|
#[allow(unused)]
|
|
/// Sets the background color of the control when hovered.
|
|
pub fn background_hover(mut self, color: impl Into<Hsla>) -> Self {
|
|
self.background_hover = color.into();
|
|
self
|
|
}
|
|
|
|
#[allow(unused)]
|
|
/// Sets the color of the icon.
|
|
pub fn icon(mut self, color: impl Into<Hsla>) -> Self {
|
|
self.icon = color.into();
|
|
self
|
|
}
|
|
|
|
#[allow(unused)]
|
|
/// Sets the color of the icon when hovered.
|
|
pub fn icon_hover(mut self, color: impl Into<Hsla>) -> Self {
|
|
self.icon_hover = color.into();
|
|
self
|
|
}
|
|
}
|
|
|
|
#[derive(IntoElement)]
|
|
pub struct WindowControl {
|
|
id: ElementId,
|
|
icon: WindowControlType,
|
|
style: WindowControlStyle,
|
|
close_action: Option<Box<dyn Action>>,
|
|
}
|
|
|
|
impl WindowControl {
|
|
pub fn new(id: impl Into<ElementId>, icon: WindowControlType, cx: &WindowContext) -> Self {
|
|
let style = WindowControlStyle::default(cx);
|
|
|
|
Self {
|
|
id: id.into(),
|
|
icon,
|
|
style,
|
|
close_action: None,
|
|
}
|
|
}
|
|
|
|
pub fn new_close(
|
|
id: impl Into<ElementId>,
|
|
icon: WindowControlType,
|
|
close_action: Box<dyn Action>,
|
|
cx: &WindowContext,
|
|
) -> Self {
|
|
let style = WindowControlStyle::default(cx);
|
|
|
|
Self {
|
|
id: id.into(),
|
|
icon,
|
|
style,
|
|
close_action: Some(close_action.boxed_clone()),
|
|
}
|
|
}
|
|
|
|
#[allow(unused)]
|
|
pub fn custom_style(
|
|
id: impl Into<ElementId>,
|
|
icon: WindowControlType,
|
|
style: WindowControlStyle,
|
|
) -> Self {
|
|
Self {
|
|
id: id.into(),
|
|
icon,
|
|
style,
|
|
close_action: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl RenderOnce for WindowControl {
|
|
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
|
let icon = svg()
|
|
.size_4()
|
|
.flex_none()
|
|
.path(self.icon.icon().path())
|
|
.text_color(self.style.icon)
|
|
.group_hover("", |this| this.text_color(self.style.icon_hover));
|
|
|
|
h_flex()
|
|
.id(self.id)
|
|
.group("")
|
|
.cursor_pointer()
|
|
.justify_center()
|
|
.content_center()
|
|
.rounded_2xl()
|
|
.w_5()
|
|
.h_5()
|
|
.hover(|this| this.bg(self.style.background_hover))
|
|
.active(|this| this.bg(self.style.background_hover))
|
|
.child(icon)
|
|
.on_mouse_move(|_, cx| cx.stop_propagation())
|
|
.on_click(move |_, cx| {
|
|
cx.stop_propagation();
|
|
match self.icon {
|
|
WindowControlType::Minimize => cx.minimize_window(),
|
|
WindowControlType::Restore => cx.zoom_window(),
|
|
WindowControlType::Maximize => cx.zoom_window(),
|
|
WindowControlType::Close => cx.dispatch_action(
|
|
self.close_action
|
|
.as_ref()
|
|
.expect("Use WindowControl::new_close() for close control.")
|
|
.boxed_clone(),
|
|
),
|
|
}
|
|
})
|
|
}
|
|
}
|