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