mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-27 19:27:21 -06:00
Merge pull request #2335 from dotCooCoo/fix/macos-unfocused-cursor
Cursors: the canvas shows only the system pointer on macOS while another app or a sheet has the focus
This commit is contained in:
@@ -530,13 +530,21 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let ui_scale = if app.session.prefs.scale_cursor_with_ui { 1.0 } else { ui.ctx().zoom_factor() };
|
||||
match ui.input(|i| i.pointer.hover_pos()) {
|
||||
// An OS cursor: the system moves it at once, where a painted one trails the pointer
|
||||
// (#444). The system cursor stands in for cursors without a glyph.
|
||||
// (#444). The system cursor stands in for cursors without a glyph. Over a window
|
||||
// without the focus, macOS shows its own pointer whichever cursor the window sets,
|
||||
// and the glyph is not painted there.
|
||||
Some(_) if custom && crate::cursors::os_cursors(crate::cursors::OS_CURSORS, app.services.wayland) => {
|
||||
let ppp = ui.ctx().pixels_per_point() / ui_scale;
|
||||
ui.ctx().set_cursor_image(app.canvas.cursors.get(c, ppp));
|
||||
cursor_icon(c)
|
||||
}
|
||||
Some(hp) if custom && crate::cursors::paint(&painter, c, hp, 1.0 / ui_scale) => egui::CursorIcon::None,
|
||||
Some(hp)
|
||||
if custom
|
||||
&& crate::cursors::paint_glyphs(crate::cursors::HIDES_ONLY_WHEN_KEY, ui.input(|i| i.focused))
|
||||
&& crate::cursors::paint(&painter, c, hp, 1.0 / ui_scale) =>
|
||||
{
|
||||
egui::CursorIcon::None
|
||||
}
|
||||
_ => cursor_icon(c),
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -395,6 +395,18 @@ pub fn os_cursors(build: bool, wayland: bool) -> bool {
|
||||
build && !wayland
|
||||
}
|
||||
|
||||
/// Does the window hide the pointer only while it is the key window? macOS does: winit hides it with
|
||||
/// a cursor rect holding a transparent cursor, and macOS applies the rect only in the key window.
|
||||
/// Over a window behind another app or under a sheet, the system pointer shows (#2020).
|
||||
pub const HIDES_ONLY_WHEN_KEY: bool = cfg!(target_os = "macos");
|
||||
|
||||
/// Does the window paint the glyphs over a hidden pointer, `focused` or not? Not where the system
|
||||
/// shows its pointer over a window without the focus ([`HIDES_ONLY_WHEN_KEY`]): the system pointer
|
||||
/// shows alone there.
|
||||
pub fn paint_glyphs(hides_only_when_key: bool, focused: bool) -> bool {
|
||||
focused || !hides_only_when_key
|
||||
}
|
||||
|
||||
/// The cursor bitmaps handed to the OS, by (cursor, pixels per point): each glyph is rasterized
|
||||
/// once, and handing the same `Arc` every frame keeps the window's OS cursor instead of making a
|
||||
/// new one.
|
||||
@@ -650,6 +662,20 @@ mod tests {
|
||||
out
|
||||
}
|
||||
|
||||
/// One [`frame`] with `focused` set: true for a window that has the focus, false while another app
|
||||
/// or a sheet has it.
|
||||
fn focus_frame(app: &mut VectorcraftApp, ctx: &egui::Context, focused: bool, events: Vec<egui::Event>) -> egui::FullOutput {
|
||||
let time = Some(ctx.input(|i| i.time) + 0.1);
|
||||
let screen_rect = Some(egui::Rect::from_min_size(Pos2::ZERO, vec2(800.0, 600.0)));
|
||||
let raw = egui::RawInput { screen_rect, time, focused, events, ..Default::default() };
|
||||
let mut out = ctx.run_ui(raw, |ui| {
|
||||
app.logic(ui.ctx());
|
||||
app.ui(ui);
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
out
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_canvas_hands_the_tool_cursor_to_the_os_and_idles_while_the_pointer_rests() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
@@ -701,6 +727,32 @@ mod tests {
|
||||
assert_eq!(out.platform_output.cursor_icon, egui::CursorIcon::None);
|
||||
}
|
||||
|
||||
/// On macOS a window without the focus shows the system pointer over the canvas, and the glyph
|
||||
/// is not painted next to it (#2020). With the focus back, the canvas sets `CursorIcon::None` again
|
||||
/// on the next frame, without a pointer move.
|
||||
#[test]
|
||||
fn a_window_without_the_focus_shows_the_system_cursor_alone_on_macos() {
|
||||
// Wayland: every desktop build paints the glyphs.
|
||||
let mut app = VectorcraftApp::new(Session::new(), crate::Services { wayland: true, ..Default::default() });
|
||||
app.run("file.new", json!({"width": 400, "height": 300})).unwrap();
|
||||
app.run("tool.select", json!({"tool": "rectangle"})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
for _ in 0..3 {
|
||||
frame(&mut app, &ctx, vec![]);
|
||||
}
|
||||
let over = app.canvas_rect.unwrap().center();
|
||||
let key = focus_frame(&mut app, &ctx, true, vec![egui::Event::PointerMoved(over)]);
|
||||
assert_eq!(key.platform_output.cursor_icon, egui::CursorIcon::None, "with the focus: the glyph alone");
|
||||
let away = focus_frame(&mut app, &ctx, false, vec![egui::Event::WindowFocused(false), egui::Event::PointerMoved(over + vec2(5.0, 3.0))]);
|
||||
if HIDES_ONLY_WHEN_KEY {
|
||||
assert_eq!(away.platform_output.cursor_icon, egui::CursorIcon::Crosshair, "macOS without the focus: the system pointer alone");
|
||||
} else {
|
||||
assert_eq!(away.platform_output.cursor_icon, egui::CursorIcon::None, "other platforms: the glyph without the focus too");
|
||||
}
|
||||
let back = focus_frame(&mut app, &ctx, true, vec![egui::Event::WindowFocused(true)]);
|
||||
assert_eq!(back.platform_output.cursor_icon, egui::CursorIcon::None, "the focus back: the glyph alone");
|
||||
}
|
||||
|
||||
/// User Interface › Scale Cursor Proportional to UI (#394): off, the tool cursors keep their
|
||||
/// size whatever the UI Scaling; on, they grow with it.
|
||||
#[test]
|
||||
@@ -745,6 +797,13 @@ mod tests {
|
||||
assert!(!os_cursors(false, false) && !os_cursors(false, true), "macOS, the web");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macos_windows_paint_the_glyphs_only_with_the_focus() {
|
||||
assert!(paint_glyphs(true, true), "macOS with the focus");
|
||||
assert!(!paint_glyphs(true, false), "macOS without the focus: the system pointer shows (#2020)");
|
||||
assert!(paint_glyphs(false, true) && paint_glyphs(false, false), "Wayland, the web: painted with the focus or without");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arrow_hotspot_is_tip() {
|
||||
let pts = arrow_points(pos2(10.0, 20.0));
|
||||
|
||||
@@ -608,7 +608,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
anchor or with Alt held over a path (convert) and over another open path's end (join). The Eyedropper has an original pipette glyph with its tip at the hotspot;
|
||||
Use Precise Cursors restores the crosshair. The same glyph feeds OS bitmaps and painted
|
||||
cursors. DPI/hotspot and preference checks in `cursors.rs` and `tests_prefs.rs` cover #1269;
|
||||
actual OS display and Caps Lock remain unverified. Behaviour reference: [public tool-pointer documentation](https://helpx.adobe.com/illustrator/using/tools.html#change_tool_pointers). Tool cursors are painted into the window on Wayland (#1652; not run on Linux, #1645 stays open until confirmed). A Precise Cursors command toggles the preference and can take a shortcut (#1663).
|
||||
actual OS display and Caps Lock remain unverified. Behaviour reference: [public tool-pointer documentation](https://helpx.adobe.com/illustrator/using/tools.html#change_tool_pointers). Tool cursors are painted into the window on Wayland (#1652; not run on Linux, #1645 stays open until confirmed). A Precise Cursors command toggles the preference and can take a shortcut (#1663). On macOS the tool cursor is painted only while the window has the focus. Over a window behind another app, or under the File › Open… sheet, the system pointer shows alone instead of next to the painted cursor (#2020).
|
||||
- **Performance:** 20k shapes + 1k texts render in 27 ms per full-retina frame (7.8 ms zoomed), 7× faster than the first version. The UI thread never blocks. The web build is 7.1 MB gzipped. Print-size images (more than 512 MB decoded, up to 2 GB) draw in the canvas and in raster exports and can be trimmed with Crop Image; an image with a side past 65,535 px is shrunk to fit, a raster export says when an image could not be decoded, and Crop Image's trim leaves CMYK images alone (#1517). On Windows `vectorcraft-cli perf` reports the cores busy and, with `--deep`, its working set, and warns on a busy machine as it does on macOS and Linux (#1502).
|
||||
With `--deep`, each memory row of `vectorcraft-cli perf` also gives the peak so far: the peak working set on Windows and the peak resident memory (`VmHWM`) on Linux. A new last row gives the memory after the SVG export and the save → open → save at the end of the run.
|
||||
The renderer composes the two ink planes of a CMYK document that shows transparency into screen colors on up to as many threads as it rasterizes with, instead of on one. At 2880 × 1800 on 8 threads, a page of 300 shapes at 20% to 90% opacity takes 30 ms a frame instead of 55 ms, and 50,000 paths with one at 99% opacity take 258 ms instead of 361 ms (`vectorcraft-cli bench`), with the same pixels.
|
||||
|
||||
Reference in New Issue
Block a user