mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-12 21:56:24 +00:00
macOS: the canvas cursor stays after the pointer moves
The AppKit cursor adapter set its cursor again only when NSCursor.currentCursor differed from it. But that is the app's last set cursor, not the one on screen: when the pointer moves, AppKit can put the system arrow back while currentCursor still reads as ours, so the adapter kept the arrow. Pressing V over the canvas showed the Selection tool's arrow, and the first move replaced it with the system pointer. While it owns the cursor, the adapter now sets it on every pass. That costs nothing when it is already showing.
This commit is contained in:
@@ -35,7 +35,6 @@ trait Backend {
|
||||
fn suspend(&mut self, window: &Self::Window);
|
||||
fn restore(&mut self, window: &Self::Window);
|
||||
fn build(&mut self, request: &Request) -> Option<Self::Cursor>;
|
||||
fn current(&self) -> Option<Self::Cursor>;
|
||||
fn set(&mut self, cursor: &Self::Cursor);
|
||||
}
|
||||
|
||||
@@ -73,9 +72,10 @@ impl<B: Backend> State<B> {
|
||||
&& *key == request.key
|
||||
&& *owned == window
|
||||
{
|
||||
if self.backend.current().as_ref() != Some(cursor) {
|
||||
self.backend.set(cursor);
|
||||
}
|
||||
// AppKit can show the system arrow when the pointer moves while
|
||||
// `NSCursor.currentCursor` (the app's last set cursor) still reads as ours, so a
|
||||
// comparison would keep the arrow. Setting the cursor again costs nothing.
|
||||
self.backend.set(cursor);
|
||||
return;
|
||||
}
|
||||
self.release();
|
||||
@@ -135,9 +135,6 @@ impl Backend for AppKit {
|
||||
fn build(&mut self, request: &Request) -> Option<Self::Cursor> {
|
||||
build(&request.bitmap)
|
||||
}
|
||||
fn current(&self) -> Option<Self::Cursor> {
|
||||
Some(NSCursor::currentCursor())
|
||||
}
|
||||
fn set(&mut self, cursor: &Self::Cursor) {
|
||||
cursor.set();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use std::cell::Cell;
|
||||
struct Fake {
|
||||
window: Option<u8>,
|
||||
suspended: Vec<u8>,
|
||||
current: Option<Key>,
|
||||
sets: usize,
|
||||
builds: usize,
|
||||
restores: Vec<u8>,
|
||||
@@ -27,17 +26,12 @@ impl Backend for Fake {
|
||||
fn restore(&mut self, w: &u8) {
|
||||
self.suspended.retain(|v| v != w);
|
||||
self.restores.push(*w);
|
||||
self.current = None;
|
||||
}
|
||||
fn build(&mut self, r: &Request) -> Option<Key> {
|
||||
self.builds += 1;
|
||||
(!self.fail).then_some(r.key)
|
||||
}
|
||||
fn current(&self) -> Option<Key> {
|
||||
self.current
|
||||
}
|
||||
fn set(&mut self, cursor: &Key) {
|
||||
self.current = Some(*cursor);
|
||||
fn set(&mut self, _: &Key) {
|
||||
self.sets += 1;
|
||||
}
|
||||
}
|
||||
@@ -66,19 +60,16 @@ fn deliver(state: &mut State<Fake>, request: Option<Request>) {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mac_cursor_ownership_deduplicates_reasserts_and_restores() {
|
||||
fn mac_cursor_ownership_reasserts_every_pass_and_restores() {
|
||||
let req = request(1.0);
|
||||
let mut state = fake_state();
|
||||
deliver(&mut state, Some(req.clone()));
|
||||
assert_eq!(state.backend.suspended, [1]);
|
||||
assert_eq!((state.backend.builds, state.backend.sets), (1, 1));
|
||||
// The pointer moves and AppKit shows the system arrow, but the app's current cursor
|
||||
// still reads as ours. The next pass sets it again without rebuilding or re-suspending.
|
||||
deliver(&mut state, Some(req.clone()));
|
||||
assert_eq!(state.backend.sets, 1);
|
||||
// Simulate a platform fallback replacing the cursor after finalization. The next pass
|
||||
// reasserts; this proves the protocol, not AppKit timing or absence of transient flicker.
|
||||
state.backend.current = None;
|
||||
deliver(&mut state, Some(req.clone()));
|
||||
assert_eq!(state.backend.sets, 2);
|
||||
assert_eq!((state.backend.builds, state.backend.sets, state.backend.suspended.len()), (1, 2, 1));
|
||||
deliver(&mut state, None);
|
||||
assert_eq!(state.backend.restores, [1]);
|
||||
deliver(&mut state, Some(req.clone()));
|
||||
|
||||
Reference in New Issue
Block a user