Skip to content

Commit f9c841a

Browse files
committed
Fix inconsistent scale
1 parent 5109fe9 commit f9c841a

1 file changed

Lines changed: 54 additions & 16 deletions

File tree

src/win/drop_target.rs

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ use windows::Win32::System::Ole::*;
1010
use windows::Win32::System::SystemServices::MODIFIERKEYS_FLAGS;
1111
use windows_core::Ref;
1212
use windows_sys::Win32::{
13-
Foundation::POINT, Graphics::Gdi::ScreenToClient, UI::Shell::DragQueryFileW,
14-
UI::WindowsAndMessaging::GetCursorPos,
13+
Foundation::{POINT, RECT},
14+
Graphics::Gdi::ScreenToClient,
15+
UI::Shell::DragQueryFileW,
16+
UI::WindowsAndMessaging::{GetClientRect, GetCursorPos},
1517
};
1618

1719
use crate::{DropData, DropEffect, Event, EventStatus, MouseEvent, PhyPoint, Point};
@@ -26,6 +28,10 @@ pub(super) struct DropTarget {
2628
// and handling drag move events gets awkward on the client end otherwise
2729
drag_position: Cell<Point>,
2830
drop_data: RefCell<DropData>,
31+
/// Whether drag client coordinates are physical pixels (`true`) or already logical
32+
/// (`false`). Cached when `is_drag_coords_physical` is called, and cleared on `DragLeave`
33+
/// and `Drop`.
34+
drag_coords_physical: Cell<Option<bool>>,
2935
}
3036

3137
impl DropTarget {
@@ -34,6 +40,7 @@ impl DropTarget {
3440
window_state,
3541
drag_position: Cell::new(Point::new(0.0, 0.0)),
3642
drop_data: RefCell::new(DropData::None),
43+
drag_coords_physical: Cell::new(None),
3744
}
3845
}
3946

@@ -62,23 +69,52 @@ impl DropTarget {
6269
}
6370
}
6471

65-
fn parse_coordinates(&self, _pt: POINTL) {
72+
/// Returns `true` when client coordinates from `GetCursorPos`/`ScreenToClient` are physical
73+
/// pixels and should be scaled with [`PhyPoint::to_logical`]. Returns `false` when they are
74+
/// already in logical space.
75+
///
76+
/// For some reason, this can vary based on the combination of parent window AND drag source.
77+
/// Most of the time the coordinates are physical, but logical coordinates have been observed
78+
/// with Bitwig as the parent and Windows Explorer as the drag source.
79+
///
80+
/// Cached on self.drag_coords_physical.
81+
fn is_drag_coords_physical(&self, window_state: &WindowState) -> bool {
82+
match self.drag_coords_physical.get() {
83+
Some(physical) => physical,
84+
None => {
85+
let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0 };
86+
unsafe { GetClientRect(window_state.hwnd, &mut rect) };
87+
let client_w = (rect.right - rect.left) as u32;
88+
let physical_w = window_state.window_info().physical_size().width;
89+
let logical_w = window_state.window_info().logical_size().width as u32;
90+
let physical = client_w.abs_diff(physical_w) < client_w.abs_diff(logical_w);
91+
92+
self.drag_coords_physical.set(Some(physical));
93+
physical
94+
}
95+
}
96+
}
97+
98+
fn end_drag_session(&self) {
99+
self.drag_coords_physical.set(None);
100+
}
101+
102+
fn parse_coordinates(&self) {
66103
let Some(window_state) = self.window_state.upgrade() else {
67104
return;
68105
};
69-
// OLE-supplied points can disagree with the actual cursor position for embedded
70-
// child windows (DPI virtualization / DragEnter quirks). Query the cursor directly
71-
// so drag coordinates match WM_MOUSEMOVE.
106+
107+
// Some parents pass weird coordinates via OLE `pt`. Query the cursor directly instead.
72108
let mut pt = POINT { x: 0, y: 0 };
73109
unsafe {
74110
GetCursorPos(&mut pt as *mut POINT);
75111
ScreenToClient(window_state.hwnd, &mut pt as *mut POINT);
76112
}
77-
let logical_point = if window_state.has_parent() {
78-
// If the window has a parent, the coordinates are already in logical coordinates
79-
Point::new(pt.x as f64, pt.y as f64)
80-
} else {
113+
114+
let logical_point = if self.is_drag_coords_physical(&window_state) {
81115
PhyPoint::new(pt.x, pt.y).to_logical(&window_state.window_info())
116+
} else {
117+
Point::new(pt.x as f64, pt.y as f64)
82118
};
83119
self.drag_position.set(logical_point);
84120
}
@@ -126,7 +162,7 @@ impl DropTarget {
126162
#[allow(non_snake_case)]
127163
impl IDropTarget_Impl for DropTarget_Impl {
128164
fn DragEnter(
129-
&self, pdataobj: Ref<IDataObject>, grfkeystate: MODIFIERKEYS_FLAGS, pt: &POINTL,
165+
&self, pdataobj: Ref<IDataObject>, grfkeystate: MODIFIERKEYS_FLAGS, _pt: &POINTL,
130166
pdweffect: *mut DROPEFFECT,
131167
) -> windows_core::Result<()> {
132168
let Some(window_state) = self.window_state.upgrade() else {
@@ -136,7 +172,7 @@ impl IDropTarget_Impl for DropTarget_Impl {
136172
let modifiers =
137173
window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfkeystate.0 as usize);
138174

139-
self.parse_coordinates(*pt);
175+
self.parse_coordinates();
140176
self.parse_drop_data(pdataobj.unwrap());
141177

142178
let event = MouseEvent::DragEntered {
@@ -150,7 +186,7 @@ impl IDropTarget_Impl for DropTarget_Impl {
150186
}
151187

152188
fn DragOver(
153-
&self, grfkeystate: MODIFIERKEYS_FLAGS, pt: &POINTL, pdweffect: *mut DROPEFFECT,
189+
&self, grfkeystate: MODIFIERKEYS_FLAGS, _pt: &POINTL, pdweffect: *mut DROPEFFECT,
154190
) -> windows_core::Result<()> {
155191
let Some(window_state) = self.window_state.upgrade() else {
156192
return Err(E_UNEXPECTED.into());
@@ -159,7 +195,7 @@ impl IDropTarget_Impl for DropTarget_Impl {
159195
let modifiers =
160196
window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfkeystate.0 as usize);
161197

162-
self.parse_coordinates(*pt);
198+
self.parse_coordinates();
163199

164200
let event = MouseEvent::DragMoved {
165201
position: self.drag_position.get(),
@@ -172,12 +208,13 @@ impl IDropTarget_Impl for DropTarget_Impl {
172208
}
173209

174210
fn DragLeave(&self) -> windows_core::Result<()> {
211+
self.end_drag_session();
175212
self.on_event(None, MouseEvent::DragLeft);
176213
Ok(())
177214
}
178215

179216
fn Drop(
180-
&self, pdataobj: Ref<IDataObject>, grfkeystate: MODIFIERKEYS_FLAGS, pt: &POINTL,
217+
&self, pdataobj: Ref<IDataObject>, grfkeystate: MODIFIERKEYS_FLAGS, _pt: &POINTL,
181218
pdweffect: *mut DROPEFFECT,
182219
) -> windows_core::Result<()> {
183220
let Some(window_state) = self.window_state.upgrade() else {
@@ -187,7 +224,7 @@ impl IDropTarget_Impl for DropTarget_Impl {
187224
let modifiers =
188225
window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfkeystate.0 as usize);
189226

190-
self.parse_coordinates(*pt);
227+
self.parse_coordinates();
191228
self.parse_drop_data(pdataobj.unwrap());
192229

193230
let event = MouseEvent::DragDropped {
@@ -197,6 +234,7 @@ impl IDropTarget_Impl for DropTarget_Impl {
197234
};
198235

199236
self.on_event(Some(pdweffect), event);
237+
self.end_drag_session();
200238
Ok(())
201239
}
202240
}

0 commit comments

Comments
 (0)