1- import { useEventListener } from '@vueuse/core'
1+ import { useEventListener , useMutationObserver } from '@vueuse/core'
22
33import { options } from '@/ui/state'
44import { getCanvas , setLockAltKey , setLockMetaKey } from '@/utils'
55
66let spacePressed = false
7+ let altPressed = false
8+ let duplicateClass : string | null = null
9+ let classSnapshot = new Set < string > ( )
10+ const DUPLICATE_URL_SIGNATURE =
11+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAIZElEQVR4AeyYTWxUVRTH3yD9UCEE'
12+ function extractHotspot ( cursor : string ) : [ number , number ] | null {
13+ const normalized = cursor . replace ( / \s + / g, ' ' )
14+ const match =
15+ normalized . match ( / (?: - w e b k i t - ) ? i m a g e - s e t \( [ ^ ) ] * \) \s * ( \d + (?: \. \d + ) ? ) \s + ( \d + (?: \. \d + ) ? ) / i) ??
16+ normalized . match ( / \) \s * ( \d + (?: \. \d + ) ? ) \s + ( \d + (?: \. \d + ) ? ) / )
17+ if ( ! match ) return null
18+ return [ Number ( match [ 1 ] ) , Number ( match [ 2 ] ) ]
19+ }
20+
21+ function hasDuplicateSignature ( cursor : string ) {
22+ return cursor . includes ( DUPLICATE_URL_SIGNATURE )
23+ }
724
825function pause ( ) {
926 setLockMetaKey ( false )
@@ -15,15 +32,15 @@ function resume() {
1532 return
1633 }
1734 setLockMetaKey ( options . value . deepSelectOn )
18- setLockAltKey ( options . value . measureOn )
35+ syncAltLock ( )
1936}
2037
2138function pauseMeasure ( ) {
2239 setLockAltKey ( false )
2340}
2441
2542function resumeMeasure ( ) {
26- setLockAltKey ( options . value . measureOn )
43+ syncAltLock ( )
2744}
2845
2946let resuming : number | null = null
@@ -43,21 +60,89 @@ function pauseMetaThenResume() {
4360}
4461
4562function keydown ( e : KeyboardEvent ) {
63+ if ( ! options . value . measureOn && e . key === 'Alt' ) {
64+ return
65+ }
66+ if ( ! altPressed && e . key === 'Alt' ) {
67+ altPressed = true
68+ setLockAltKey ( false )
69+ reconcileCursor ( )
70+ }
4671 if ( ! spacePressed && e . key === ' ' ) {
4772 spacePressed = true
4873 pause ( )
4974 }
5075}
5176
5277function keyup ( e : KeyboardEvent ) {
78+ if ( ! options . value . measureOn && e . key === 'Alt' ) {
79+ return
80+ }
81+ if ( altPressed && e . key === 'Alt' ) {
82+ altPressed = false
83+ syncAltLock ( )
84+ reconcileCursor ( )
85+ }
5386 if ( spacePressed && e . key === ' ' ) {
5487 spacePressed = false
5588 resume ( )
5689 }
5790}
5891
92+ function syncAltLock ( ) {
93+ setLockAltKey ( ! altPressed && options . value . measureOn )
94+ }
95+
96+ function isDuplicateCursor ( host : HTMLElement ) {
97+ if ( duplicateClass ) {
98+ return host . classList . contains ( duplicateClass )
99+ }
100+ const cursor = getComputedStyle ( host ) . cursor
101+ const hotspot = extractHotspot ( cursor )
102+ return hotspot != null && hotspot [ 0 ] === 8 && hotspot [ 1 ] === 8 && hasDuplicateSignature ( cursor )
103+ }
104+
105+ function learnDuplicateClass ( host : HTMLElement ) {
106+ if ( duplicateClass ) return
107+ const added = Array . from ( host . classList ) . filter ( ( c ) => ! classSnapshot . has ( c ) )
108+ if ( added . length === 1 ) {
109+ duplicateClass = added [ 0 ]
110+ }
111+ }
112+
113+ function applyCursorCover ( host : HTMLElement ) {
114+ host . dataset . tpCursorOverride = ''
115+ }
116+
117+ function clearCursorCover ( host : HTMLElement ) {
118+ delete host . dataset . tpCursorOverride
119+ }
120+
121+ function reconcileCursor ( host ?: HTMLElement | null ) {
122+ const target = host ?? cursorHost
123+ if ( ! target ) return
124+ if ( ! options . value . measureOn || altPressed ) {
125+ clearCursorCover ( target )
126+ classSnapshot = new Set ( target . classList )
127+ return
128+ }
129+ if ( isDuplicateCursor ( target ) ) {
130+ learnDuplicateClass ( target )
131+ applyCursorCover ( target )
132+ } else {
133+ clearCursorCover ( target )
134+ }
135+ classSnapshot = new Set ( target . classList )
136+ }
137+
138+ let cursorHost : HTMLElement | null = null
139+
59140export function useKeyLock ( ) {
60141 const canvas = getCanvas ( )
142+ cursorHost = canvas ?. parentElement ?. parentElement as HTMLElement | null
143+ if ( cursorHost ) {
144+ classSnapshot = new Set ( cursorHost . classList )
145+ }
61146
62147 useEventListener ( canvas , 'mouseleave' , pause )
63148 useEventListener ( canvas , 'mouseenter' , resume )
@@ -67,10 +152,34 @@ export function useKeyLock() {
67152 useEventListener ( 'keydown' , keydown )
68153 useEventListener ( 'keyup' , keyup )
69154
155+ if ( cursorHost ) {
156+ useMutationObserver (
157+ cursorHost ,
158+ ( ) => {
159+ if ( ! options . value . measureOn ) {
160+ clearCursorCover ( cursorHost ! )
161+ return
162+ }
163+ reconcileCursor ( cursorHost )
164+ } ,
165+ { attributes : true , attributeFilter : [ 'class' ] }
166+ )
167+ }
168+
169+ reconcileCursor ( cursorHost )
170+
70171 watch (
71172 ( ) => options . value . deepSelectOn ,
72173 ( ) => {
73174 setLockMetaKey ( options . value . deepSelectOn )
74175 }
75176 )
177+
178+ watch (
179+ ( ) => options . value . measureOn ,
180+ ( ) => {
181+ syncAltLock ( )
182+ reconcileCursor ( cursorHost )
183+ }
184+ )
76185}
0 commit comments