Skip to content

Commit 82f5e78

Browse files
committed
Add screen absolute mouse position for resizing
1 parent 3724a00 commit 82f5e78

8 files changed

Lines changed: 172 additions & 19 deletions

File tree

src/event.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,12 @@ pub enum ScrollDelta {
3838
pub enum MouseEvent {
3939
/// The mouse cursor was moved
4040
CursorMoved {
41-
/// The logical coordinates of the mouse position
41+
/// The logical coordinates of the mouse position (window-relative)
4242
position: Point,
43+
/// The logical coordinates of the mouse position in screen space (absolute)
44+
/// This remains constant even when the window resizes, making it suitable
45+
/// for drag operations that resize the window.
46+
screen_position: Point,
4347
/// The modifiers that were held down just before the event.
4448
modifiers: Modifiers,
4549
},
@@ -79,17 +83,21 @@ pub enum MouseEvent {
7983
CursorLeft,
8084

8185
DragEntered {
82-
/// The logical coordinates of the mouse position
86+
/// The logical coordinates of the mouse position (window-relative)
8387
position: Point,
88+
/// The logical coordinates of the mouse position in screen space (absolute)
89+
screen_position: Point,
8490
/// The modifiers that were held down just before the event.
8591
modifiers: Modifiers,
8692
/// Data being dragged
8793
data: DropData,
8894
},
8995

9096
DragMoved {
91-
/// The logical coordinates of the mouse position
97+
/// The logical coordinates of the mouse position (window-relative)
9298
position: Point,
99+
/// The logical coordinates of the mouse position in screen space (absolute)
100+
screen_position: Point,
93101
/// The modifiers that were held down just before the event.
94102
modifiers: Modifiers,
95103
/// Data being dragged
@@ -99,8 +107,10 @@ pub enum MouseEvent {
99107
DragLeft,
100108

101109
DragDropped {
102-
/// The logical coordinates of the mouse position
110+
/// The logical coordinates of the mouse position (window-relative)
103111
position: Point,
112+
/// The logical coordinates of the mouse position in screen space (absolute)
113+
screen_position: Point,
104114
/// The modifiers that were held down just before the event.
105115
modifiers: Modifiers,
106116
/// Data being dragged

src/macos/cursor.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use crate::MouseCursor;
2+
use cocoa::base::id;
3+
use objc::{class, msg_send, sel, sel_impl};
4+
5+
pub fn mouse_cursor_to_nscursor(cursor: MouseCursor) -> id {
6+
unsafe {
7+
let nscursor_class = class!(NSCursor);
8+
match cursor {
9+
MouseCursor::Default => msg_send![nscursor_class, arrowCursor],
10+
MouseCursor::Hand => msg_send![nscursor_class, pointingHandCursor],
11+
MouseCursor::HandGrabbing => msg_send![nscursor_class, closedHandCursor],
12+
MouseCursor::Help => msg_send![nscursor_class, arrowCursor], // No help cursor
13+
MouseCursor::Hidden => {
14+
// Return a null cursor for hidden - will be handled specially
15+
std::ptr::null_mut()
16+
}
17+
MouseCursor::Text => msg_send![nscursor_class, IBeamCursor],
18+
MouseCursor::VerticalText => msg_send![nscursor_class, IBeamCursor],
19+
MouseCursor::Working => msg_send![nscursor_class, arrowCursor],
20+
MouseCursor::PtrWorking => msg_send![nscursor_class, arrowCursor],
21+
MouseCursor::NotAllowed => msg_send![nscursor_class, operationNotAllowedCursor],
22+
MouseCursor::PtrNotAllowed => msg_send![nscursor_class, operationNotAllowedCursor],
23+
MouseCursor::ZoomIn => msg_send![nscursor_class, arrowCursor],
24+
MouseCursor::ZoomOut => msg_send![nscursor_class, arrowCursor],
25+
MouseCursor::Alias => msg_send![nscursor_class, dragLinkCursor],
26+
MouseCursor::Copy => msg_send![nscursor_class, dragCopyCursor],
27+
MouseCursor::Move => msg_send![nscursor_class, arrowCursor],
28+
MouseCursor::AllScroll => msg_send![nscursor_class, arrowCursor],
29+
MouseCursor::Cell => msg_send![nscursor_class, crosshairCursor],
30+
MouseCursor::Crosshair => msg_send![nscursor_class, crosshairCursor],
31+
MouseCursor::EResize => msg_send![nscursor_class, resizeRightCursor],
32+
MouseCursor::NResize => msg_send![nscursor_class, resizeUpCursor],
33+
MouseCursor::NeResize => msg_send![nscursor_class, arrowCursor], // No built-in
34+
MouseCursor::NwResize => msg_send![nscursor_class, arrowCursor], // No built-in
35+
MouseCursor::SResize => msg_send![nscursor_class, resizeDownCursor],
36+
MouseCursor::SeResize => msg_send![nscursor_class, arrowCursor], // No built-in
37+
MouseCursor::SwResize => msg_send![nscursor_class, arrowCursor], // No built-in
38+
MouseCursor::WResize => msg_send![nscursor_class, resizeLeftCursor],
39+
MouseCursor::EwResize => msg_send![nscursor_class, resizeLeftRightCursor],
40+
MouseCursor::NsResize => msg_send![nscursor_class, resizeUpDownCursor],
41+
MouseCursor::NwseResize => {
42+
// Use private API for diagonal resize cursor
43+
msg_send![nscursor_class, _windowResizeNorthWestSouthEastCursor]
44+
}
45+
MouseCursor::NeswResize => {
46+
// Use private API for diagonal resize cursor
47+
msg_send![nscursor_class, _windowResizeNorthEastSouthWestCursor]
48+
}
49+
MouseCursor::ColResize => msg_send![nscursor_class, resizeLeftRightCursor],
50+
MouseCursor::RowResize => msg_send![nscursor_class, resizeUpDownCursor],
51+
}
52+
}
53+
}

src/macos/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Eventually we should migrate to the objc2 crate and remove this.
33
#![allow(unexpected_cfgs)]
44

5+
mod cursor;
56
mod keyboard;
67
mod view;
78
mod window;

src/macos/view.rs

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -393,17 +393,38 @@ extern "C" fn update_tracking_areas(this: &Object, _self: Sel, _: id) {
393393
extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) {
394394
let state = unsafe { WindowState::from_view(this) };
395395

396-
let point: NSPoint = unsafe {
396+
// Window-relative position (existing behavior)
397+
let window_point: NSPoint = unsafe {
397398
let point = NSEvent::locationInWindow(event);
398-
399399
msg_send![this, convertPoint:point fromView:nil]
400400
};
401+
402+
// Screen-absolute position (new!)
403+
// NSEvent::mouseLocation returns screen coordinates with Y=0 at BOTTOM
404+
// We need to flip Y-axis to match Windows/X11 convention (Y=0 at TOP)
405+
let screen_point: NSPoint = unsafe {
406+
NSEvent::mouseLocation(event)
407+
};
408+
409+
// Get the screen height to flip Y coordinate
410+
let screen_height = unsafe {
411+
let screen: id = msg_send![class!(NSScreen), mainScreen];
412+
let frame: NSRect = msg_send![screen, frame];
413+
frame.size.height
414+
};
415+
401416
let modifiers = unsafe { NSEvent::modifierFlags(event) };
402417

403-
let position = Point { x: point.x, y: point.y };
418+
let position = Point { x: window_point.x, y: window_point.y };
419+
// Flip Y-axis: convert from bottom-origin to top-origin
420+
let screen_position = Point {
421+
x: screen_point.x,
422+
y: screen_height - screen_point.y
423+
};
404424

405425
state.trigger_event(Event::Mouse(MouseEvent::CursorMoved {
406426
position,
427+
screen_position,
407428
modifiers: make_modifiers(modifiers),
408429
}));
409430
}
@@ -430,9 +451,29 @@ extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) {
430451
}));
431452
}
432453

433-
fn get_drag_position(sender: id) -> Point {
434-
let point: NSPoint = unsafe { msg_send![sender, draggingLocation] };
435-
Point::new(point.x, point.y)
454+
fn get_drag_position(sender: id) -> (Point, Point) {
455+
// Window-relative position
456+
let window_point: NSPoint = unsafe { msg_send![sender, draggingLocation] };
457+
458+
// For drag events, we need to get screen position from the global mouse location
459+
// since NSDraggingInfo doesn't provide it directly
460+
// NSEvent::mouseLocation returns coordinates with Y=0 at BOTTOM
461+
let screen_point: NSPoint = unsafe {
462+
let ns_event_class: id = msg_send![class!(NSEvent), class];
463+
msg_send![ns_event_class, mouseLocation]
464+
};
465+
466+
// Get screen height to flip Y coordinate (convert from bottom-origin to top-origin)
467+
let screen_height = unsafe {
468+
let screen: id = msg_send![class!(NSScreen), mainScreen];
469+
let frame: NSRect = msg_send![screen, frame];
470+
frame.size.height
471+
};
472+
473+
(
474+
Point::new(window_point.x, window_point.y),
475+
Point::new(screen_point.x, screen_height - screen_point.y)
476+
)
436477
}
437478

438479
fn get_drop_data(sender: id) -> DropData {
@@ -473,9 +514,11 @@ extern "C" fn dragging_entered(this: &Object, _sel: Sel, sender: id) -> NSUInteg
473514
let state = unsafe { WindowState::from_view(this) };
474515
let modifiers = state.keyboard_state().last_mods();
475516
let drop_data = get_drop_data(sender);
517+
let (position, screen_position) = get_drag_position(sender);
476518

477519
let event = MouseEvent::DragEntered {
478-
position: get_drag_position(sender),
520+
position,
521+
screen_position,
479522
modifiers: make_modifiers(modifiers),
480523
data: drop_data,
481524
};
@@ -487,9 +530,11 @@ extern "C" fn dragging_updated(this: &Object, _sel: Sel, sender: id) -> NSUInteg
487530
let state = unsafe { WindowState::from_view(this) };
488531
let modifiers = state.keyboard_state().last_mods();
489532
let drop_data = get_drop_data(sender);
533+
let (position, screen_position) = get_drag_position(sender);
490534

491535
let event = MouseEvent::DragMoved {
492-
position: get_drag_position(sender),
536+
position,
537+
screen_position,
493538
modifiers: make_modifiers(modifiers),
494539
data: drop_data,
495540
};
@@ -508,9 +553,11 @@ extern "C" fn perform_drag_operation(this: &Object, _sel: Sel, sender: id) -> BO
508553
let state = unsafe { WindowState::from_view(this) };
509554
let modifiers = state.keyboard_state().last_mods();
510555
let drop_data = get_drop_data(sender);
556+
let (position, screen_position) = get_drag_position(sender);
511557

512558
let event = MouseEvent::DragDropped {
513-
position: get_drag_position(sender),
559+
position,
560+
screen_position,
514561
modifiers: make_modifiers(modifiers),
515562
data: drop_data,
516563
};

src/macos/window.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,18 @@ impl<'a> Window<'a> {
334334
}
335335
}
336336

337-
pub fn set_mouse_cursor(&mut self, _mouse_cursor: MouseCursor) {
338-
todo!()
337+
pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) {
338+
let ns_cursor = crate::macos::cursor::mouse_cursor_to_nscursor(mouse_cursor);
339+
340+
unsafe {
341+
if ns_cursor.is_null() {
342+
// Hide cursor
343+
let _: () = msg_send![class!(NSCursor), hide];
344+
} else {
345+
// Set the cursor
346+
let _: () = msg_send![ns_cursor, set];
347+
}
348+
}
339349
}
340350

341351
#[cfg(feature = "opengl")]

src/win/drop_target.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub(super) struct DropTarget {
7070
// These are cached since DragOver and DragLeave callbacks don't provide them,
7171
// and handling drag move events gets awkward on the client end otherwise
7272
drag_position: Point,
73+
drag_screen_position: Point,
7374
drop_data: DropData,
7475
}
7576

@@ -81,6 +82,7 @@ impl DropTarget {
8182
window_state,
8283

8384
drag_position: Point::new(0.0, 0.0),
85+
drag_screen_position: Point::new(0.0, 0.0),
8486
drop_data: DropData::None,
8587
}
8688
}
@@ -114,10 +116,16 @@ impl DropTarget {
114116
let Some(window_state) = self.window_state.upgrade() else {
115117
return;
116118
};
117-
let mut pt = POINT { x: pt.x, y: pt.y };
118-
unsafe { ScreenToClient(window_state.hwnd, &mut pt as *mut POINT) };
119-
let phy_point = PhyPoint::new(pt.x, pt.y);
120-
self.drag_position = phy_point.to_logical(&window_state.window_info());
119+
120+
// Screen-absolute position (pt is already in screen coordinates)
121+
let screen_phy_point = PhyPoint::new(pt.x, pt.y);
122+
self.drag_screen_position = screen_phy_point.to_logical(&window_state.window_info());
123+
124+
// Window-relative position (convert from screen to client)
125+
let mut window_pt = POINT { x: pt.x, y: pt.y };
126+
unsafe { ScreenToClient(window_state.hwnd, &mut window_pt as *mut POINT) };
127+
let window_phy_point = PhyPoint::new(window_pt.x, window_pt.y);
128+
self.drag_position = window_phy_point.to_logical(&window_state.window_info());
121129
}
122130

123131
fn parse_drop_data(&mut self, data_object: &IDataObject) {
@@ -213,6 +221,7 @@ impl DropTarget {
213221

214222
let event = MouseEvent::DragEntered {
215223
position: drop_target.drag_position,
224+
screen_position: drop_target.drag_screen_position,
216225
modifiers,
217226
data: drop_target.drop_data.clone(),
218227
};
@@ -237,6 +246,7 @@ impl DropTarget {
237246

238247
let event = MouseEvent::DragMoved {
239248
position: drop_target.drag_position,
249+
screen_position: drop_target.drag_screen_position,
240250
modifiers,
241251
data: drop_target.drop_data.clone(),
242252
};
@@ -269,6 +279,7 @@ impl DropTarget {
269279

270280
let event = MouseEvent::DragDropped {
271281
position: drop_target.drag_position,
282+
screen_position: drop_target.drag_screen_position,
272283
modifiers,
273284
data: drop_target.drop_data.clone(),
274285
};

src/win/window.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,27 @@ unsafe fn wnd_proc_inner(
196196
.on_event(&mut window, enter_event);
197197
}
198198

199+
// Window-relative position (from lparam)
199200
let x = (lparam & 0xFFFF) as i16 as i32;
200201
let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
201202

202203
let physical_pos = PhyPoint { x, y };
203204
let logical_pos = physical_pos.to_logical(&window_state.window_info.borrow());
205+
206+
// Screen-absolute position (using GetCursorPos)
207+
let mut screen_physical_pos = POINT { x: 0, y: 0 };
208+
unsafe {
209+
winapi::um::winuser::GetCursorPos(&mut screen_physical_pos);
210+
}
211+
let screen_physical = PhyPoint {
212+
x: screen_physical_pos.x,
213+
y: screen_physical_pos.y
214+
};
215+
let screen_logical_pos = screen_physical.to_logical(&window_state.window_info.borrow());
216+
204217
let move_event = Event::Mouse(MouseEvent::CursorMoved {
205218
position: logical_pos,
219+
screen_position: screen_logical_pos,
206220
modifiers: window_state
207221
.keyboard_state
208222
.borrow()

src/x11/event_loop.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,14 @@ impl EventLoop {
180180
let physical_pos = PhyPoint::new(event.event_x as i32, event.event_y as i32);
181181
let logical_pos = physical_pos.to_logical(&self.window.window_info);
182182

183+
let screen_physical_pos = PhyPoint::new(event.root_x as i32, event.root_y as i32);
184+
let screen_logical_pos = screen_physical_pos.to_logical(&self.window.window_info);
185+
183186
self.handler.on_event(
184187
&mut crate::Window::new(Window { inner: &self.window }),
185188
Event::Mouse(MouseEvent::CursorMoved {
186189
position: logical_pos,
190+
screen_position: screen_logical_pos,
187191
modifiers: key_mods(event.state),
188192
}),
189193
);
@@ -198,10 +202,13 @@ impl EventLoop {
198202
// we generate a CursorMoved as well, so the mouse position from here isn't lost
199203
let physical_pos = PhyPoint::new(event.event_x as i32, event.event_y as i32);
200204
let logical_pos = physical_pos.to_logical(&self.window.window_info);
205+
let screen_physical_pos = PhyPoint::new(event.root_x as i32, event.root_y as i32);
206+
let screen_logical_pos = screen_physical_pos.to_logical(&self.window.window_info);
201207
self.handler.on_event(
202208
&mut crate::Window::new(Window { inner: &self.window }),
203209
Event::Mouse(MouseEvent::CursorMoved {
204210
position: logical_pos,
211+
screen_position: screen_logical_pos,
205212
modifiers: key_mods(event.state),
206213
}),
207214
);

0 commit comments

Comments
 (0)