diff --git a/crates/compositor/src/compositor_linux.rs b/crates/compositor/src/compositor_linux.rs index 3b2632b19..0ea5afd6e 100644 --- a/crates/compositor/src/compositor_linux.rs +++ b/crates/compositor/src/compositor_linux.rs @@ -5075,11 +5075,11 @@ mod tests { /// Les états que le rendu passe en revue (hotspots de `DEFAULT_CURSOR_SPRITES`), et s'ils /// sont centrés : ni tangage ni lacet. const MODEL_STATES: [(&str, [f32; 2], bool); 6] = [ - ("arrow", [0.119, 0.0874], false), - ("pointer", [0.3893, 0.0032], false), - ("text", [0.4375, 0.5333], true), - ("open-hand", [0.4375, 0.1781], false), - ("resize-ew", [0.4881, 0.4706], true), + ("arrow", [0.1205, 0.0881], false), + ("pointer", [0.3874, 0.0032], false), + ("text", [0.4355, 0.5369], true), + ("open-hand", [0.4375, 0.1724], false), + ("resize-ew", [0.485, 0.4706], true), ("not-allowed", [0.5, 0.5], true), ]; diff --git a/crates/compositor/src/compositor_macos.rs b/crates/compositor/src/compositor_macos.rs index c422c4bb2..01e809026 100644 --- a/crates/compositor/src/compositor_macos.rs +++ b/crates/compositor/src/compositor_macos.rs @@ -3709,11 +3709,11 @@ mod tests { /// Les états que le rendu passe en revue (hotspots de `DEFAULT_CURSOR_SPRITES`), et s'ils /// sont centrés : ni tangage ni lacet. const MODEL_STATES: [(&str, [f32; 2], bool); 6] = [ - ("arrow", [0.119, 0.0874], false), - ("pointer", [0.3893, 0.0032], false), - ("text", [0.4375, 0.5333], true), - ("open-hand", [0.4375, 0.1781], false), - ("resize-ew", [0.4881, 0.4706], true), + ("arrow", [0.1205, 0.0881], false), + ("pointer", [0.3874, 0.0032], false), + ("text", [0.4355, 0.5369], true), + ("open-hand", [0.4375, 0.1724], false), + ("resize-ew", [0.485, 0.4706], true), ("not-allowed", [0.5, 0.5], true), ]; diff --git a/crates/compositor/src/cursor_sdf.rs b/crates/compositor/src/cursor_sdf.rs index 987d22429..1dbce9c7e 100644 --- a/crates/compositor/src/cursor_sdf.rs +++ b/crates/compositor/src/cursor_sdf.rs @@ -458,11 +458,11 @@ mod tests { fn the_shipped_arrow_loads() { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../public/cursors/default/arrow.png"); let sdf = CursorSdf::load(path).expect("arrow.png"); - assert_eq!((sdf.width, sdf.height), (42 * 4, 70 * 4)); - assert_eq!(sdf.shape.size, [0.6, 1.0]); + assert_eq!((sdf.width, sdf.height), (83 * 4, 139 * 4)); + assert_eq!(sdf.shape.size, [83.0 / 139.0, 1.0]); assert!(sdf.shape.top < 0.02, "haut de la silhouette : {}", sdf.shape.top); - // Le hotspot (0.119, 0.0874) est la pointe de l'incrustation, dans la silhouette. - assert!(sdf.sample([0.119, 0.0874]) < 0.0); + // Le hotspot (0.1205, 0.0881) est la pointe de l'incrustation, dans la silhouette. + assert!(sdf.sample([0.1205, 0.0881]) < 0.0); assert!(sdf.sample([0.9, 0.1]) > 0.0); assert_eq!(sdf.f16_bytes().len(), sdf.texels.len() * 2); } diff --git a/crates/compositor/src/frame_geometry.rs b/crates/compositor/src/frame_geometry.rs index 18e1f9246..1e99d413c 100644 --- a/crates/compositor/src/frame_geometry.rs +++ b/crates/compositor/src/frame_geometry.rs @@ -6476,7 +6476,7 @@ mod tests { #[test] fn sprite_hotspot_stays_on_target_at_any_size() { let center = [0.4, 0.6]; - let hotspot = [0.119, 0.0874]; // flèche intégrée : la pointe, près du coin haut-gauche + let hotspot = [0.1205, 0.0881]; // flèche intégrée : la pointe, près du coin haut-gauche for (w, h) in [(0.02, 0.04), (0.08, 0.16)] { let dst = cursor_sprite_dst(center, w, h, hotspot); @@ -7091,17 +7091,17 @@ mod tests { /// Les seize états du thème par défaut et leurs hotspots (`DEFAULT_CURSOR_SPRITES`, /// `src/lib/cursor/cursorThemes.ts`). const DEFAULT_SPRITES: [(&str, [f32; 2]); 16] = [ - ("arrow", [0.119, 0.0874]), - ("text", [0.4375, 0.5333]), - ("pointer", [0.3893, 0.0032]), + ("arrow", [0.1205, 0.0881]), + ("text", [0.4355, 0.5369]), + ("pointer", [0.3874, 0.0032]), ("crosshair", [0.4667, 0.4667]), - ("open-hand", [0.4375, 0.1781]), - ("closed-hand", [0.3889, 0.451]), - ("resize-ew", [0.4881, 0.4706]), + ("open-hand", [0.4375, 0.1724]), + ("closed-hand", [0.3889, 0.4455]), + ("resize-ew", [0.485, 0.4706]), ("resize-ns", [0.5, 0.5]), ("resize-nesw", [0.5, 0.5]), ("resize-nwse", [0.5, 0.5]), - ("move", [0.4444, 0.4444]), + ("move", [0.4437, 0.4437]), ("not-allowed", [0.5, 0.5]), ("wait", [0.5, 0.5]), ("app-starting", [0.05, 0.0537]), diff --git a/crates/compositor/tests/cursor_model_render.rs b/crates/compositor/tests/cursor_model_render.rs index 7c6e52eb8..9b473ec8f 100644 --- a/crates/compositor/tests/cursor_model_render.rs +++ b/crates/compositor/tests/cursor_model_render.rs @@ -41,17 +41,17 @@ const CONTACT_S: f32 = 0.0495; /// Les seize états du thème par défaut et leurs hotspots (`DEFAULT_CURSOR_SPRITES`, /// `src/lib/cursor/cursorThemes.ts`). const STATES: [(&str, [f32; 2]); 16] = [ - ("arrow", [0.119, 0.0874]), - ("text", [0.4375, 0.5333]), - ("pointer", [0.3893, 0.0032]), + ("arrow", [0.1205, 0.0881]), + ("text", [0.4355, 0.5369]), + ("pointer", [0.3874, 0.0032]), ("crosshair", [0.4667, 0.4667]), - ("open-hand", [0.4375, 0.1781]), - ("closed-hand", [0.3889, 0.451]), - ("resize-ew", [0.4881, 0.4706]), + ("open-hand", [0.4375, 0.1724]), + ("closed-hand", [0.3889, 0.4455]), + ("resize-ew", [0.485, 0.4706]), ("resize-ns", [0.5, 0.5]), ("resize-nesw", [0.5, 0.5]), ("resize-nwse", [0.5, 0.5]), - ("move", [0.4444, 0.4444]), + ("move", [0.4437, 0.4437]), ("not-allowed", [0.5, 0.5]), ("wait", [0.5, 0.5]), ("app-starting", [0.05, 0.0537]), diff --git a/crates/compositor/tests/cursor_tap_render.rs b/crates/compositor/tests/cursor_tap_render.rs index b29116c7a..8b908f922 100644 --- a/crates/compositor/tests/cursor_tap_render.rs +++ b/crates/compositor/tests/cursor_tap_render.rs @@ -167,8 +167,8 @@ fn scene_json(rotation: &str, hide: bool, click_bounce: f32) -> String { "zoomRegions":[{{"clipIndex":0,"startSec":0,"endSec":10,"scale":1,"focusX":0.5,"focusY":0.5,"focusMode":"manual","rotation":{rotation},"hideCursor":{hide}}}], "annotations":[], "cursor":{{"show":true,"size":4,"smoothing":0.5,"motionBlur":0.5,"clickBounce":{click_bounce},"model3d":true,"clipToBounds":false,"theme":"default", - "cursorSprites":{{"arrow":{{"path":"{dir}/arrow.png","hotspotX":0.119,"hotspotY":0.0874}}, - "pointer":{{"path":"{dir}/pointer.png","hotspotX":0.3893,"hotspotY":0.0032}}}}}}, + "cursorSprites":{{"arrow":{{"path":"{dir}/arrow.png","hotspotX":0.1205,"hotspotY":0.0881}}, + "pointer":{{"path":"{dir}/pointer.png","hotspotX":0.3874,"hotspotY":0.0032}}}}}}, "cropByClip":[null], "output":{{"width":1280,"height":720,"fps":30}}}}"## ) diff --git a/crates/compositor/tests/device_frame_render.rs b/crates/compositor/tests/device_frame_render.rs index ad9e62aab..6b649a005 100644 --- a/crates/compositor/tests/device_frame_render.rs +++ b/crates/compositor/tests/device_frame_render.rs @@ -1041,7 +1041,7 @@ fn orbit_clip_frames() { std::fs::write(&path, format!(r#"{{"samples":[{}]}}"#, samples.join(","))).expect("sidecar"); let track = CursorTrack::load(path.to_str().unwrap(), 0.0, 10.0).expect("piste curseur"); let cursor = format!( - r#"{{"show":true,"size":4,"smoothing":0.2,"motionBlur":0,"clickBounce":2.5,"model3d":true,"clipToBounds":false,"theme":"default","cursorSprites":{{"arrow":{{"path":"{}/arrow.png","hotspotX":0.119,"hotspotY":0.0874}}}}}}"#, + r#"{{"show":true,"size":4,"smoothing":0.2,"motionBlur":0,"clickBounce":2.5,"model3d":true,"clipToBounds":false,"theme":"default","cursorSprites":{{"arrow":{{"path":"{}/arrow.png","hotspotX":0.1205,"hotspotY":0.0881}}}}}}"#, std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("../../public/cursors/default") .to_string_lossy() diff --git a/crates/compositor/tests/follow_camera_render.rs b/crates/compositor/tests/follow_camera_render.rs index e1aaa9b06..8678a8f87 100644 --- a/crates/compositor/tests/follow_camera_render.rs +++ b/crates/compositor/tests/follow_camera_render.rs @@ -381,7 +381,7 @@ fn orbit_scene(source: &str, regions: &str, (w, h): (u32, u32)) -> Scene { "zoomRegions":[{regions}], "annotations":[], "cursor":{{"show":true,"size":2.2,"smoothing":0,"motionBlur":0,"clickBounce":2.5,"model3d":true,"clipToBounds":false,"theme":"default", - "cursorSprites":{{"arrow":{{"path":"{arrow}","hotspotX":0.119,"hotspotY":0.0874}}}}}}, + "cursorSprites":{{"arrow":{{"path":"{arrow}","hotspotX":0.1205,"hotspotY":0.0881}}}}}}, "cropByClip":[null], "output":{{"width":{w},"height":{h},"fps":30}}}}"## )) diff --git a/public/cursors/default/app-starting.png b/public/cursors/default/app-starting.png index 9a1649eea..b36bdf895 100644 Binary files a/public/cursors/default/app-starting.png and b/public/cursors/default/app-starting.png differ diff --git a/public/cursors/default/arrow.png b/public/cursors/default/arrow.png index 85998081e..2dd2686e5 100644 Binary files a/public/cursors/default/arrow.png and b/public/cursors/default/arrow.png differ diff --git a/public/cursors/default/closed-hand.png b/public/cursors/default/closed-hand.png index 621fba8a2..41852125a 100644 Binary files a/public/cursors/default/closed-hand.png and b/public/cursors/default/closed-hand.png differ diff --git a/public/cursors/default/crosshair.png b/public/cursors/default/crosshair.png index 09d40cd95..edb851773 100644 Binary files a/public/cursors/default/crosshair.png and b/public/cursors/default/crosshair.png differ diff --git a/public/cursors/default/help.png b/public/cursors/default/help.png index 6746db84d..79d347999 100644 Binary files a/public/cursors/default/help.png and b/public/cursors/default/help.png differ diff --git a/public/cursors/default/move.png b/public/cursors/default/move.png index 69a819fcf..25694ef97 100644 Binary files a/public/cursors/default/move.png and b/public/cursors/default/move.png differ diff --git a/public/cursors/default/not-allowed.png b/public/cursors/default/not-allowed.png index f7f35d12e..cc7fad557 100644 Binary files a/public/cursors/default/not-allowed.png and b/public/cursors/default/not-allowed.png differ diff --git a/public/cursors/default/open-hand.png b/public/cursors/default/open-hand.png index 147ebdabf..f3271c042 100644 Binary files a/public/cursors/default/open-hand.png and b/public/cursors/default/open-hand.png differ diff --git a/public/cursors/default/pointer.png b/public/cursors/default/pointer.png index a89d388bf..60b12be56 100644 Binary files a/public/cursors/default/pointer.png and b/public/cursors/default/pointer.png differ diff --git a/public/cursors/default/resize-ew.png b/public/cursors/default/resize-ew.png index b15a4fe5f..a3a6f1f37 100644 Binary files a/public/cursors/default/resize-ew.png and b/public/cursors/default/resize-ew.png differ diff --git a/public/cursors/default/resize-nesw.png b/public/cursors/default/resize-nesw.png index 0f9746865..d17c21a78 100644 Binary files a/public/cursors/default/resize-nesw.png and b/public/cursors/default/resize-nesw.png differ diff --git a/public/cursors/default/resize-ns.png b/public/cursors/default/resize-ns.png index 229ddfaab..4607b9176 100644 Binary files a/public/cursors/default/resize-ns.png and b/public/cursors/default/resize-ns.png differ diff --git a/public/cursors/default/resize-nwse.png b/public/cursors/default/resize-nwse.png index eac720eb6..6c27c919c 100644 Binary files a/public/cursors/default/resize-nwse.png and b/public/cursors/default/resize-nwse.png differ diff --git a/public/cursors/default/text.png b/public/cursors/default/text.png index 27a5e5ef9..1d4acdb82 100644 Binary files a/public/cursors/default/text.png and b/public/cursors/default/text.png differ diff --git a/public/cursors/default/up-arrow.png b/public/cursors/default/up-arrow.png index a32665e50..830a3a46a 100644 Binary files a/public/cursors/default/up-arrow.png and b/public/cursors/default/up-arrow.png differ diff --git a/public/cursors/default/wait.png b/public/cursors/default/wait.png index e8ba48724..512855887 100644 Binary files a/public/cursors/default/wait.png and b/public/cursors/default/wait.png differ diff --git a/scripts/generate-default-cursor-sprites.mjs b/scripts/generate-default-cursor-sprites.mjs index 2fb957680..9f98665c0 100644 --- a/scripts/generate-default-cursor-sprites.mjs +++ b/scripts/generate-default-cursor-sprites.mjs @@ -25,7 +25,10 @@ import { chromium } from "playwright"; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const SRC_DIR = path.join(ROOT, "src", "assets", "cursors"); const OUT_DIR = path.join(ROOT, "public", "cursors", "default"); -const OUT_SIZE = 128; +// The arrow comes out 139 px tall: sharp up to the top of the size slider (164 px in a 1080p +// export), where a 128 box (70 px) was stretched 2.3x and blurred. No mipmaps, so not more: +// at the default size a bigger master would alias. +const OUT_SIZE = 256; /** * cursorType -> { file, hotspotX, hotspotY } in the 32-logical reference. diff --git a/src/components/ai-edition/CursorPane.levels.test.tsx b/src/components/ai-edition/CursorPane.levels.test.tsx index 5b62a22c0..73fa1ffe4 100644 --- a/src/components/ai-edition/CursorPane.levels.test.tsx +++ b/src/components/ai-edition/CursorPane.levels.test.tsx @@ -43,15 +43,22 @@ const pressed = (group: string) => screen.getByRole("group", { name: group }).querySelectorAll('button[aria-pressed="true"]'); describe("CursorPane named levels", () => { - it("names the size and the click bounce instead of showing a number", () => { - renderWithCursor({ cursorSize: 2, cursorClickBounce: 0 }); - expect([...pressed("Size")].map((b) => b.textContent)).toEqual(["Large"]); + it("names the click bounce instead of showing a number", () => { + renderWithCursor({ cursorClickBounce: 0 }); expect([...pressed("Click bounce")].map((b) => b.textContent)).toEqual(["None"]); - expect(screen.queryByRole("slider", { name: "Size" })).toBeNull(); }); it("presses nothing for a stored value between two levels", () => { - renderWithCursor({ cursorSize: 2.4 }); - expect(pressed("Size")).toHaveLength(0); + renderWithCursor({ cursorClickBounce: 1.5 }); + expect(pressed("Click bounce")).toHaveLength(0); + }); +}); + +describe("CursorPane size", () => { + // Named steps stopped at 2.75; people asked for at least twice that. + it("is a slider from the default up to four times it", () => { + renderWithCursor({ cursorSize: 4.5 }); + const slider = screen.getByRole("slider", { name: "Size" }) as HTMLInputElement; + expect([slider.min, slider.max, slider.value]).toEqual(["1.5", "6", "4.5"]); }); }); diff --git a/src/components/ai-edition/RightPanes.tsx b/src/components/ai-edition/RightPanes.tsx index 0128e813c..dd73092e0 100644 --- a/src/components/ai-edition/RightPanes.tsx +++ b/src/components/ai-edition/RightPanes.tsx @@ -112,6 +112,7 @@ import { type FrameTheme, RECORDING_FRAMES, type RecordingFrame, + SETTING_BOUNDS, WEBCAM_ANCHOR_GRID, WEBCAM_SIZE_MAX, WEBCAM_SIZE_MIN, @@ -3889,17 +3890,23 @@ export function CursorPane() { > ) : null} - {namedLevelRow( - ts("cursor.size"), - CURSOR_SIZE_LEVELS.map((level) => ({ value: level.value, label: ts(level.labelKey) })), - settings.cursor.size, - !hasDocument, - (size) => { - void set({ cursor: { size } }); - if (isNativeCompositorActive()) setNativeParam("cursorSize", size); - }, - )}