]> gitweb.factorcode.org Git - factor.git/blob - basis/ui/gestures/gestures.factor
scryfall: better moxfield words
[factor.git] / basis / ui / gestures / gestures.factor
1 ! Copyright (C) 2005, 2010 Slava Pestov.
2 ! See https://factorcode.org/license.txt for BSD license.
3 USING: accessors arrays ascii assocs boxes calendar classes columns
4 combinators combinators.short-circuit deques kernel make math
5 math.order math.parser math.vectors namespaces sequences sets system
6 timers ui.gadgets ui.gadgets.private words ;
7 IN: ui.gestures
8
9 : get-gesture-handler ( gesture gadget -- quot )
10     class-of superclasses-of [ "gestures" word-prop ] map assoc-stack ;
11
12 GENERIC: handle-gesture ( gesture gadget -- ? )
13
14 M: object handle-gesture
15     [ nip ]
16     [ get-gesture-handler ] 2bi
17     dup [ call( gadget -- ) f ] [ 2drop t ] if ;
18
19 GENERIC: handles-gesture? ( gesture gadget -- ? )
20
21 M: object handles-gesture?
22     get-gesture-handler >boolean ;
23
24 : parents-handle-gesture? ( gesture gadget -- ? )
25     [ handles-gesture? not ] with each-parent not ;
26
27 : set-gestures ( class hash -- ) "gestures" set-word-prop ;
28
29 : gesture-queue ( -- deque ) \ gesture-queue get ;
30
31 GENERIC: send-queued-gesture ( request -- )
32
33 TUPLE: send-gesture-tuple gesture gadget ;
34
35 M: send-gesture-tuple send-queued-gesture
36     [ gesture>> ] [ gadget>> ] bi handle-gesture drop ;
37
38 : queue-gesture ( ... class -- )
39     boa gesture-queue push-front notify-ui-thread ; inline
40
41 : send-gesture ( gesture gadget -- )
42     \ send-gesture-tuple queue-gesture ;
43
44 : each-gesture ( gesture seq -- ) [ send-gesture ] with each ;
45
46 TUPLE: propagate-gesture-tuple gesture gadget ;
47
48 : resend-gesture ( gesture gadget -- ? )
49     [ handle-gesture ] with each-parent ;
50
51 M: propagate-gesture-tuple send-queued-gesture
52     [ gesture>> ] [ gadget>> ] bi resend-gesture drop ;
53
54 : propagate-gesture ( gesture gadget -- )
55     \ propagate-gesture-tuple queue-gesture ;
56
57 TUPLE: propagate-key-gesture-tuple gesture world ;
58
59 : world-focus ( world -- gadget )
60     [ focus>> ] [ world-focus ] ?when ;
61
62 M: propagate-key-gesture-tuple send-queued-gesture
63     [ gesture>> ] [ world>> world-focus ] bi
64     [ handle-gesture ] with each-parent drop ;
65
66 :: propagate-key-gesture ( gesture world -- )
67     world world-focus preedit? [
68         gesture world \ propagate-key-gesture-tuple queue-gesture
69     ] unless ;
70
71 TUPLE: user-input-tuple string world ;
72
73 M: user-input-tuple send-queued-gesture
74     [ string>> ] [ world>> world-focus ] bi
75     [ user-input* ] with each-parent drop ;
76
77 : user-input ( string world -- )
78     '[ _ \ user-input-tuple queue-gesture ] unless-empty ;
79
80 ! Gesture objects
81 TUPLE: drag # ;             C: <drag> drag
82 TUPLE: button-up mods # ;   C: <button-up> button-up
83 TUPLE: button-down mods # ; C: <button-down> button-down
84 TUPLE: file-drop mods ;     C: <file-drop> file-drop
85
86 SYMBOL: dropped-files
87
88 SINGLETONS:
89     motion
90     mouse-scroll
91     mouse-enter mouse-leave
92     lose-focus gain-focus ;
93
94 ! Higher-level actions
95 SINGLETONS:
96     undo-action redo-action
97     cut-action copy-action paste-action
98     delete-action select-all-action
99     left-action right-action up-action down-action
100     zoom-in-action zoom-out-action
101     new-action open-action save-action save-as-action
102     revert-action close-action ;
103
104 UNION: action
105     undo-action redo-action
106     cut-action copy-action paste-action
107     delete-action select-all-action
108     left-action right-action up-action down-action
109     zoom-in-action zoom-out-action
110     new-action open-action save-action save-as-action
111     revert-action close-action ;
112
113 CONSTANT: action-gestures
114     {
115         { "z" undo-action }
116         { "y" redo-action }
117         { "x" cut-action }
118         { "c" copy-action }
119         { "v" paste-action }
120         { "a" select-all-action }
121         { "n" new-action }
122         { "o" open-action }
123         { "s" save-action }
124         { "S" save-as-action }
125         { "w" close-action }
126     }
127
128 ! Modifiers
129 SYMBOLS: C+ A+ M+ S+ ;
130
131 TUPLE: key-gesture mods sym ;
132
133 TUPLE: key-down < key-gesture ;
134
135 : new-key-gesture ( mods sym action? class -- key-gesture )
136     [ [ [ S+ swap remove f like ] dip ] unless ] dip boa ; inline
137
138 : <key-down> ( mods sym action? -- key-down )
139     key-down new-key-gesture ;
140
141 TUPLE: key-up < key-gesture ;
142
143 : <key-up> ( mods sym action? -- key-up )
144     key-up new-key-gesture ;
145
146 ! Hand state
147
148 ! Note that these are only really useful inside an event
149 ! handler, and that the locations hand-loc and hand-click-loc
150 ! are in the coordinate system of the world which contains
151 ! the gadget in question.
152 SYMBOL: hand-gadget
153 SYMBOL: hand-world
154 SYMBOL: hand-loc
155 { 0 0 } hand-loc set-global
156
157 SYMBOL: hand-clicked
158 SYMBOL: hand-click-loc
159 SYMBOL: hand-click#
160 SYMBOL: hand-last-button
161 SYMBOL: hand-last-time
162 0 hand-last-button set-global
163 0 hand-last-time set-global
164
165 SYMBOL: hand-buttons
166 V{ } clone hand-buttons set-global
167
168 SYMBOL: scroll-direction
169 { 0 0 } scroll-direction set-global
170
171 SYMBOL: double-click-timeout
172 300 milliseconds double-click-timeout set-global
173
174 : hand-moved? ( -- ? )
175     hand-loc get-global hand-click-loc get-global = not ;
176
177 : button-gesture ( gesture -- )
178     hand-clicked get-global propagate-gesture ;
179
180 : drag-gesture ( -- )
181     hand-buttons get-global
182     [ first <drag> button-gesture ] unless-empty ;
183
184 SYMBOL: drag-timer
185
186 <box> drag-timer set-global
187
188 : start-drag-timer ( -- )
189     hand-buttons get-global empty? [
190         [ drag-gesture ]
191         300 milliseconds
192         100 milliseconds
193         <timer>
194         [ drag-timer get-global >box ]
195         [ start-timer ] bi
196     ] when ;
197
198 : stop-drag-timer ( -- )
199     hand-buttons get-global empty? [
200         drag-timer get-global ?box
201         [ stop-timer ] [ drop ] if
202     ] when ;
203
204 : fire-motion ( -- )
205     hand-buttons get-global empty? [
206         motion hand-gadget get-global propagate-gesture
207     ] [
208         drag-gesture
209     ] if ;
210
211 : hand-gestures ( new old -- )
212     drop-prefix <reversed>
213     mouse-leave swap each-gesture
214     mouse-enter swap each-gesture ;
215
216 : forget-rollover ( -- )
217     f hand-world set-global
218     hand-gadget get-global
219     [ f hand-gadget set-global f ] dip
220     parents hand-gestures ;
221
222 : send-lose-focus ( gadget -- )
223     lose-focus swap send-gesture ;
224
225 : send-gain-focus ( gadget -- )
226     gain-focus swap send-gesture ;
227
228 : focus-child ( child gadget ? -- )
229     [
230         dup focus>> [
231             dup send-lose-focus
232             f swap t focus-child
233         ] when*
234         dupd focus<< [
235             send-gain-focus
236         ] when*
237     ] [
238         focus<<
239     ] if ;
240
241 : modifier ( mod modifiers -- seq )
242     [ second swap bitand 0 > ] with filter
243     0 <column> members [ f ] [ >array ] if-empty ;
244
245 : drag-loc ( -- loc )
246     hand-loc get-global hand-click-loc get-global v- ;
247
248 : hand-rel ( gadget -- loc )
249     hand-loc get-global swap screen-loc v- ;
250
251 : hand-click-rel ( gadget -- loc )
252     hand-click-loc get-global swap screen-loc v- ;
253
254 : multi-click-timeout? ( -- ? )
255     nano-count hand-last-time get - nanoseconds
256     double-click-timeout get before=? ;
257
258 : multi-click-button? ( button -- button ? )
259     dup hand-last-button get = ;
260
261 : multi-click-position? ( -- ? )
262     hand-loc get-global hand-click-loc get-global distance 10 <= ;
263
264 : multi-click? ( button -- ? )
265     {
266         [ multi-click-timeout? ]
267         [ multi-click-button? ]
268         [ multi-click-position? ]
269     } 0&& nip ;
270
271 : update-click# ( button -- )
272     [
273         dup multi-click? [
274             hand-click# inc
275         ] [
276             1 hand-click# namespaces:set
277         ] if
278         hand-last-button namespaces:set
279         nano-count hand-last-time namespaces:set
280     ] with-global ;
281
282 : update-clicked ( -- )
283     hand-gadget get-global hand-clicked set-global
284     hand-loc get-global hand-click-loc set-global ;
285
286 : under-hand ( -- seq )
287     hand-gadget get-global parents <reversed> ;
288
289 : move-hand ( loc world -- )
290     dup hand-world set-global
291     under-hand [
292         over hand-loc set-global
293         pick-up hand-gadget set-global
294         under-hand
295     ] dip hand-gestures ;
296
297 : send-button-down ( gesture loc world -- )
298     move-hand
299     start-drag-timer
300     dup #>>
301     dup update-click# hand-buttons get-global push
302     update-clicked
303     button-gesture ;
304
305 : send-button-up ( gesture loc world -- )
306     move-hand
307     dup #>> hand-buttons get-global remove! drop
308     stop-drag-timer
309     button-gesture ;
310
311 : send-scroll ( direction loc world -- )
312     move-hand
313     scroll-direction set-global
314     mouse-scroll hand-gadget get-global propagate-gesture ;
315
316 : send-action ( world gesture -- )
317     swap world-focus propagate-gesture ;
318
319 GENERIC: gesture>string ( gesture -- string/f )
320
321 HOOK: modifiers>string os ( modifiers -- string )
322
323 M: macosx modifiers>string
324     [
325         {
326             { M+ [ "\u002318" ] }
327             { A+ [ "\u002325" ] }
328             { S+ [ "\u0021e7" ] }
329             { C+ [ "\u002303" ] }
330         } case
331     ] map "" concat-as ;
332
333 M: object modifiers>string
334     [ name>> ] map "" concat-as ;
335
336 HOOK: keysym>string os ( keysym -- string )
337
338 M: macosx keysym>string >upper ;
339
340 M: object keysym>string dup length 1 = [ >lower ] when ;
341
342 M: key-down gesture>string
343     [ mods>> ] [ sym>> ] bi
344     {
345         { [ dup { [ length 1 = ] [ first LETTER? ] } 1&& ] [ [ S+ prefix ] dip ] }
346         { [ dup " " = ] [ drop "SPACE" ] }
347         [ ]
348     } cond
349     [ modifiers>string ] [ keysym>string ] bi* append ;
350
351 M: button-up gesture>string
352     [
353         dup mods>> modifiers>string %
354         "Click Button" %
355         #>> [ " " % # ] when*
356     ] "" make ;
357
358 M: button-down gesture>string
359     [
360         dup mods>> modifiers>string %
361         "Press Button" %
362         #>> [ " " % # ] when*
363     ] "" make ;
364
365 M: file-drop gesture>string drop "Drop files" ;
366
367 M: left-action gesture>string drop "Swipe left" ;
368
369 M: right-action gesture>string drop "Swipe right" ;
370
371 M: up-action gesture>string drop "Swipe up" ;
372
373 M: down-action gesture>string drop "Swipe down" ;
374
375 M: zoom-in-action gesture>string drop "Zoom in" ;
376
377 M: zoom-out-action gesture>string drop "Zoom out (pinch)" ;
378
379 HOOK: action-modifier os ( -- mod )
380
381 M: object action-modifier C+ ;
382 M: macosx action-modifier M+ ;
383
384 M: action gesture>string
385     action-gestures value-at
386     action-modifier 1array
387     swap f <key-down> gesture>string ;
388
389 M: object gesture>string drop f ;