T51: Ely stands inside the main window on its pane's bottom edge while her box would not fit above it, which the owner asked for after 0.1.4, where on a window at the screen's top or full screen she stuck between the menu bar and the terminal: each platform measures the room above the pane up to its display's work area, macOS's visibleFrame, Windows' rcWork, X11's _NET_WORKAREA, and on Wayland, which tells a client no place, she stands inside while the window is full screen, maximized or tiled at its top
CI / platforms (windows-latest) (push) Successful in 1m38s
CI / platforms (ubuntu-24.04) (push) Successful in 2m21s
CI / macos (push) Successful in 12m50s

This commit is contained in:
ZacharyZhang-NY committed 2026-10-19 01:55:59 -04:00
1 parent 7032285d13
commit 15ef5d52d0
10 files changed
+291 -98

No files matched your search

+94 -33
View File
@@ -1,4 +1,4 @@
//! Ely, the icon's girl, peeking over the main window's top edge from a window of her own.
//! Ely, the icon's girl, peeking over the main window's top edge from a window of her own, or up from its bottom edge where no room lies above.
mod look;
#[cfg(target_os = "macos")]
@@ -12,9 +12,10 @@ mod x11;
use std::{cell::RefCell, rc::Rc, time::Instant};
use gpui::{
App, AppContext as _, Bounds, Context, InteractiveElement, IntoElement, MouseButton,
ParentElement, Pixels, Render, Styled, Subscription, Window, WindowBackgroundAppearance,
WindowBounds, WindowHandle, WindowKind, WindowOptions, canvas, div, point, popup, px, size,
App, AppContext as _, Bounds, Context, Decorations, InteractiveElement, IntoElement,
MouseButton, ParentElement, Pixels, Render, Styled, Subscription, Window,
WindowBackgroundAppearance, WindowBounds, WindowHandle, WindowKind, WindowOptions, canvas, div,
point, popup, px, size,
};
#[cfg(target_os = "macos")]
@@ -36,6 +37,13 @@ const ROOM: f32 = 16.0;
/// How far in from the pane's left edge she holds its top edge.
const LEFT: f32 = 140.0;
/// The pane edge her bottom stands on: the top, or the bottom, inside the window.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Edge {
Top,
Bottom,
}
/// Ely in her window: she moves all the time, her acts follow the sessions' marks, and a press pets her.
pub struct Pet {
rig: Rc<Rig>,
@@ -48,8 +56,9 @@ pub struct Pet {
act: Act,
last: Option<Pose>,
eased: Option<(Pose, Instant)>,
/// How far in from the pane's left edge she opened.
/// How far in from the pane's left edge she opened, and the edge she stands on.
left: f32,
edge: Edge,
/// What keeps her window on the main window's edge, where the platform does not.
glue: Option<platform::Glue>,
_watch: Vec<Subscription>,
@@ -80,11 +89,15 @@ pub fn follow(main: WindowHandle<Workspace>, cx: &mut App) {
let Some(pet) = *shown.borrow() else {
return;
};
let at = left(pane(window).size.width);
if wayland(window) && pet.read(cx).is_ok_and(|pet| pet.left != at) {
let at = (left(pane(window).size.width), wayland_edge(window));
if wayland(window) && pet.read(cx).is_ok_and(|pet| (pet.left, pet.edge) != at) {
let shown = shown.clone();
cx.defer(move |cx| {
close(pet, "to stand where the new width puts her", cx);
close(
pet,
"to stand where the window's new size or state puts her",
cx,
);
*shown.borrow_mut() = open_pet(main, cx);
});
}
@@ -118,21 +131,47 @@ fn left(width: Pixels) -> f32 {
LEFT.min(f32::from(width) - WIDTH).max(0.0)
}
/// Opens her window on `main`'s top edge: a popup Wayland's compositor places, else a window kept there.
/// The edge she stands on with `room` points free above the pane, up to the top of the work area of the main window's display.
fn edge(room: f32) -> Edge {
if room >= HEAD + ROOM {
Edge::Top
} else {
Edge::Bottom
}
}
/// The edge she stands on over a Wayland window, whose place no client knows: the bottom while it is full screen, maximized or tiled at its top.
fn wayland_edge(window: &Window) -> Edge {
let tiled = matches!(window.window_decorations(), Decorations::Client { tiling } if tiling.top);
if tiled || window.is_fullscreen() || window.is_maximized() {
Edge::Bottom
} else {
Edge::Top
}
}
/// Opens her window on `main`'s pane: a popup Wayland's compositor places, else a window kept there.
fn show(main: WindowHandle<Workspace>, cx: &mut App) -> anyhow::Result<WindowHandle<Pet>> {
let (bounds, pane, wayland) = main.update(cx, |_, window, _| {
(window.bounds(), pane(window), wayland(window))
let (bounds, pane, wayland, edge) = main.update(cx, |_, window, _| {
let wayland = wayland(window);
let edge = if wayland {
wayland_edge(window)
} else {
Edge::Top
};
(window.bounds(), pane(window), wayland, edge)
})?;
let at = left(pane.size.width);
let room = size(px(WIDTH), px(HEAD + ROOM));
let kind = if wayland {
let (y, anchor) = match edge {
Edge::Top => (pane.origin.y, popup::PopupAnchor::TopLeft),
Edge::Bottom => (pane.bottom() - px(1.), popup::PopupAnchor::BottomLeft),
};
WindowKind::AnchoredPopup(popup::PopupOptions {
parent: main.into(),
anchor_rect: Bounds::new(
point(pane.origin.x + px(at), pane.origin.y),
size(px(1.), px(1.)),
),
anchor: popup::PopupAnchor::TopLeft,
anchor_rect: Bounds::new(point(pane.origin.x + px(at), y), size(px(1.), px(1.))),
anchor,
gravity: popup::PopupGravity::TopRight,
constraint_adjustment: popup::PopupConstraintAdjustment::empty(),
offset: point(px(0.), px(0.)),
@@ -183,29 +222,34 @@ fn show(main: WindowHandle<Workspace>, cx: &mut App) -> anyhow::Result<WindowHan
last: None,
eased: None,
left: at,
edge,
glue: None,
_watch: vec![feel],
}
})
})?;
if !wayland {
glue(main, pet, cx)?;
}
let way = if wayland {
"a Wayland popup"
let (way, edge) = if wayland {
("a Wayland popup", edge)
} else {
"a window kept there"
("a window kept there", glue(main, pet, cx)?)
};
log::info!("pet: Ely peeks over the main window, {way}, feeling {start:?}");
let edge = format!("{edge:?}").to_lowercase();
log::info!(
"pet: Ely peeks over the main window, {way}, on the pane's {edge} edge, feeling {start:?}"
);
Ok(pet)
}
/// Keeps her window on `main`'s top edge as it moves, comes forward and hides.
fn glue(main: WindowHandle<Workspace>, pet: WindowHandle<Pet>, cx: &mut App) -> anyhow::Result<()> {
/// Keeps her window on `main`'s pane as it moves, comes forward and hides, and gives the edge she first stands on.
fn glue(
main: WindowHandle<Workspace>,
pet: WindowHandle<Pet>,
cx: &mut App,
) -> anyhow::Result<Edge> {
main.update(cx, |_, main, cx| {
pet.update(cx, |pet, window, cx| {
let glue = platform::Glue::new(window, main)?;
glue.place(main)?;
pet.edge = glue.place(main)?;
pet.glue = Some(glue);
pet._watch
.push(cx.observe_window_bounds(main, |pet, main, _| pet.place(main)));
@@ -217,7 +261,7 @@ fn glue(main: WindowHandle<Workspace>, pet: WindowHandle<Pet>, cx: &mut App) ->
pet.shown(visibility.is_visible(), main)
}),
);
anyhow::Ok(())
anyhow::Ok(pet.edge)
})?
})?
}
@@ -290,16 +334,23 @@ impl Pet {
pose
}
fn place(&self, main: &Window) {
if let Some(glue) = &self.glue
&& let Err(error) = glue.place(main)
{
log::error!("{error:#}");
fn place(&mut self, main: &Window) {
let Some(glue) = &self.glue else {
return;
};
match glue.place(main) {
Ok(edge) if edge != self.edge => {
let name = format!("{edge:?}").to_lowercase();
log::info!("pet: Ely moves to the pane's {name} edge");
self.edge = edge;
}
Ok(_) => {}
Err(error) => log::error!("{error:#}"),
}
}
#[cfg(target_os = "linux")]
fn shown(&self, visible: bool, main: &Window) {
fn shown(&mut self, visible: bool, main: &Window) {
if let Some(glue) = &self.glue {
match glue.shown(visible) {
Ok(()) => log::debug!("pet: Ely shows {visible} with the main window"),
@@ -353,6 +404,7 @@ mod tests {
last: None,
eased: None,
left: LEFT,
edge: Edge::Top,
glue: None,
_watch: Vec::new(),
}
@@ -411,6 +463,15 @@ mod tests {
);
}
#[test]
fn she_stands_on_the_top_edge_while_her_box_fits_above_it() {
assert_eq!(edge(HEAD + ROOM), Edge::Top, "just room for her");
assert_eq!(edge(400.0), Edge::Top);
assert_eq!(edge(HEAD + ROOM - 0.5), Edge::Bottom, "half a point short");
assert_eq!(edge(0.0), Edge::Bottom, "a window at the work area's top");
assert_eq!(edge(-30.0), Edge::Bottom, "a window above it");
}
#[test]
fn she_holds_the_edge_of_any_pane() {
assert_eq!(left(px(1200.)), LEFT);
+13 -5
View File
@@ -9,7 +9,7 @@ use objc::{
};
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
use super::left;
use super::{Edge, edge, left};
/// AppKit's `NSWindowAbove`.
const ABOVE: isize = 1;
@@ -51,17 +51,25 @@ impl Glue {
Ok(Self { pet, main })
}
/// Stands her on the main window's top edge, as far in as its width allows.
pub fn place(&self, _: &Window) -> anyhow::Result<()> {
/// Stands her on the main window's top edge while her box fits under the top of its screen's visible frame, else on its bottom edge, as far in as its width allows, and gives the edge.
pub fn place(&self, _: &Window) -> anyhow::Result<Edge> {
unsafe {
let frame: Rect = msg_send![self.main, frame];
let screen: *mut Object = msg_send![self.main, screen];
anyhow::ensure!(!screen.is_null(), "pet: the main window is on no screen");
let visible: Rect = msg_send![screen, visibleFrame];
let top = frame.origin.y + frame.height;
let edge = edge((visible.origin.y + visible.height - top) as f32);
let at = Point {
x: frame.origin.x + f64::from(left(gpui::px(frame.width as f32))),
y: frame.origin.y + frame.height,
y: match edge {
Edge::Top => top,
Edge::Bottom => frame.origin.y,
},
};
let _: () = msg_send![self.pet, setFrameOrigin: at];
Ok(edge)
}
Ok(())
}
}
+30 -15
View File
@@ -1,4 +1,4 @@
//! Ely's window on Windows: a bare popup owned by the main window, so it stays above it and hides with it, sized and moved as it moves.
//! Ely's window on Windows: a bare popup owned by the main window, so it stays above it and hides with it, sized and moved onto an edge of its pane as it moves.
use std::{
cell::Cell,
@@ -10,7 +10,9 @@ use gpui::{Bounds, Pixels, Window};
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
use windows_sys::Win32::{
Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM},
Graphics::Gdi::{MONITOR_DEFAULTTONEAREST, MonitorFromRect},
Graphics::Gdi::{
GetMonitorInfoW, MONITOR_DEFAULTTONEAREST, MONITORINFO, MonitorFromRect, MonitorFromWindow,
},
UI::{
HiDpi::{GetDpiForMonitor, GetDpiForWindow, MDT_EFFECTIVE_DPI},
WindowsAndMessaging::{
@@ -23,7 +25,7 @@ use windows_sys::Win32::{
},
};
use super::{HEAD, ROOM, WIDTH, left, pane};
use super::{Edge, HEAD, ROOM, WIDTH, edge, left, pane};
#[cfg(test)]
mod tests;
@@ -75,33 +77,46 @@ impl Glue {
Ok(Self { pet, main })
}
/// Stands her on the main window's top edge, as far in as its width allows.
pub fn place(&self, main: &Window) -> anyhow::Result<()> {
/// Stands her on the main window's top edge while her box fits under the top of its display's work area, else on its bottom edge, as far in as its width allows, and gives the edge.
pub fn place(&self, main: &Window) -> anyhow::Result<Edge> {
self.put(main.scale_factor(), pane(main))
}
/// Posts her bottom left corner on `pane` at `scale`, in the main window's points; her window sizes her at the DPI it has when the place lands, as gpui did not when it opened her off the display.
fn put(&self, scale: f32, pane: Bounds<Pixels>) -> anyhow::Result<()> {
/// Posts her bottom left corner on an edge of `pane` at `scale`, in the main window's points; her window sizes her at the DPI it has when the place lands, as gpui did not when it opened her off the display.
fn put(&self, scale: f32, pane: Bounds<Pixels>) -> anyhow::Result<Edge> {
let mut main = WINDOWINFO {
cbSize: size_of::<WINDOWINFO>() as u32,
..Default::default()
};
let read = unsafe { GetWindowInfo(self.main, &mut main) };
anyhow::ensure!(read != 0, "pet: the main window's place does not read");
let corner = stand(main.rcClient, scale, pane);
let mut display = MONITORINFO {
cbSize: size_of::<MONITORINFO>() as u32,
..Default::default()
};
let monitor = unsafe { MonitorFromWindow(self.main, MONITOR_DEFAULTTONEAREST) };
let read = unsafe { GetMonitorInfoW(monitor, &mut display) };
anyhow::ensure!(read != 0, "pet: the main window's display does not read");
let (_, top) = stand(main.rcClient, scale, pane, Edge::Top);
let edge = edge((top - display.rcWork.top) as f32 / scale);
let corner = stand(main.rcClient, scale, pane, edge);
let posted = unsafe { PostMessageW(self.pet, PLACE, pack(corner), 0) };
anyhow::ensure!(posted != 0, "pet: her place does not post");
log::debug!("pet: Ely is to stand on {corner:?}");
Ok(())
log::debug!("pet: Ely is to stand on {corner:?}, the pane's {edge:?} edge");
Ok(edge)
}
}
/// Her bottom left corner on `pane` at `scale` over the main window's `client` rect, in pixels.
fn stand(client: RECT, scale: f32, pane: Bounds<Pixels>) -> (i32, i32) {
let (x, y) = (f32::from(pane.origin.x), f32::from(pane.origin.y));
/// Her bottom left corner on `edge` of `pane` at `scale` over the main window's `client` rect, in pixels.
fn stand(client: RECT, scale: f32, pane: Bounds<Pixels>, edge: Edge) -> (i32, i32) {
let y = match edge {
Edge::Top => pane.origin.y,
Edge::Bottom => pane.bottom(),
};
let x = f32::from(pane.origin.x) + left(pane.size.width);
(
client.left + ((x + left(pane.size.width)) * scale).round() as i32,
client.top + (y * scale).round() as i32,
client.left + (x * scale).round() as i32,
client.top + (f32::from(y) * scale).round() as i32,
)
}
+57 -21
View File
@@ -42,6 +42,28 @@ fn deliver(window: HWND) {
}
}
/// Moves `window` to `(x, y)`, 800 by 600.
fn move_main(window: HWND, (x, y): (i32, i32)) {
let flags = SWP_NOZORDER | SWP_NOACTIVATE;
let moved = unsafe { SetWindowPos(window, std::ptr::null_mut(), x, y, 800, 600, flags) };
assert_ne!(moved, 0, "the main window moves");
}
/// The work area of the display under most of `window`.
fn work_area(window: HWND) -> RECT {
let mut display = MONITORINFO {
cbSize: size_of::<MONITORINFO>() as u32,
..Default::default()
};
let monitor = unsafe { MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST) };
assert_ne!(
unsafe { GetMonitorInfoW(monitor, &mut display) },
0,
"its display"
);
display.rcWork
}
fn info(window: HWND) -> WINDOWINFO {
let mut info = WINDOWINFO {
cbSize: size_of::<WINDOWINFO>() as u32,
@@ -70,7 +92,7 @@ unsafe extern "system" fn standin(
}
#[test]
fn her_bottom_stands_on_the_panes_top_edge_whatever_the_two_windows_dpi() {
fn her_bottom_stands_on_either_edge_of_the_pane_whatever_the_two_windows_dpi() {
let client = RECT {
left: 100,
top: 20,
@@ -79,24 +101,26 @@ fn her_bottom_stands_on_the_panes_top_edge_whatever_the_two_windows_dpi() {
};
let pane = Bounds::new(point(px(10.), px(8.)), size(px(800.), px(600.)));
for (scale, dpi) in [(1.0, 96), (1.0, 144), (1.5, 96), (2.0, 120)] {
let her = boxed(stand(client, scale, pane), dpi);
let own = dpi as f32 / 96.0;
let own_box = (
(WIDTH * own).round() as i32,
((HEAD + ROOM) * own).round() as i32,
);
let corner = (
100 + ((10.0 + left(px(800.))) * scale).round() as i32,
20 + (8.0 * scale).round() as i32,
);
assert_eq!(
(
(her.right - her.left, her.bottom - her.top),
(her.left, her.bottom)
),
(own_box, corner),
"the main window at {scale}, hers at {dpi} DPI"
);
for (edge, y) in [(Edge::Top, 8.0), (Edge::Bottom, 608.0)] {
let her = boxed(stand(client, scale, pane, edge), dpi);
let own = dpi as f32 / 96.0;
let own_box = (
(WIDTH * own).round() as i32,
((HEAD + ROOM) * own).round() as i32,
);
let corner = (
100 + ((10.0 + left(px(800.))) * scale).round() as i32,
20 + (y * scale).round() as i32,
);
assert_eq!(
(
(her.right - her.left, her.bottom - her.top),
(her.left, her.bottom)
),
(own_box, corner),
"the main window at {scale}, hers at {dpi} DPI, on the {edge:?} edge"
);
}
}
}
@@ -234,7 +258,7 @@ fn places_that_pile_up_across_two_displays_drain_in_her_dpis_size() {
}
#[test]
fn the_large_framed_popup_gpui_makes_stands_bare_in_her_box_on_the_panes_top_edge() {
fn the_large_framed_popup_gpui_makes_stands_bare_in_her_box_on_an_edge_of_the_pane() {
let main = window(WS_OVERLAPPEDWINDOW, 0);
// gpui's popup: style 0, an overlapped window, which Windows gives a caption.
let pet = window(0, WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
@@ -245,8 +269,10 @@ fn the_large_framed_popup_gpui_makes_stands_bare_in_her_box_on_the_panes_top_edg
);
unsafe { SetWindowLongPtrW(pet, GWLP_WNDPROC, standin as *const () as isize) };
let glue = Glue::hold(pet, main).expect("held");
let work = work_area(main);
move_main(main, (work.left + 200, work.top + 400));
let pane = Bounds::new(point(px(0.), px(0.)), size(px(380.), px(280.)));
glue.put(1.0, pane).expect("placed");
let roomy = glue.put(1.0, pane).expect("placed");
let made = info(pet).rcWindow;
deliver(pet);
let (her, under) = (info(pet), info(main));
@@ -269,6 +295,11 @@ fn the_large_framed_popup_gpui_makes_stands_bare_in_her_box_on_the_panes_top_edg
};
let (_, _, kept) = change(display);
let (suggested, taken, stood) = change(display + 48);
// At the work area's top the main window leaves her no room above it.
move_main(main, (work.left + 200, work.top));
let cramped = glue.put(1.0, pane).expect("placed again");
deliver(pet);
let (inside, low) = (info(pet).rcWindow, info(main).rcClient);
unsafe {
DestroyWindow(pet);
DestroyWindow(main);
@@ -311,4 +342,9 @@ fn the_large_framed_popup_gpui_makes_stands_bare_in_her_box_on_the_panes_top_edg
own_box,
"no size until her window takes the posted place"
);
assert_eq!(
(roomy, cramped, inside.left, inside.bottom),
(Edge::Top, Edge::Bottom, low.left + 140, low.top + 280),
"with no room above, inside on the pane's bottom edge"
);
}
+82 -20
View File
@@ -1,20 +1,21 @@
//! Ely's window on X11: gpui's popup, which no window manager places, put on the main window's top edge and just above it.
//! Ely's window on X11: gpui's popup, which no window manager places, put on an edge of the main window's pane and just above it.
use anyhow::{Context as _, bail};
use gpui::{Bounds, Pixels, Window};
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
use x11rb::{
protocol::xproto::{ConfigureWindowAux, ConnectionExt as _, StackMode},
protocol::xproto::{Atom, AtomEnum, ConfigureWindowAux, ConnectionExt as _, StackMode},
rust_connection::RustConnection,
};
use super::{HEAD, ROOM, left, pane};
use super::{Edge, HEAD, ROOM, edge, left, pane};
/// Her X window, the main one, and a connection of her own to their server.
/// Her X window, the main one, a connection of her own to their server, and the atom of the work area its window manager names.
pub struct Glue {
connection: RustConnection,
pet: u32,
main: u32,
workarea: Atom,
}
impl Glue {
@@ -22,35 +23,67 @@ impl Glue {
let (pet, main) = (native(pet)?, native(main)?);
// gpui opens $DISPLAY too; a window's place belongs to whoever asks.
let (connection, _) = x11rb::connect(None).context("pet: no X server")?;
Ok(Self {
let workarea = (connection.intern_atom(false, b"_NET_WORKAREA"))?
.reply()
.context("pet: the work area's atom")?
.atom;
let glue = Self {
connection,
pet,
main,
})
workarea,
};
match glue.work_top(glue.frame()?.0)? {
Some(top) => log::info!("pet: the work area starts {top} pixels down the screen"),
None => log::info!(
"pet: the window manager names no work area; the screen's top bounds her"
),
}
Ok(glue)
}
/// Stands her on the main window's pane, as far in as its width allows, stacked just above the main window's frame.
pub fn place(&self, main: &Window) -> anyhow::Result<()> {
/// Stands her on the main window's pane, on its top edge while her box fits under the top of the work area, else on its bottom edge, as far in as its width allows, stacked just above the main window's frame, and gives the edge.
pub fn place(&self, main: &Window) -> anyhow::Result<Edge> {
self.put(main.scale_factor(), pane(main))
}
/// Places her at `scale` over `pane`, in the main window's points.
fn put(&self, scale: f32, pane: Bounds<Pixels>) -> anyhow::Result<()> {
let (x, y) = (f32::from(pane.origin.x), f32::from(pane.origin.y));
/// Places her at `scale` on an edge of `pane`, in the main window's points.
fn put(&self, scale: f32, pane: Bounds<Pixels>) -> anyhow::Result<Edge> {
let (root, frame) = self.frame()?;
let at = (self.connection)
.translate_coordinates(self.main, root, 0, 0)?
.reply()
.context("pet: the main window's place")?;
let down = |y: Pixels| i32::from(at.dst_y) + (f32::from(y) * scale).round() as i32;
let room = down(pane.origin.y) - self.work_top(root)?.unwrap_or(0);
let edge = edge(room as f32 / scale);
let bottom = down(match edge {
Edge::Top => pane.origin.y,
Edge::Bottom => pane.bottom(),
});
let x = f32::from(pane.origin.x) + left(pane.size.width);
let aux = ConfigureWindowAux::new()
.x(i32::from(at.dst_x) + ((x + left(pane.size.width)) * scale).round() as i32)
.y(i32::from(at.dst_y) + ((y - HEAD - ROOM) * scale).round() as i32)
.x(i32::from(at.dst_x) + (x * scale).round() as i32)
.y(bottom - ((HEAD + ROOM) * scale).round() as i32)
.sibling(frame)
.stack_mode(StackMode::ABOVE);
(self.connection)
.configure_window(self.pet, &aux)?
.check()
.context("pet: the X server did not move her window")
.context("pet: the X server did not move her window")?;
Ok(edge)
}
/// The top of the work area the window manager names in `_NET_WORKAREA`, its first desktop's, which managers keep alike; none where it names none.
fn work_top(&self, root: u32) -> anyhow::Result<Option<i32>> {
let reply = (self.connection)
.get_property(false, root, self.workarea, AtomEnum::CARDINAL, 0, 4)?
.reply()
.context("pet: the work area")?;
Ok(reply
.value32()
.and_then(|mut area| area.nth(1))
.map(|top| top as i32))
}
/// Maps her window while the main one shows, and unmaps it while that hides.
@@ -93,7 +126,8 @@ mod tests {
use x11rb::{
COPY_DEPTH_FROM_PARENT,
connection::Connection as _,
protocol::xproto::{CreateWindowAux, MapState, WindowClass},
protocol::xproto::{CreateWindowAux, MapState, PropMode, WindowClass},
wrapper::ConnectionExt as _,
};
use gpui::{point, px, size};
@@ -102,7 +136,7 @@ mod tests {
use crate::testx11::Xvfb;
#[test]
fn she_stands_on_the_pane_just_above_the_frame_and_hides_with_it() {
fn she_stands_on_an_edge_of_the_pane_just_above_the_frame_and_hides_with_it() {
let xvfb = Xvfb::start();
let (connection, screen) = x11rb::connect(Some(&xvfb.display)).expect("the test connects");
let root = connection.setup().roots[screen].root;
@@ -140,17 +174,23 @@ mod tests {
&CreateWindowAux::new().override_redirect(1),
);
// A window manager's frame, its title bar 20 pixels tall, round the main window.
let frame = window(root, (100, 80, 800, 620), &plain);
let frame = window(root, (100, 400, 800, 620), &plain);
let main = window(frame, (0, 20, 800, 600), &plain);
window(root, (0, 0, 50, 50), &plain);
let hers = x11rb::connect(Some(&xvfb.display)).expect("hers").0;
let workarea = (hers.intern_atom(false, b"_NET_WORKAREA"))
.expect("the request")
.reply()
.expect("the atom")
.atom;
let glue = Glue {
connection: hers,
pet,
main,
workarea,
};
let pane = Bounds::new(point(px(10.), px(10.)), size(px(380.), px(280.)));
glue.put(2.0, pane).expect("she is placed");
let roomy = glue.put(2.0, pane).expect("she is placed");
let check = x11rb::connect(Some(&xvfb.display))
.expect("the check connects")
.0;
@@ -159,11 +199,15 @@ mod tests {
.expect("the request")
.reply()
.expect("her place");
let tall = ((HEAD + ROOM) * 2.0).round() as i32;
let stands = (
100 + ((10.0 + left(px(380.))) * 2.0).round() as i32,
100 + ((10.0 - HEAD - ROOM) * 2.0).round() as i32,
440 - tall,
);
assert_eq!(
(roomy, i32::from(at.x), i32::from(at.y)),
(Edge::Top, stands.0, stands.1)
);
assert_eq!((i32::from(at.x), i32::from(at.y)), stands);
let order = check
.query_tree(root)
.expect("the request")
@@ -193,5 +237,23 @@ mod tests {
assert_eq!(mapped(&check), MapState::UNMAPPED);
glue.shown(true).expect("shown");
assert_ne!(mapped(&check), MapState::UNMAPPED);
// A panel reserves the screen's top 300 pixels, so the pane's top lies 70 points under it.
let area = [0, 300, 1024, 724];
(check.change_property32(PropMode::REPLACE, root, workarea, AtomEnum::CARDINAL, &area))
.expect("the request")
.check()
.expect("a work area");
let cramped = glue.put(2.0, pane).expect("she is placed again");
let low = check
.get_geometry(pet)
.expect("the request")
.reply()
.expect("her place");
let inside = 420 + ((10.0 + 280.0) * 2.0_f32).round() as i32 - tall;
assert_eq!(
(cramped, i32::from(low.x), i32::from(low.y)),
(Edge::Bottom, stands.0, inside),
"with no room above, inside on the pane's bottom edge"
);
}
}