]> gitweb.factorcode.org Git - factor.git/blob - basis/ui/backend/cocoa/views/views.factor
bind file management action gestures to standard cocoa menu items
[factor.git] / basis / ui / backend / cocoa / views / views.factor
1 ! Copyright (C) 2006, 2008 Slava Pestov
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors alien alien.c-types arrays assocs cocoa kernel math
4 cocoa.messages cocoa.subclassing cocoa.classes cocoa.views
5 cocoa.application cocoa.pasteboard cocoa.types cocoa.windows sequences
6 ui ui.private ui.gadgets ui.gadgets.private ui.gadgets.worlds
7 ui.gestures core-foundation.strings core-graphics core-graphics.types
8 threads combinators math.rectangles ;
9 IN: ui.backend.cocoa.views
10
11 : send-mouse-moved ( view event -- )
12     [ mouse-location ] [ drop window ] 2bi move-hand fire-motion yield ;
13
14 : button ( event -- n )
15     #! Cocoa -> Factor UI button mapping
16     -> buttonNumber H{ { 0 1 } { 2 2 } { 1 3 } } at ;
17
18 CONSTANT: modifiers
19     {
20         { S+ HEX: 20000 }
21         { C+ HEX: 40000 }
22         { A+ HEX: 100000 }
23         { M+ HEX: 80000 }
24     }
25
26 CONSTANT: key-codes
27     H{
28         { 71 "CLEAR" }
29         { 36 "RET" }
30         { 76 "ENTER" }
31         { 53 "ESC" }
32         { 48 "TAB" }
33         { 51 "BACKSPACE" }
34         { 115 "HOME" }
35         { 117 "DELETE" }
36         { 119 "END" }
37         { 122 "F1" }
38         { 120 "F2" }
39         { 99 "F3" }
40         { 118 "F4" }
41         { 96 "F5" }
42         { 97 "F6" }
43         { 98 "F7" }
44         { 100 "F8" }
45         { 123 "LEFT" }
46         { 124 "RIGHT" }
47         { 125 "DOWN" }
48         { 126 "UP" }
49         { 116 "PAGE_UP" }
50         { 121 "PAGE_DOWN" }
51     }
52
53 : key-code ( event -- string ? )
54     dup -> keyCode key-codes at
55     [ t ] [ -> charactersIgnoringModifiers CF>string f ] ?if ;
56
57 : event-modifiers ( event -- modifiers )
58     -> modifierFlags modifiers modifier ;
59
60 : key-event>gesture ( event -- modifiers keycode action? )
61     [ event-modifiers ] [ key-code ] bi ;
62
63 : send-key-event ( view gesture -- )
64     swap window propagate-key-gesture ;
65
66 : interpret-key-event ( view event -- )
67     NSArray swap -> arrayWithObject: -> interpretKeyEvents: ;
68
69 : send-key-down-event ( view event -- )
70     [ key-event>gesture <key-down> send-key-event ]
71     [ interpret-key-event ]
72     2bi ;
73
74 : send-key-up-event ( view event -- )
75     key-event>gesture <key-up> send-key-event ;
76
77 : mouse-event>gesture ( event -- modifiers button )
78     [ event-modifiers ] [ button ] bi ;
79
80 : send-button-down$ ( view event -- )
81     [ nip mouse-event>gesture <button-down> ]
82     [ mouse-location ]
83     [ drop window ]
84     2tri send-button-down ;
85
86 : send-button-up$ ( view event -- )
87     [ nip mouse-event>gesture <button-up> ]
88     [ mouse-location ]
89     [ drop window ]
90     2tri send-button-up ;
91
92 : send-wheel$ ( view event -- )
93     [ nip [ -> deltaX ] [ -> deltaY ] bi [ sgn neg ] bi@ 2array ]
94     [ mouse-location ]
95     [ drop window ]
96     2tri send-wheel ;
97
98 : send-action$ ( view event gesture -- junk )
99     [ drop window ] dip send-action f ;
100
101 : add-resize-observer ( observer object -- )
102     [
103         "updateFactorGadgetSize:"
104         "NSViewFrameDidChangeNotification" <NSString>
105     ] dip add-observer ;
106
107 : string-or-nil? ( NSString -- ? )
108     [ CF>string NSStringPboardType = ] [ t ] if* ;
109
110 : valid-service? ( gadget send-type return-type -- ? )
111     2dup [ string-or-nil? ] [ string-or-nil? ] bi* and
112     [ drop [ gadget-selection? ] [ drop t ] if ] [ 3drop f ] if ;
113
114 : NSRect>rect ( NSRect world -- rect )
115     [ [ [ CGRect-x ] [ CGRect-y ] bi ] [ dim>> second ] bi* swap - 2array ]
116     [ drop [ CGRect-w ] [ CGRect-h ] bi 2array ]
117     2bi <rect> ;
118
119 : rect>NSRect ( rect world -- NSRect )
120     [ [ loc>> first2 ] [ dim>> second ] bi* swap - ]
121     [ drop dim>> first2 ]
122     2bi <CGRect> ;
123
124 CLASS: {
125     { +superclass+ "NSOpenGLView" }
126     { +name+ "FactorView" }
127     { +protocols+ { "NSTextInput" } }
128 }
129
130 ! Rendering
131 { "drawRect:" "void" { "id" "SEL" "NSRect" }
132     [ 2drop window relayout-1 ]
133 }
134
135 ! Events
136 { "acceptsFirstMouse:" "char" { "id" "SEL" "id" }
137     [ 3drop 1 ]
138 }
139
140 { "mouseEntered:" "void" { "id" "SEL" "id" }
141     [ nip send-mouse-moved ]
142 }
143
144 { "mouseExited:" "void" { "id" "SEL" "id" }
145     [ 3drop forget-rollover ]
146 }
147
148 { "mouseMoved:" "void" { "id" "SEL" "id" }
149     [ nip send-mouse-moved ]
150 }
151
152 { "mouseDragged:" "void" { "id" "SEL" "id" }
153     [ nip send-mouse-moved ]
154 }
155
156 { "rightMouseDragged:" "void" { "id" "SEL" "id" }
157     [ nip send-mouse-moved ]
158 }
159
160 { "otherMouseDragged:" "void" { "id" "SEL" "id" }
161     [ nip send-mouse-moved ]
162 }
163
164 { "mouseDown:" "void" { "id" "SEL" "id" }
165     [ nip send-button-down$ ]
166 }
167
168 { "mouseUp:" "void" { "id" "SEL" "id" }
169     [ nip send-button-up$ ]
170 }
171
172 { "rightMouseDown:" "void" { "id" "SEL" "id" }
173     [ nip send-button-down$ ]
174 }
175
176 { "rightMouseUp:" "void" { "id" "SEL" "id" }
177     [ nip send-button-up$ ]
178 }
179
180 { "otherMouseDown:" "void" { "id" "SEL" "id" }
181     [ nip send-button-down$ ]
182 }
183
184 { "otherMouseUp:" "void" { "id" "SEL" "id" }
185     [ nip send-button-up$ ]
186 }
187
188 { "scrollWheel:" "void" { "id" "SEL" "id" }
189     [ nip send-wheel$ ]
190 }
191
192 { "keyDown:" "void" { "id" "SEL" "id" }
193     [ nip send-key-down-event ]
194 }
195
196 { "keyUp:" "void" { "id" "SEL" "id" }
197     [ nip send-key-up-event ]
198 }
199
200 { "undo:" "id" { "id" "SEL" "id" }
201     [ nip undo-action send-action$ ]
202 }
203
204 { "redo:" "id" { "id" "SEL" "id" }
205     [ nip redo-action send-action$ ]
206 }
207
208 { "cut:" "id" { "id" "SEL" "id" }
209     [ nip cut-action send-action$ ]
210 }
211
212 { "copy:" "id" { "id" "SEL" "id" }
213     [ nip copy-action send-action$ ]
214 }
215
216 { "paste:" "id" { "id" "SEL" "id" }
217     [ nip paste-action send-action$ ]
218 }
219
220 { "delete:" "id" { "id" "SEL" "id" }
221     [ nip delete-action send-action$ ]
222 }
223
224 { "selectAll:" "id" { "id" "SEL" "id" }
225     [ nip select-all-action send-action$ ]
226 }
227
228 { "newDocument:" "id" { "id" "SEL" "id" }
229     [ nip new-action send-action$ ]
230 }
231
232 { "openDocument:" "id" { "id" "SEL" "id" }
233     [ nip open-action send-action$ ]
234 }
235
236 { "saveDocument:" "id" { "id" "SEL" "id" }
237     [ nip save-action send-action$ ]
238 }
239
240 { "saveDocumentAs:" "id" { "id" "SEL" "id" }
241     [ nip save-as-action send-action$ ]
242 }
243
244 { "revertDocumentToSaved:" "id" { "id" "SEL" "id" }
245     [ nip revert-action send-action$ ]
246 }
247
248 ! Multi-touch gestures: this is undocumented.
249 ! http://cocoadex.com/2008/02/nsevent-modifications-swipe-ro.html
250 { "magnifyWithEvent:" "void" { "id" "SEL" "id" }
251     [
252         nip
253         dup -> deltaZ sgn {
254             {  1 [ zoom-in-action send-action$ ] }
255             { -1 [ zoom-out-action send-action$ ] }
256             {  0 [ 2drop ] }
257         } case
258     ]
259 }
260
261 { "swipeWithEvent:" "void" { "id" "SEL" "id" }
262     [
263         nip
264         dup -> deltaX sgn {
265             {  1 [ left-action send-action$ ] }
266             { -1 [ right-action send-action$ ] }
267             {  0
268                 [
269                     dup -> deltaY sgn {
270                         {  1 [ up-action send-action$ ] }
271                         { -1 [ down-action send-action$ ] }
272                         {  0 [ 2drop ] }
273                     } case
274                 ]
275             }
276         } case
277     ]
278 }
279
280 ! "rotateWithEvent:" "void" { "id" "SEL" "id" }}
281
282 { "acceptsFirstResponder" "char" { "id" "SEL" }
283     [ 2drop 1 ]
284 }
285
286 ! Services
287 { "validRequestorForSendType:returnType:" "id" { "id" "SEL" "id" "id" }
288     [
289         ! We return either self or nil
290         [ over window-focus ] 2dip
291         valid-service? [ drop ] [ 2drop f ] if
292     ]
293 }
294
295 { "writeSelectionToPasteboard:types:" "char" { "id" "SEL" "id" "id" }
296     [
297         CF>string-array NSStringPboardType swap member? [
298             [ drop window-focus gadget-selection ] dip over
299             [ set-pasteboard-string 1 ] [ 2drop 0 ] if
300         ] [ 3drop 0 ] if
301     ]
302 }
303
304 { "readSelectionFromPasteboard:" "char" { "id" "SEL" "id" }
305     [
306         pasteboard-string dup [
307             [ drop window ] dip swap user-input 1
308         ] [ 3drop 0 ] if
309     ]
310 }
311
312 ! Text input
313 { "insertText:" "void" { "id" "SEL" "id" }
314     [ nip CF>string swap window user-input ]
315 }
316
317 { "hasMarkedText" "char" { "id" "SEL" }
318     [ 2drop 0 ]
319 }
320
321 { "markedRange" "NSRange" { "id" "SEL" }
322     [ 2drop 0 0 <NSRange> ]
323 }
324
325 { "selectedRange" "NSRange" { "id" "SEL" }
326     [ 2drop 0 0 <NSRange> ]
327 }
328
329 { "setMarkedText:selectedRange:" "void" { "id" "SEL" "id" "NSRange" }
330     [ 2drop 2drop ]
331 }
332
333 { "unmarkText" "void" { "id" "SEL" }
334     [ 2drop ]
335 }
336
337 { "validAttributesForMarkedText" "id" { "id" "SEL" }
338     [ 2drop NSArray -> array ]
339 }
340
341 { "attributedSubstringFromRange:" "id" { "id" "SEL" "NSRange" }
342     [ 3drop f ]
343 }
344
345 { "characterIndexForPoint:" "NSUInteger" { "id" "SEL" "NSPoint" }
346     [ 3drop 0 ]
347 }
348
349 { "firstRectForCharacterRange:" "NSRect" { "id" "SEL" "NSRange" }
350     [ 3drop 0 0 0 0 <CGRect> ]
351 }
352
353 { "conversationIdentifier" "NSInteger" { "id" "SEL" }
354     [ drop alien-address ]
355 }
356
357 ! Initialization
358 { "updateFactorGadgetSize:" "void" { "id" "SEL" "id" }
359     [ 2drop [ window ] [ view-dim ] bi >>dim drop yield ]
360 }
361
362 { "doCommandBySelector:" "void" { "id" "SEL" "SEL" }
363     [ 3drop ]
364 }
365
366 { "initWithFrame:pixelFormat:" "id" { "id" "SEL" "NSRect" "id" }
367     [
368         [ drop ] 2dip
369         SUPER-> initWithFrame:pixelFormat:
370         dup dup add-resize-observer
371     ]
372 }
373
374 { "dealloc" "void" { "id" "SEL" }
375     [
376         drop
377         [ unregister-window ]
378         [ remove-observer ]
379         [ SUPER-> dealloc ]
380         tri
381     ]
382 } ;
383
384 : sync-refresh-to-screen ( GLView -- )
385     -> openGLContext -> CGLContextObj NSOpenGLCPSwapInterval 1 <int>
386     CGLSetParameter drop ;
387
388 : <FactorView> ( dim pixel-format -- view )
389     [ FactorView ] 2dip <GLView> [ sync-refresh-to-screen ] keep ;
390
391 : save-position ( world window -- )
392     -> frame CGRect-top-left 2array >>window-loc drop ;
393
394 CLASS: {
395     { +superclass+ "NSObject" }
396     { +name+ "FactorWindowDelegate" }
397 }
398
399 { "windowDidMove:" "void" { "id" "SEL" "id" }
400     [
401         2nip -> object [ -> contentView window ] keep save-position
402     ]
403 }
404
405 { "windowDidBecomeKey:" "void" { "id" "SEL" "id" }
406     [
407         2nip -> object -> contentView window focus-world
408     ]
409 }
410
411 { "windowDidResignKey:" "void" { "id" "SEL" "id" }
412     [
413         forget-rollover
414         2nip -> object -> contentView
415         dup -> isInFullScreenMode zero? 
416         [ window unfocus-world ]
417         [ drop ] if
418     ]
419 }
420
421 { "windowShouldClose:" "char" { "id" "SEL" "id" }
422     [
423         3drop 1
424     ]
425 }
426
427 { "windowWillClose:" "void" { "id" "SEL" "id" }
428     [
429         2nip -> object -> contentView window ungraft
430     ]
431 } ;
432
433 : install-window-delegate ( window -- )
434     FactorWindowDelegate install-delegate ;