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