T55: a drag moves Ely along the edge she stands on, which the owner asked for after 0.1.5: on macOS, Windows and X11 her window follows the pointer's place on the screen, on Wayland her popup opens again where she is let go; she moves by whole points inside the pane, and the settings keep where she stands as ely_left, 140 by default
This commit is contained in:
13 files changed
+181
-57
No files matched your search
+92
-18
@@ -18,10 +18,10 @@ use std::{
|
||||
};
|
||||
|
||||
use gpui::{
|
||||
App, AppContext as _, Bounds, Context, Decorations, InteractiveElement, IntoElement,
|
||||
MouseButton, ParentElement, Pixels, Point, Render, Styled, Subscription, Window,
|
||||
WindowBackgroundAppearance, WindowBounds, WindowHandle, WindowKind, WindowOptions, canvas, div,
|
||||
point, popup, px, size,
|
||||
App, AppContext as _, Bounds, Context, Decorations, DispatchPhase, InteractiveElement,
|
||||
IntoElement, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels,
|
||||
Point, Render, Styled, Subscription, Window, WindowBackgroundAppearance, WindowBounds,
|
||||
WindowHandle, WindowKind, WindowOptions, canvas, div, point, popup, px, size,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -40,8 +40,6 @@ use crate::{settings::SettingsStore, workspace::Workspace};
|
||||
const WIDTH: f32 = 112.0;
|
||||
const HEAD: f32 = 107.4;
|
||||
const ROOM: f32 = 16.0;
|
||||
/// How far in from the pane's left edge she holds its top edge.
|
||||
const LEFT: f32 = 140.0;
|
||||
/// How often she checks her Wayland anchor against the main window.
|
||||
const CHECK: Duration = Duration::from_millis(500);
|
||||
|
||||
@@ -70,6 +68,10 @@ pub struct Pet {
|
||||
/// The edge she stands on, and on Wayland the anchor her popup opened at, from the pane's top left, which never moves.
|
||||
edge: Edge,
|
||||
anchor: Option<Point<Pixels>>,
|
||||
/// How far in from the pane's left edge she stands, and a drag under way: the pointer's x where it began and where she stood then.
|
||||
at: f32,
|
||||
drag: Option<(f32, f32)>,
|
||||
main: WindowHandle<Workspace>,
|
||||
/// What keeps her window on the main window's edge, where the platform does not.
|
||||
glue: Option<platform::Glue>,
|
||||
_watch: Vec<Subscription>,
|
||||
@@ -125,9 +127,9 @@ fn close(pet: WindowHandle<Pet>, why: &str, cx: &mut App) {
|
||||
}
|
||||
}
|
||||
|
||||
/// How far in from the pane's left edge she stands on a pane `width` points wide: `LEFT`, or less on a narrow pane, so she holds its edge.
|
||||
fn left(width: Pixels) -> f32 {
|
||||
LEFT.min(f32::from(width) - WIDTH).max(0.0)
|
||||
/// How far in from the pane's left edge she stands on a pane `width` points wide: `at`, or less on a narrow pane, so she holds its edge.
|
||||
fn left(width: Pixels, at: f32) -> f32 {
|
||||
at.min(f32::from(width) - WIDTH).max(0.0)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@@ -150,19 +152,20 @@ fn wayland_edge(window: &Window) -> Edge {
|
||||
}
|
||||
|
||||
/// Where she stands over a Wayland window: the edge, and her anchor counted from the pane's top left, as gpui's popups count.
|
||||
fn wayland_spot(window: &Window) -> (Edge, Point<Pixels>) {
|
||||
fn wayland_spot(window: &Window, at: f32) -> (Edge, Point<Pixels>) {
|
||||
let pane = pane(window);
|
||||
let edge = wayland_edge(window);
|
||||
let y = match edge {
|
||||
Edge::Top => px(0.),
|
||||
Edge::Bottom => pane.size.height - px(1.),
|
||||
};
|
||||
(edge, point(px(left(pane.size.width)), y))
|
||||
(edge, point(px(left(pane.size.width, at)), y))
|
||||
}
|
||||
|
||||
/// Opens her again over a Wayland `main` once its size or state moves the anchor her popup opened at, which never moves.
|
||||
fn reanchor(main: WindowHandle<Workspace>, shown: &Shown, cx: &mut App) -> anyhow::Result<()> {
|
||||
let spot = main.update(cx, |_, window, _| wayland_spot(window))?;
|
||||
let at = SettingsStore::get(cx).ely_left;
|
||||
let spot = main.update(cx, |_, window, _| wayland_spot(window, at))?;
|
||||
let Some(pet) = *shown.borrow() else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -174,7 +177,7 @@ fn reanchor(main: WindowHandle<Workspace>, shown: &Shown, cx: &mut App) -> anyho
|
||||
}
|
||||
close(
|
||||
pet,
|
||||
"to stand where the window's new size or state puts her",
|
||||
"to stand where the window's size or state, or a drag, puts her",
|
||||
cx,
|
||||
);
|
||||
*shown.borrow_mut() = open_pet(main, cx);
|
||||
@@ -183,15 +186,16 @@ fn reanchor(main: WindowHandle<Workspace>, shown: &Shown, cx: &mut App) -> anyho
|
||||
|
||||
/// 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 kept = SettingsStore::get(cx).ely_left;
|
||||
let (bounds, pane, wayland, (edge, spot)) = main.update(cx, |_, window, _| {
|
||||
(
|
||||
window.bounds(),
|
||||
pane(window),
|
||||
wayland(window),
|
||||
wayland_spot(window),
|
||||
wayland_spot(window, kept),
|
||||
)
|
||||
})?;
|
||||
let at = left(pane.size.width);
|
||||
let at = left(pane.size.width, kept);
|
||||
let room = size(px(WIDTH), px(HEAD + ROOM));
|
||||
let (edge, kind) = if wayland {
|
||||
let anchor = match edge {
|
||||
@@ -254,6 +258,9 @@ fn show(main: WindowHandle<Workspace>, cx: &mut App) -> anyhow::Result<WindowHan
|
||||
eased: None,
|
||||
edge,
|
||||
anchor: wayland.then_some(spot),
|
||||
at: kept,
|
||||
drag: None,
|
||||
main,
|
||||
glue: None,
|
||||
_watch: vec![feel],
|
||||
}
|
||||
@@ -282,7 +289,7 @@ fn glue(
|
||||
main.update(cx, |_, main, cx| {
|
||||
pet.update(cx, |pet, window, cx| {
|
||||
let glue = platform::Glue::new(window, main)?;
|
||||
pet.edge = glue.place(main)?;
|
||||
pet.edge = glue.place(main, pet.at)?;
|
||||
pet.glue = Some(glue);
|
||||
pet._watch
|
||||
.push(cx.observe_window_bounds(main, |pet, main, _| pet.place(main)));
|
||||
@@ -371,7 +378,7 @@ impl Pet {
|
||||
let Some(glue) = &self.glue else {
|
||||
return;
|
||||
};
|
||||
match glue.place(main) {
|
||||
match glue.place(main, self.at) {
|
||||
Ok(edge) if edge != self.edge => {
|
||||
let name = format!("{edge:?}").to_lowercase();
|
||||
log::info!("pet: Ely moves to the pane's {name} edge");
|
||||
@@ -382,6 +389,58 @@ impl Pet {
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes hold of her for a drag along her edge, the pointer `local` in her window.
|
||||
fn grab(&mut self, local: Pixels, scale: f32) {
|
||||
match self.pointer(local, scale) {
|
||||
Ok(x) => self.drag = Some((x, self.at)),
|
||||
Err(error) => log::error!("{error:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves her along her edge after the pointer, inside the pane.
|
||||
fn slide(&mut self, local: Pixels, scale: f32, cx: &mut App) {
|
||||
let Some((from, at)) = self.drag else {
|
||||
return;
|
||||
};
|
||||
let x = match self.pointer(local, scale) {
|
||||
Ok(x) => x,
|
||||
Err(error) => {
|
||||
self.drag = None;
|
||||
return log::error!("{error:#}");
|
||||
}
|
||||
};
|
||||
let main = self.main;
|
||||
let slid = main.update(cx, |_, main, _| {
|
||||
let room = (f32::from(pane(main).size.width) - WIDTH).max(0.0);
|
||||
// Whole points, so the kept place carries no float noise.
|
||||
self.at = (at + x - from).round().clamp(0.0, room.floor());
|
||||
self.place(main);
|
||||
});
|
||||
if let Err(error) = slid {
|
||||
log::debug!("pet: the main window does not read: {error:#}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Lets go of her, the settings keeping where the drag left her, where a Wayland popup then opens again.
|
||||
fn release(&mut self, cx: &mut App) {
|
||||
if self.drag.take().is_none() || SettingsStore::get(cx).ely_left == self.at {
|
||||
return;
|
||||
}
|
||||
let at = self.at;
|
||||
match SettingsStore::update(cx, |settings| settings.ely_left = at) {
|
||||
Ok(()) => log::info!("pet: Ely stands {at} points in from the pane's left edge"),
|
||||
Err(error) => log::error!("pet: where Ely stands is not kept: {error:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// The pointer's x in points: on the screen where her glue moves her window, else in her window, which a drag leaves in place.
|
||||
fn pointer(&self, local: Pixels, scale: f32) -> anyhow::Result<f32> {
|
||||
match &self.glue {
|
||||
Some(glue) => glue.pointer(scale),
|
||||
None => Ok(local.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn shown(&mut self, visible: bool, main: &Window) {
|
||||
if let Some(glue) = &self.glue {
|
||||
@@ -402,17 +461,32 @@ impl Render for Pet {
|
||||
// gpui gives a hidden window no frames, so she rests while unseen.
|
||||
window.request_animation_frame();
|
||||
let rig = self.rig.clone();
|
||||
let this = cx.entity();
|
||||
let drawn = canvas(
|
||||
|_, _, _| {},
|
||||
move |bounds, _, window, cx| {
|
||||
let head = point(bounds.origin.x, bounds.origin.y + px(ROOM));
|
||||
rig.paint(head, &pose, window, cx);
|
||||
// Her window keeps the pointer while the button is held, inside it or not.
|
||||
let held = this.clone();
|
||||
window.on_mouse_event(move |event: &MouseMoveEvent, phase, window, cx| {
|
||||
if phase == DispatchPhase::Bubble && event.dragging() {
|
||||
let scale = window.scale_factor();
|
||||
held.update(cx, |pet, cx| pet.slide(event.position.x, scale, cx));
|
||||
}
|
||||
});
|
||||
window.on_mouse_event(move |event: &MouseUpEvent, phase, _, cx| {
|
||||
if phase == DispatchPhase::Bubble && event.button == MouseButton::Left {
|
||||
this.update(cx, |pet, cx| pet.release(cx));
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
div().size_full().child(drawn.size_full()).on_mouse_down(
|
||||
MouseButton::Left,
|
||||
cx.listener(|pet, _, _, cx| {
|
||||
cx.listener(|pet, event: &MouseDownEvent, window, cx| {
|
||||
pet.press(Instant::now());
|
||||
pet.grab(event.position.x, window.scale_factor());
|
||||
cx.notify();
|
||||
}),
|
||||
)
|
||||
|
||||
+10
-4
@@ -3,7 +3,7 @@
|
||||
use anyhow::bail;
|
||||
use gpui::Window;
|
||||
use objc::{
|
||||
msg_send,
|
||||
class, msg_send,
|
||||
runtime::{NO, Object, YES},
|
||||
sel, sel_impl,
|
||||
};
|
||||
@@ -64,8 +64,8 @@ impl Glue {
|
||||
Ok(Self { pet, main })
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
/// 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, `at` in as far as its width allows, and gives the edge.
|
||||
pub fn place(&self, _: &Window, at: f32) -> anyhow::Result<Edge> {
|
||||
unsafe {
|
||||
let frame: Rect = msg_send![self.main, frame];
|
||||
let screen: *mut Object = msg_send![self.main, screen];
|
||||
@@ -74,7 +74,7 @@ impl Glue {
|
||||
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))),
|
||||
x: frame.origin.x + f64::from(left(gpui::px(frame.width as f32), at)),
|
||||
y: match edge {
|
||||
Edge::Top => top,
|
||||
Edge::Bottom => frame.origin.y,
|
||||
@@ -84,6 +84,12 @@ impl Glue {
|
||||
Ok(edge)
|
||||
}
|
||||
}
|
||||
|
||||
/// The pointer's x on the screen, in points.
|
||||
pub fn pointer(&self, _: f32) -> anyhow::Result<f32> {
|
||||
let at: Point = unsafe { msg_send![class!(NSEvent), mouseLocation] };
|
||||
Ok(at.x as f32)
|
||||
}
|
||||
}
|
||||
|
||||
/// The NSWindow gpui made for `window`.
|
||||
|
||||
+12
-4
@@ -1,5 +1,7 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use gpui::WindowId;
|
||||
|
||||
use super::{look::Mark, *};
|
||||
|
||||
/// Her, `mood` since `since`, come long before.
|
||||
@@ -15,6 +17,9 @@ fn ely(mood: Mood, since: Instant) -> Pet {
|
||||
eased: None,
|
||||
edge: Edge::Top,
|
||||
anchor: None,
|
||||
at: 140.0,
|
||||
drag: None,
|
||||
main: WindowHandle::new(WindowId::from(1)),
|
||||
glue: None,
|
||||
_watch: Vec::new(),
|
||||
}
|
||||
@@ -98,11 +103,14 @@ fn she_stands_on_the_top_edge_while_her_box_fits_above_it() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn she_holds_the_edge_of_any_pane() {
|
||||
assert_eq!(left(px(1200.)), LEFT);
|
||||
assert_eq!(left(px(200.)), 200.0 - WIDTH);
|
||||
fn she_holds_the_edge_of_any_pane_where_a_drag_left_her() {
|
||||
assert_eq!(left(px(1200.), 140.0), 140.0);
|
||||
assert_eq!(left(px(1200.), 900.0), 900.0, "dragged right");
|
||||
assert_eq!(left(px(1200.), 0.0), 0.0, "dragged to its left end");
|
||||
assert_eq!(left(px(600.), 900.0), 600.0 - WIDTH, "a narrower pane");
|
||||
assert_eq!(left(px(200.), 140.0), 200.0 - WIDTH);
|
||||
assert_eq!(
|
||||
left(px(100.)),
|
||||
left(px(100.), 140.0),
|
||||
0.0,
|
||||
"narrower than she is, she stands at its left"
|
||||
);
|
||||
|
||||
+20
-12
@@ -9,14 +9,14 @@ use anyhow::bail;
|
||||
use gpui::{Bounds, Pixels, Window};
|
||||
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
|
||||
use windows_sys::Win32::{
|
||||
Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM},
|
||||
Foundation::{HWND, LPARAM, LRESULT, POINT, RECT, WPARAM},
|
||||
Graphics::Gdi::{
|
||||
GetMonitorInfoW, MONITOR_DEFAULTTONEAREST, MONITORINFO, MonitorFromRect, MonitorFromWindow,
|
||||
},
|
||||
UI::{
|
||||
HiDpi::{GetDpiForMonitor, GetDpiForWindow, MDT_EFFECTIVE_DPI},
|
||||
WindowsAndMessaging::{
|
||||
CallWindowProcW, GWL_STYLE, GWLP_HWNDPARENT, GWLP_WNDPROC, GetWindowInfo,
|
||||
CallWindowProcW, GWL_STYLE, GWLP_HWNDPARENT, GWLP_WNDPROC, GetCursorPos, GetWindowInfo,
|
||||
GetWindowLongPtrW, HWND_NOTOPMOST, MA_NOACTIVATE, PostMessageW, SWP_FRAMECHANGED,
|
||||
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, SetWindowLongPtrW, SetWindowPos,
|
||||
USER_DEFAULT_SCREEN_DPI, WINDOWINFO, WM_APP, WM_DPICHANGED, WM_MOUSEACTIVATE, WNDPROC,
|
||||
@@ -77,13 +77,21 @@ impl Glue {
|
||||
Ok(Self { pet, main })
|
||||
}
|
||||
|
||||
/// 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))
|
||||
/// 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, `at` in as far as its width allows, and gives the edge.
|
||||
pub fn place(&self, main: &Window, at: f32) -> anyhow::Result<Edge> {
|
||||
self.put(main.scale_factor(), pane(main), at)
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
/// The pointer's x on the screen, in points at `scale`.
|
||||
pub fn pointer(&self, scale: f32) -> anyhow::Result<f32> {
|
||||
let mut at = POINT::default();
|
||||
let read = unsafe { GetCursorPos(&mut at) };
|
||||
anyhow::ensure!(read != 0, "pet: the pointer's place does not read");
|
||||
Ok(at.x as f32 / scale)
|
||||
}
|
||||
|
||||
/// Posts her bottom left corner on an edge of `pane` at `scale`, `at` points in, 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>, at: f32) -> anyhow::Result<Edge> {
|
||||
let mut main = WINDOWINFO {
|
||||
cbSize: size_of::<WINDOWINFO>() as u32,
|
||||
..Default::default()
|
||||
@@ -97,9 +105,9 @@ impl Glue {
|
||||
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 (_, top) = stand(main.rcClient, scale, pane, Edge::Top, at);
|
||||
let edge = edge((top - display.rcWork.top) as f32 / scale);
|
||||
let corner = stand(main.rcClient, scale, pane, edge);
|
||||
let corner = stand(main.rcClient, scale, pane, edge, at);
|
||||
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:?}, the pane's {edge:?} edge");
|
||||
@@ -107,13 +115,13 @@ impl Glue {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
/// Her bottom left corner on `edge` of `pane`, `at` points in, at `scale` over the main window's `client` rect, in pixels.
|
||||
fn stand(client: RECT, scale: f32, pane: Bounds<Pixels>, edge: Edge, at: f32) -> (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);
|
||||
let x = f32::from(pane.origin.x) + left(pane.size.width, at);
|
||||
(
|
||||
client.left + (x * scale).round() as i32,
|
||||
client.top + (f32::from(y) * scale).round() as i32,
|
||||
|
||||
@@ -102,14 +102,14 @@ fn her_bottom_stands_on_either_edge_of_the_pane_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)] {
|
||||
for (edge, y) in [(Edge::Top, 8.0), (Edge::Bottom, 608.0)] {
|
||||
let her = boxed(stand(client, scale, pane, edge), dpi);
|
||||
let her = boxed(stand(client, scale, pane, edge, 140.0), 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,
|
||||
100 + ((10.0 + left(px(800.), 140.0)) * scale).round() as i32,
|
||||
20 + (y * scale).round() as i32,
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -272,7 +272,7 @@ fn the_large_framed_popup_gpui_makes_stands_bare_in_her_box_on_an_edge_of_the_pa
|
||||
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.)));
|
||||
let roomy = glue.put(1.0, pane).expect("placed");
|
||||
let roomy = glue.put(1.0, pane, 140.0).expect("placed");
|
||||
let made = info(pet).rcWindow;
|
||||
deliver(pet);
|
||||
let (her, under) = (info(pet), info(main));
|
||||
@@ -297,7 +297,7 @@ fn the_large_framed_popup_gpui_makes_stands_bare_in_her_box_on_an_edge_of_the_pa
|
||||
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");
|
||||
let cramped = glue.put(1.0, pane, 140.0).expect("placed again");
|
||||
deliver(pet);
|
||||
let (inside, low) = (info(pet).rcWindow, info(main).rcClient);
|
||||
unsafe {
|
||||
@@ -317,7 +317,7 @@ fn the_large_framed_popup_gpui_makes_stands_bare_in_her_box_on_an_edge_of_the_pa
|
||||
((HEAD + ROOM) * own).round() as i32,
|
||||
);
|
||||
let edge = (
|
||||
under.rcClient.left + left(px(380.)).round() as i32,
|
||||
under.rcClient.left + left(px(380.), 140.0).round() as i32,
|
||||
under.rcClient.top,
|
||||
);
|
||||
let rect = |rect: RECT| (rect.left, rect.top, rect.right, rect.bottom);
|
||||
|
||||
+20
-11
@@ -49,20 +49,29 @@ impl Glue {
|
||||
Ok(glue)
|
||||
}
|
||||
|
||||
/// Stands her on the main window's pane, on its top edge while her box fits under the top of the monitor holding most of the pane, or of the work area where that lies lower, 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))
|
||||
/// Stands her on the main window's pane, on its top edge while her box fits under the top of the monitor holding most of the pane, or of the work area where that lies lower, else on its bottom edge, `at` in as far as its width allows, stacked just above the main window's frame, and gives the edge.
|
||||
pub fn place(&self, main: &Window, at: f32) -> anyhow::Result<Edge> {
|
||||
self.put(main.scale_factor(), pane(main), at)
|
||||
}
|
||||
|
||||
/// The pointer's x on the screen, in points at `scale`.
|
||||
pub fn pointer(&self, scale: f32) -> anyhow::Result<f32> {
|
||||
let reply = (self.connection)
|
||||
.query_pointer(self.pet)?
|
||||
.reply()
|
||||
.context("pet: the pointer's place")?;
|
||||
Ok(f32::from(reply.root_x) / scale)
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
fn put(&self, scale: f32, pane: Bounds<Pixels>, at: f32) -> anyhow::Result<Edge> {
|
||||
let (root, frame) = self.frame()?;
|
||||
let at = (self.connection)
|
||||
let origin = (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 across = |x: Pixels| i32::from(at.dst_x) + (f32::from(x) * scale).round() as i32;
|
||||
let down = |y: Pixels| i32::from(origin.dst_y) + (f32::from(y) * scale).round() as i32;
|
||||
let across = |x: Pixels| i32::from(origin.dst_x) + (f32::from(x) * scale).round() as i32;
|
||||
let (west, top) = (across(pane.origin.x), down(pane.origin.y));
|
||||
let rect = [
|
||||
west,
|
||||
@@ -77,7 +86,7 @@ impl Glue {
|
||||
Edge::Bottom => pane.bottom(),
|
||||
});
|
||||
let aux = ConfigureWindowAux::new()
|
||||
.x(across(pane.origin.x + px(left(pane.size.width))))
|
||||
.x(across(pane.origin.x + px(left(pane.size.width, at))))
|
||||
.y(bottom - ((HEAD + ROOM) * scale).round() as i32)
|
||||
.sibling(frame)
|
||||
.stack_mode(StackMode::ABOVE);
|
||||
@@ -269,7 +278,7 @@ mod tests {
|
||||
workarea,
|
||||
};
|
||||
let pane = Bounds::new(point(px(10.), px(10.)), size(px(380.), px(280.)));
|
||||
let roomy = glue.put(2.0, pane).expect("she is placed");
|
||||
let roomy = glue.put(2.0, pane, 140.0).expect("she is placed");
|
||||
let check = x11rb::connect(Some(&xvfb.display))
|
||||
.expect("the check connects")
|
||||
.0;
|
||||
@@ -280,7 +289,7 @@ mod tests {
|
||||
.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 + left(px(380.), 140.0)) * 2.0).round() as i32,
|
||||
440 - tall,
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -322,7 +331,7 @@ mod tests {
|
||||
.expect("the request")
|
||||
.check()
|
||||
.expect("a work area");
|
||||
let cramped = glue.put(2.0, pane).expect("she is placed again");
|
||||
let cramped = glue.put(2.0, pane, 140.0).expect("she is placed again");
|
||||
let low = check
|
||||
.get_geometry(pet)
|
||||
.expect("the request")
|
||||
|
||||
@@ -74,6 +74,8 @@ pub struct Settings {
|
||||
pub copy_on_select: bool,
|
||||
/// Ely peeks over the main window's top edge.
|
||||
pub ely: bool,
|
||||
/// How far in from the pane's left edge she stands, in points, as a drag left her.
|
||||
pub ely_left: f32,
|
||||
pub show_sessions: bool,
|
||||
pub show_panel: bool,
|
||||
pub panel: PanelTab,
|
||||
@@ -106,6 +108,7 @@ impl Default for Settings {
|
||||
option_as_alt: OptionAsAlt::Off,
|
||||
copy_on_select: false,
|
||||
ely: true,
|
||||
ely_left: 140.0,
|
||||
show_sessions: true,
|
||||
show_panel: true,
|
||||
panel: PanelTab::Git,
|
||||
@@ -124,6 +127,7 @@ pub const PADDING: RangeInclusive<f32> = 0.0..=64.0;
|
||||
pub const SCROLLBACK: RangeInclusive<u32> = 0..=100_000;
|
||||
pub const COLUMN: RangeInclusive<f32> = 160.0..=960.0;
|
||||
pub const WINDOW: RangeInclusive<f32> = 100.0..=16384.0;
|
||||
pub const ELY_LEFT: RangeInclusive<f32> = 0.0..=16384.0;
|
||||
|
||||
impl Settings {
|
||||
/// Every value inside its range, every theme built in; the error names the field.
|
||||
@@ -138,6 +142,7 @@ impl Settings {
|
||||
within("panel_width", self.panel_width, &COLUMN)?;
|
||||
within("window_width", self.window_width, &WINDOW)?;
|
||||
within("window_height", self.window_height, &WINDOW)?;
|
||||
within("ely_left", self.ely_left, &ELY_LEFT)?;
|
||||
for (field, id) in [
|
||||
("theme", Some(&self.theme)),
|
||||
("dark_theme", self.dark_theme.as_ref()),
|
||||
|
||||
Reference in new issue
Block a user