Skip to content

Commit bf27eb3

Browse files
committed
Add screen absolute mouse position for resizing
1 parent 237d323 commit bf27eb3

File tree

8 files changed

+172
-19
lines changed

8 files changed

+172
-19
lines changed

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
@@ -73,6 +73,7 @@ pub(super) struct DropTarget {
7373
// These are cached since DragOver and DragLeave callbacks don't provide them,
7474
// and handling drag move events gets awkward on the client end otherwise
7575
drag_position: Point,
76+
drag_screen_position: Point,
7677
drop_data: DropData,
7778
}
7879

@@ -84,6 +85,7 @@ impl DropTarget {
8485
window_state,
8586

8687
drag_position: Point::new(0.0, 0.0),
88+
drag_screen_position: Point::new(0.0, 0.0),
8789
drop_data: DropData::None,
8890
}
8991
}
@@ -117,10 +119,16 @@ impl DropTarget {
117119
let Some(window_state) = self.window_state.upgrade() else {
118120
return;
119121
};
120-
let mut pt = POINT { x: pt.x, y: pt.y };
121-
unsafe { ScreenToClient(window_state.hwnd, &mut pt as *mut POINT) };
122-
let phy_point = PhyPoint::new(pt.x, pt.y);
123-
self.drag_position = phy_point.to_logical(&window_state.window_info());
122+
123+
// Screen-absolute position (pt is already in screen coordinates)
124+
let screen_phy_point = PhyPoint::new(pt.x, pt.y);
125+
self.drag_screen_position = screen_phy_point.to_logical(&window_state.window_info());
126+
127+
// Window-relative position (convert from screen to client)
128+
let mut window_pt = POINT { x: pt.x, y: pt.y };
129+
unsafe { ScreenToClient(window_state.hwnd, &mut window_pt as *mut POINT) };
130+
let window_phy_point = PhyPoint::new(window_pt.x, window_pt.y);
131+
self.drag_position = window_phy_point.to_logical(&window_state.window_info());
124132
}
125133

126134
fn parse_drop_data(&mut self, data_object: &IDataObject) {
@@ -216,6 +224,7 @@ impl DropTarget {
216224

217225
let event = MouseEvent::DragEntered {
218226
position: drop_target.drag_position,
227+
screen_position: drop_target.drag_screen_position,
219228
modifiers,
220229
data: drop_target.drop_data.clone(),
221230
};
@@ -240,6 +249,7 @@ impl DropTarget {
240249

241250
let event = MouseEvent::DragMoved {
242251
position: drop_target.drag_position,
252+
screen_position: drop_target.drag_screen_position,
243253
modifiers,
244254
data: drop_target.drop_data.clone(),
245255
};
@@ -272,6 +282,7 @@ impl DropTarget {
272282

273283
let event = MouseEvent::DragDropped {
274284
position: drop_target.drag_position,
285+
screen_position: drop_target.drag_screen_position,
275286
modifiers,
276287
data: drop_target.drop_data.clone(),
277288
};

src/win/window.rs

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

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

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

src/x11/event_loop.rs

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

179+
let screen_physical_pos = PhyPoint::new(event.root_x as i32, event.root_y as i32);
180+
let screen_logical_pos = screen_physical_pos.to_logical(&self.window.window_info);
181+
179182
self.handler.on_event(
180183
&mut crate::Window::new(Window { inner: &self.window }),
181184
Event::Mouse(MouseEvent::CursorMoved {
182185
position: logical_pos,
186+
screen_position: screen_logical_pos,
183187
modifiers: key_mods(event.state),
184188
}),
185189
);
@@ -194,10 +198,13 @@ impl EventLoop {
194198
// we generate a CursorMoved as well, so the mouse position from here isn't lost
195199
let physical_pos = PhyPoint::new(event.event_x as i32, event.event_y as i32);
196200
let logical_pos = physical_pos.to_logical(&self.window.window_info);
201+
let screen_physical_pos = PhyPoint::new(event.root_x as i32, event.root_y as i32);
202+
let screen_logical_pos = screen_physical_pos.to_logical(&self.window.window_info);
197203
self.handler.on_event(
198204
&mut crate::Window::new(Window { inner: &self.window }),
199205
Event::Mouse(MouseEvent::CursorMoved {
200206
position: logical_pos,
207+
screen_position: screen_logical_pos,
201208
modifiers: key_mods(event.state),
202209
}),
203210
);

0 commit comments

Comments
 (0)