/
OSWindowMorphicEventHandler.class.st
330 lines (279 loc) · 9.36 KB
/
OSWindowMorphicEventHandler.class.st
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
"
I can be used to convert OSWindow-level events to Morphic events.
So, that installing my instance as event handler for specific window would allow running Morphic World in it.
Later, the Morphic can be integrated with OSWindow API to avoid unnecessary conversion and thus eliminating the need in having this class.
"
Class {
#name : #OSWindowMorphicEventHandler,
#superclass : #OSWindowEventVisitor,
#instVars : [
'morphicWorld',
'eventQueue'
],
#classVars : [
'SymbolCharacterMapping'
],
#pools : [
'OSKeySymbols'
],
#category : #'OSWindow-Core-Morphic'
}
{ #category : #'as yet unclassified' }
OSWindowMorphicEventHandler class >> for: morphicWorld [
^ self new morphicWorld: morphicWorld; yourself
]
{ #category : #'as yet unclassified' }
OSWindowMorphicEventHandler class >> initialize [
"
self initialize
"
super initialize.
SymbolCharacterMapping := Dictionary new.
{
OSK_RETURN . Character cr.
OSK_BACKSPACE . Character backspace.
OSK_TAB . Character tab.
OSK_HOME . Character home.
OSK_LEFT . Character arrowLeft.
OSK_UP . Character arrowUp.
OSK_RIGHT . Character arrowRight.
OSK_DOWN . Character arrowDown.
OSK_END . Character end.
OSK_INSERT . Character insert.
OSK_PAGEUP . Character pageUp.
OSK_PAGEDOWN . Character pageDown.
OSK_DELETE. Character delete.
OSK_KP_0 . $0.
OSK_KP_1 . $1.
OSK_KP_2 . $2.
OSK_KP_3 . $3.
OSK_KP_4 . $4.
OSK_KP_5 . $5.
OSK_KP_6 . $6.
OSK_KP_7 . $7.
OSK_KP_8 . $8.
OSK_KP_9 . $9.
OSK_KP_DIVIDE . $/.
OSK_KP_MULTIPLY . $*.
OSK_KP_PLUS . $+.
OSK_KP_MINUS . $-.
OSK_KP_ENTER . Character cr.
OSK_KP_PERIOD . $..
} pairsDo: [ :key :val | SymbolCharacterMapping at: key put: val charCode ]
]
{ #category : #private }
OSWindowMorphicEventHandler >> activeHand [
^ self morphicWorld activeHand
]
{ #category : #converting }
OSWindowMorphicEventHandler >> convertButton: osButton [
osButton = 1 ifTrue: [ ^ MouseButtonEvent redButton ].
osButton = 2 ifTrue: [ ^ MouseButtonEvent blueButton ].
osButton = 3 ifTrue: [ ^ MouseButtonEvent yellowButton ].
]
{ #category : #converting }
OSWindowMorphicEventHandler >> convertModifiers: modifiers [
| buttons |
buttons := 0.
"Alt/Option key"
modifiers alt ifTrue: [
"On windows and unix, treat alt key as command key"
buttons := Smalltalk os isWin32 | Smalltalk os isUnix
ifTrue: [ buttons | 2r01000000 ]
ifFalse: [ buttons | 2r00100000 ]
].
modifiers ctrl ifTrue: [ buttons := buttons | 2r00010000 ]. "Control key"
modifiers shift ifTrue: [ buttons := buttons | 8 ]. "Shift key"
modifiers cmd ifTrue: [ buttons := buttons | 2r01000000 ]. "Cmd key"
modifiers buttons button1 ifTrue: [ buttons := buttons | MouseButtonEvent redButton ].
modifiers buttons button2 ifTrue: [ buttons := buttons | MouseButtonEvent blueButton ].
modifiers buttons button3 ifTrue: [ buttons := buttons | MouseButtonEvent yellowButton ].
^ buttons
]
{ #category : #events }
OSWindowMorphicEventHandler >> dispatchMorphicEvent: anEvent [
morphicWorld defer: [
(morphicWorld activeHand isNotNil and: [ anEvent hand isNotNil ]) ifTrue: [
morphicWorld activeHand handleEvent: anEvent
]
].
]
{ #category : #events }
OSWindowMorphicEventHandler >> enqueue: aMorphicEvent [
"Put morphic event into receiver's internal queue.
All events in queue will be processed later, by UI process, once it will get there."
self eventQueue nextPut: aMorphicEvent.
]
{ #category : #accessing }
OSWindowMorphicEventHandler >> eventQueue [
"using lazy-initialize here to deal with bootstrapping issues,
since existing instances originally has no queue"
^ eventQueue ifNil: [ eventQueue := WaitfreeQueue new ]
]
{ #category : #events }
OSWindowMorphicEventHandler >> handleEvent: anEvent [
"convert the event to morphic one, and dispatch it whatever..."
| morphicEvent |
morphicEvent := anEvent accept: self.
morphicEvent isMorphicEvent ifFalse: [ ^ self ].
self dispatchMorphicEvent: morphicEvent
]
{ #category : #initialization }
OSWindowMorphicEventHandler >> initialize [
eventQueue := WaitfreeQueue new.
]
{ #category : #visiting }
OSWindowMorphicEventHandler >> mapSymbolToKeyValue: symbol [
^ SymbolCharacterMapping at: symbol ifAbsent: [
"Don't allow symbol values outside the unicode range"
symbol >= 16r400000 ifTrue: [ 0 ] ifFalse: [ symbol ] ]
]
{ #category : #accessing }
OSWindowMorphicEventHandler >> morphicWorld [
^ morphicWorld
]
{ #category : #accessing }
OSWindowMorphicEventHandler >> morphicWorld: aMorphicWorld [
morphicWorld := aMorphicWorld
]
{ #category : #visiting }
OSWindowMorphicEventHandler >> visitKeyDownEvent: anEvent [
| keyEvent keyEvent2 mods |
mods := anEvent modifiers.
keyEvent := KeyboardEvent new
setType: #keyDown
buttons: (self convertModifiers: mods)
position: anEvent position
keyValue: (self mapSymbolToKeyValue: anEvent symbol)
charCode: (self mapSymbolToKeyValue: anEvent symbol)
hand: self activeHand
stamp: Time millisecondClockValue.
keyEvent scanCode: anEvent scanCode.
self enqueue: keyEvent.
"This is for ctrl/alt held alone makes morphic think that null character is good text input"
keyEvent keyValue = 0 ifTrue: [ ^ self ].
"That's stupid and horrible, but that's the way we doing it for now"
keyEvent keyValue = 27 "Esc" ifFalse: [
anEvent character ifNil: [
(mods alt or: [ mods ctrl or: [ mods cmd ] ]) ifFalse: [
^ nil
]]
].
"Workaround for SDL<->Morphic: produce keystroke events
if any modifier key held down (for non-modified keys there are OSTextInputEvent)"
keyEvent2 := KeyboardEvent new
setType: #keystroke
buttons: (self convertModifiers: anEvent modifiers)
position: anEvent position
keyValue: (self mapSymbolToKeyValue: anEvent symbol)
charCode: (self mapSymbolToKeyValue: anEvent symbol)
hand: morphicWorld activeHand
stamp: Time millisecondClockValue.
keyEvent2 scanCode: anEvent scanCode.
^ keyEvent2
]
{ #category : #visiting }
OSWindowMorphicEventHandler >> visitKeyUpEvent: anEvent [
| keyEvent |
keyEvent := KeyboardEvent new
setType: #keyUp
buttons: (self convertModifiers: anEvent modifiers)
position: anEvent position
keyValue: (self mapSymbolToKeyValue: anEvent symbol)
charCode: (self mapSymbolToKeyValue: anEvent symbol)
hand: self activeHand
stamp: Time millisecondClockValue.
keyEvent scanCode: anEvent scanCode.
^ keyEvent
]
{ #category : #visiting }
OSWindowMorphicEventHandler >> visitMouseButtonPressEvent: anEvent [
anEvent isWheel ifTrue: [
^ MouseWheelEvent new
setType: #mouseWheel
position: anEvent position
direction: anEvent wheelDirection
buttons: (self convertModifiers: anEvent modifiers)
hand: self activeHand
stamp: Time millisecondClockValue ].
^ MouseButtonEvent new
setType: #mouseDown
position: anEvent position
which: (self convertButton: anEvent button)
buttons: (self convertModifiers: anEvent modifiers) | (self convertButton: anEvent button)
hand: self activeHand
stamp: Time millisecondClockValue
]
{ #category : #visiting }
OSWindowMorphicEventHandler >> visitMouseButtonReleaseEvent: anEvent [
anEvent isWheel ifTrue: [ ^ nil ].
^ MouseButtonEvent new
setType: #mouseUp
position: anEvent position
which: (self convertButton: anEvent button)
buttons: (self convertModifiers: anEvent modifiers) | (self convertModifiers: anEvent modifiers)
hand: self activeHand
stamp: Time millisecondClockValue
]
{ #category : #visiting }
OSWindowMorphicEventHandler >> visitMouseMoveEvent: anEvent [
| oldPos |
oldPos := morphicWorld activeHand ifNil: [ 0@0 ] ifNotNil: [:hand | hand position ].
morphicWorld beCursorOwner.
^ MouseMoveEvent basicNew
setType: #mouseMove
startPoint: oldPos
endPoint: anEvent position
trail: { oldPos. anEvent position }
buttons: (self convertModifiers: anEvent modifiers)
hand: self activeHand
stamp: Time millisecondClockValue
]
{ #category : #visiting }
OSWindowMorphicEventHandler >> visitMouseWheelEvent: anEvent [
| vertical |
vertical := anEvent scrollVertical.
vertical = 0 ifTrue: [ ^ nil ].
^ MouseWheelEvent new
setType: #mouseWheel
position: anEvent position
direction: (vertical > 0 ifTrue: [Character arrowUp] ifFalse: [Character arrowDown])
buttons: (self convertModifiers: anEvent modifiers)
hand: self activeHand
stamp: Time millisecondClockValue
]
{ #category : #visiting }
OSWindowMorphicEventHandler >> visitTextInputEvent: anEvent [
| keyEvent char mods |
anEvent text ifNil: [ ^ nil ].
char := anEvent text first.
char ifNil: [ ^ nil ].
mods := anEvent modifiers.
"If a modifier key is pressed the keystroke event is handled by #visitMouseDownEvent:"
(mods alt or: [ mods ctrl or: [ mods cmd ] ])
ifTrue: [ ^ nil ].
keyEvent := KeyboardEvent new
setType: #keystroke
buttons: (self convertModifiers: anEvent modifiers)
position: anEvent position
keyValue: char charCode
charCode: char charCode
hand: self activeHand
stamp: Time millisecondClockValue.
^ keyEvent
]
{ #category : #visiting }
OSWindowMorphicEventHandler >> visitUnknownEvent: anEvent [
self
traceCr: ('Unknown event: <1s>' expandMacrosWith: anEvent data printString)
]
{ #category : #visiting }
OSWindowMorphicEventHandler >> visitWindowCloseEvent: anEvent [
anEvent suppressDefaultAction.
self currentWorld defer: [WorldState quitSession].
]
{ #category : #visiting }
OSWindowMorphicEventHandler >> visitWindowResizeEvent: anEvent [
"window resized"
morphicWorld worldState worldRenderer checkForNewScreenSize.
]