Detailed changes
@@ -1268,6 +1268,9 @@ pub enum WindowKind {
/// A window that appears above all other windows, usually used for alerts or popups
/// use sparingly!
PopUp,
+
+ /// A floating window that appears on top of its parent window
+ Floating,
}
/// The appearance of the window, as defined by the operating system.
@@ -695,6 +695,8 @@ impl LinuxClient for WaylandClient {
) -> anyhow::Result<Box<dyn PlatformWindow>> {
let mut state = self.0.borrow_mut();
+ let parent = state.keyboard_focused_window.as_ref().map(|w| w.toplevel());
+
let (window, surface_id) = WaylandWindow::new(
handle,
state.globals.clone(),
@@ -702,6 +704,7 @@ impl LinuxClient for WaylandClient {
WaylandClientStatePtr(Rc::downgrade(&self.0)),
params,
state.common.appearance,
+ parent,
)?;
state.windows.insert(surface_id, window.0.clone());
@@ -14,14 +14,16 @@ use raw_window_handle as rwh;
use wayland_backend::client::ObjectId;
use wayland_client::WEnum;
use wayland_client::{Proxy, protocol::wl_surface};
-use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1;
use wayland_protocols::wp::viewporter::client::wp_viewport;
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;
use wayland_protocols::xdg::shell::client::xdg_surface;
use wayland_protocols::xdg::shell::client::xdg_toplevel::{self};
+use wayland_protocols::{
+ wp::fractional_scale::v1::client::wp_fractional_scale_v1,
+ xdg::shell::client::xdg_toplevel::XdgToplevel,
+};
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur;
-use crate::scene::Scene;
use crate::{
AnyWindowHandle, Bounds, Decorations, Globals, GpuSpecs, Modifiers, Output, Pixels,
PlatformDisplay, PlatformInput, Point, PromptButton, PromptLevel, RequestFrameOptions,
@@ -36,6 +38,7 @@ use crate::{
linux::wayland::{display::WaylandDisplay, serial::SerialKind},
},
};
+use crate::{WindowKind, scene::Scene};
#[derive(Default)]
pub(crate) struct Callbacks {
@@ -276,6 +279,7 @@ impl WaylandWindow {
client: WaylandClientStatePtr,
params: WindowParams,
appearance: WindowAppearance,
+ parent: Option<XdgToplevel>,
) -> anyhow::Result<(Self, ObjectId)> {
let surface = globals.compositor.create_surface(&globals.qh, ());
let xdg_surface = globals
@@ -283,6 +287,10 @@ impl WaylandWindow {
.get_xdg_surface(&surface, &globals.qh, surface.id());
let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());
+ if params.kind == WindowKind::Floating {
+ toplevel.set_parent(parent.as_ref());
+ }
+
if let Some(size) = params.window_min_size {
toplevel.set_min_size(size.width.0 as i32, size.height.0 as i32);
}
@@ -337,6 +345,10 @@ impl WaylandWindowStatePtr {
self.state.borrow().surface.clone()
}
+ pub fn toplevel(&self) -> xdg_toplevel::XdgToplevel {
+ self.state.borrow().toplevel.clone()
+ }
+
pub fn ptr_eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.state, &other.state)
}
@@ -1448,6 +1448,10 @@ impl LinuxClient for X11Client {
params: WindowParams,
) -> anyhow::Result<Box<dyn PlatformWindow>> {
let mut state = self.0.borrow_mut();
+ let parent_window = state
+ .keyboard_focused_window
+ .and_then(|focused_window| state.windows.get(&focused_window))
+ .map(|window| window.window.x_window);
let x_window = state
.xcb_connection
.generate_id()
@@ -1466,6 +1470,7 @@ impl LinuxClient for X11Client {
&state.atoms,
state.scale_factor,
state.common.appearance,
+ parent_window,
)?;
check_reply(
|| "Failed to set XdndAware property",
@@ -57,6 +57,7 @@ x11rb::atom_manager! {
WM_PROTOCOLS,
WM_DELETE_WINDOW,
WM_CHANGE_STATE,
+ WM_TRANSIENT_FOR,
_NET_WM_PID,
_NET_WM_NAME,
_NET_WM_STATE,
@@ -72,6 +73,7 @@ x11rb::atom_manager! {
_NET_WM_MOVERESIZE,
_NET_WM_WINDOW_TYPE,
_NET_WM_WINDOW_TYPE_NOTIFICATION,
+ _NET_WM_WINDOW_TYPE_DIALOG,
_NET_WM_SYNC,
_NET_SUPPORTED,
_MOTIF_WM_HINTS,
@@ -392,6 +394,7 @@ impl X11WindowState {
atoms: &XcbAtoms,
scale_factor: f32,
appearance: WindowAppearance,
+ parent_window: Option<xproto::Window>,
) -> anyhow::Result<Self> {
let x_screen_index = params
.display_id
@@ -529,6 +532,7 @@ impl X11WindowState {
),
)?;
}
+
if params.kind == WindowKind::PopUp {
check_reply(
|| "X11 ChangeProperty32 setting window type for pop-up failed.",
@@ -542,6 +546,38 @@ impl X11WindowState {
)?;
}
+ if params.kind == WindowKind::Floating {
+ if let Some(parent_window) = parent_window {
+ // WM_TRANSIENT_FOR hint indicating the main application window. For floating windows, we set
+ // a parent window (WM_TRANSIENT_FOR) such that the window manager knows where to
+ // place the floating window in relation to the main window.
+ // https://specifications.freedesktop.org/wm-spec/1.4/ar01s05.html
+ check_reply(
+ || "X11 ChangeProperty32 setting WM_TRANSIENT_FOR for floating window failed.",
+ xcb.change_property32(
+ xproto::PropMode::REPLACE,
+ x_window,
+ atoms.WM_TRANSIENT_FOR,
+ xproto::AtomEnum::WINDOW,
+ &[parent_window],
+ ),
+ )?;
+ }
+
+ // _NET_WM_WINDOW_TYPE_DIALOG indicates that this is a dialog (floating) window
+ // https://specifications.freedesktop.org/wm-spec/1.4/ar01s05.html
+ check_reply(
+ || "X11 ChangeProperty32 setting window type for floating window failed.",
+ xcb.change_property32(
+ xproto::PropMode::REPLACE,
+ x_window,
+ atoms._NET_WM_WINDOW_TYPE,
+ xproto::AtomEnum::ATOM,
+ &[atoms._NET_WM_WINDOW_TYPE_DIALOG],
+ ),
+ )?;
+ }
+
check_reply(
|| "X11 ChangeProperty32 setting protocols failed.",
xcb.change_property32(
@@ -737,6 +773,7 @@ impl X11Window {
atoms: &XcbAtoms,
scale_factor: f32,
appearance: WindowAppearance,
+ parent_window: Option<xproto::Window>,
) -> anyhow::Result<Self> {
let ptr = X11WindowStatePtr {
state: Rc::new(RefCell::new(X11WindowState::new(
@@ -752,6 +789,7 @@ impl X11Window {
atoms,
scale_factor,
appearance,
+ parent_window,
)?)),
callbacks: Rc::new(RefCell::new(Callbacks::default())),
xcb: xcb.clone(),
@@ -618,7 +618,7 @@ impl MacWindow {
}
let native_window: id = match kind {
- WindowKind::Normal => msg_send![WINDOW_CLASS, alloc],
+ WindowKind::Normal | WindowKind::Floating => msg_send![WINDOW_CLASS, alloc],
WindowKind::PopUp => {
style_mask |= NSWindowStyleMaskNonactivatingPanel;
msg_send![PANEL_CLASS, alloc]
@@ -776,7 +776,7 @@ impl MacWindow {
native_window.makeFirstResponder_(native_view);
match kind {
- WindowKind::Normal => {
+ WindowKind::Normal | WindowKind::Floating => {
native_window.setLevel_(NSNormalWindowLevel);
native_window.setAcceptsMouseMovedEvents_(YES);
@@ -136,6 +136,7 @@ pub fn open_rules_library(
window_background: cx.theme().window_background_appearance(),
window_decorations: Some(window_decorations),
window_min_size: Some(size(px(800.), px(600.))), // 4:3 Aspect Ratio
+ kind: gpui::WindowKind::Floating,
..Default::default()
},
|window, cx| {
@@ -484,7 +484,7 @@ pub fn open_settings_editor(
}),
focus: true,
show: true,
- kind: gpui::WindowKind::Normal,
+ kind: gpui::WindowKind::Floating,
window_background: cx.theme().window_background_appearance(),
window_min_size: Some(size(px(900.), px(750.))), // 4:3 Aspect Ratio
window_bounds: Some(WindowBounds::centered(size(px(900.), px(750.)), cx)),