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