Spaces:
Runtime error
Runtime error
File size: 14,089 Bytes
d82cf6a |
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 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
from pyglet.window import key, mouse
from pyglet.libs.darwin.quartzkey import keymap, charmap
from pyglet.libs.darwin import cocoapy
from .pyglet_textview import PygletTextView
NSTrackingArea = cocoapy.ObjCClass('NSTrackingArea')
# Event data helper functions.
def getMouseDelta(nsevent):
dx = nsevent.deltaX()
dy = -nsevent.deltaY()
return dx, dy
def getMousePosition(self, nsevent):
in_window = nsevent.locationInWindow()
in_window = self.convertPoint_fromView_(in_window, None)
x = int(in_window.x)
y = int(in_window.y)
# Must record mouse position for BaseWindow.draw_mouse_cursor to work.
self._window._mouse_x = x
self._window._mouse_y = y
return x, y
def getModifiers(nsevent):
modifiers = 0
modifierFlags = nsevent.modifierFlags()
if modifierFlags & cocoapy.NSAlphaShiftKeyMask:
modifiers |= key.MOD_CAPSLOCK
if modifierFlags & cocoapy.NSShiftKeyMask:
modifiers |= key.MOD_SHIFT
if modifierFlags & cocoapy.NSControlKeyMask:
modifiers |= key.MOD_CTRL
if modifierFlags & cocoapy.NSAlternateKeyMask:
modifiers |= key.MOD_ALT
modifiers |= key.MOD_OPTION
if modifierFlags & cocoapy.NSCommandKeyMask:
modifiers |= key.MOD_COMMAND
if modifierFlags & cocoapy.NSFunctionKeyMask:
modifiers |= key.MOD_FUNCTION
return modifiers
def getSymbol(nsevent):
symbol = keymap.get(nsevent.keyCode(), None)
if symbol is not None:
return symbol
chars = cocoapy.cfstring_to_string(nsevent.charactersIgnoringModifiers())
if chars:
return charmap.get(chars[0].upper(), None)
return None
class PygletView_Implementation:
PygletView = cocoapy.ObjCSubclass('NSView', 'PygletView')
@PygletView.method(b'@'+cocoapy.NSRectEncoding+cocoapy.PyObjectEncoding)
def initWithFrame_cocoaWindow_(self, frame, window):
# The tracking area is used to get mouseEntered, mouseExited, and cursorUpdate
# events so that we can custom set the mouse cursor within the view.
self._tracking_area = None
self = cocoapy.ObjCInstance(cocoapy.send_super(self, 'initWithFrame:', frame, argtypes=[cocoapy.NSRect]))
if not self:
return None
# CocoaWindow object.
self._window = window
self.updateTrackingAreas()
# Create an instance of PygletTextView to handle text events.
# We must do this because NSOpenGLView doesn't conform to the
# NSTextInputClient protocol by default, and the insertText: method will
# not do the right thing with respect to translating key sequences like
# "Option-e", "e" if the protocol isn't implemented. So the easiest
# thing to do is to subclass NSTextView which *does* implement the
# protocol and let it handle text input.
self._textview = PygletTextView.alloc().initWithCocoaWindow_(window)
# Add text view to the responder chain.
self.addSubview_(self._textview)
return self
@PygletView.method('v')
def dealloc(self):
self._window = None
# cocoapy.end_message(self.objc_self, 'removeFromSuperviewWithoutNeedingDisplay')
self._textview.release()
self._textview = None
self._tracking_area.release()
self._tracking_area = None
cocoapy.send_super(self, 'dealloc')
@PygletView.method('v')
def updateTrackingAreas(self):
# This method is called automatically whenever the tracking areas need to be
# recreated, for example when window resizes.
if self._tracking_area:
self.removeTrackingArea_(self._tracking_area)
self._tracking_area.release()
self._tracking_area = None
tracking_options = cocoapy.NSTrackingMouseEnteredAndExited | cocoapy.NSTrackingActiveInActiveApp | cocoapy.NSTrackingCursorUpdate
frame = self.frame()
self._tracking_area = NSTrackingArea.alloc().initWithRect_options_owner_userInfo_(
frame, # rect
tracking_options, # options
self, # owner
None) # userInfo
self.addTrackingArea_(self._tracking_area)
@PygletView.method('B')
def canBecomeKeyView(self):
return True
@PygletView.method('B')
def isOpaque(self):
return True
## Event responders.
# This method is called whenever the view changes size.
@PygletView.method(b'v'+cocoapy.NSSizeEncoding)
def setFrameSize_(self, size):
cocoapy.send_super(self, 'setFrameSize:', size,
superclass_name='NSView',
argtypes=[cocoapy.NSSize])
# This method is called when view is first installed as the
# contentView of window. Don't do anything on first call.
# This also helps ensure correct window creation event ordering.
if not self._window.context.canvas:
return
width, height = int(size.width), int(size.height)
self._window.switch_to()
self._window.context.update_geometry()
self._window._width, self._window._height = width, height
self._window.dispatch_event("on_resize", width, height)
self._window.dispatch_event("on_expose")
# Can't get app.event_loop.enter_blocking() working with Cocoa, because
# when mouse clicks on the window's resize control, Cocoa enters into a
# mini-event loop that only responds to mouseDragged and mouseUp events.
# This means that using NSTimer to call idle() won't work. Our kludge
# is to override NSWindow's nextEventMatchingMask_etc method and call
# idle() from there.
if self.inLiveResize():
from pyglet import app
if app.event_loop is not None:
app.event_loop.idle()
@PygletView.method('v@')
def pygletKeyDown_(self, nsevent):
symbol = getSymbol(nsevent)
modifiers = getModifiers(nsevent)
self._window.dispatch_event('on_key_press', symbol, modifiers)
@PygletView.method('v@')
def pygletKeyUp_(self, nsevent):
symbol = getSymbol(nsevent)
modifiers = getModifiers(nsevent)
self._window.dispatch_event('on_key_release', symbol, modifiers)
@PygletView.method('v@')
def pygletFlagsChanged_(self, nsevent):
# Handles on_key_press and on_key_release events for modifier keys.
# Note that capslock is handled differently than other keys; it acts
# as a toggle, so on_key_release is only sent when it's turned off.
# TODO: Move these constants somewhere else.
# Undocumented left/right modifier masks found by experimentation:
NSLeftShiftKeyMask = 1 << 1
NSRightShiftKeyMask = 1 << 2
NSLeftControlKeyMask = 1 << 0
NSRightControlKeyMask = 1 << 13
NSLeftAlternateKeyMask = 1 << 5
NSRightAlternateKeyMask = 1 << 6
NSLeftCommandKeyMask = 1 << 3
NSRightCommandKeyMask = 1 << 4
maskForKey = {key.LSHIFT: NSLeftShiftKeyMask,
key.RSHIFT: NSRightShiftKeyMask,
key.LCTRL: NSLeftControlKeyMask,
key.RCTRL: NSRightControlKeyMask,
key.LOPTION: NSLeftAlternateKeyMask,
key.ROPTION: NSRightAlternateKeyMask,
key.LCOMMAND: NSLeftCommandKeyMask,
key.RCOMMAND: NSRightCommandKeyMask,
key.CAPSLOCK: cocoapy.NSAlphaShiftKeyMask,
key.FUNCTION: cocoapy.NSFunctionKeyMask}
symbol = keymap.get(nsevent.keyCode(), None)
# Ignore this event if symbol is not a modifier key. We must check this
# because e.g., we receive a flagsChanged message when using CMD-tab to
# switch applications, with symbol == "a" when command key is released.
if symbol is None or symbol not in maskForKey:
return
modifiers = getModifiers(nsevent)
modifierFlags = nsevent.modifierFlags()
if symbol and modifierFlags & maskForKey[symbol]:
self._window.dispatch_event('on_key_press', symbol, modifiers)
else:
self._window.dispatch_event('on_key_release', symbol, modifiers)
# Overriding this method helps prevent system beeps for unhandled events.
@PygletView.method('B@')
def performKeyEquivalent_(self, nsevent):
# Let arrow keys and certain function keys pass through the responder
# chain so that the textview can handle on_text_motion events.
modifierFlags = nsevent.modifierFlags()
if modifierFlags & cocoapy.NSNumericPadKeyMask:
return False
if modifierFlags & cocoapy.NSFunctionKeyMask:
ch = cocoapy.cfstring_to_string(nsevent.charactersIgnoringModifiers())
if ch in (cocoapy.NSHomeFunctionKey, cocoapy.NSEndFunctionKey,
cocoapy.NSPageUpFunctionKey, cocoapy.NSPageDownFunctionKey):
return False
# Send the key equivalent to the main menu to perform menu items.
NSApp = cocoapy.ObjCClass('NSApplication').sharedApplication()
NSApp.mainMenu().performKeyEquivalent_(nsevent)
# Indicate that we've handled the event so system won't beep.
return True
@PygletView.method('v@')
def mouseMoved_(self, nsevent):
if self._window._mouse_ignore_motion:
self._window._mouse_ignore_motion = False
return
# Don't send on_mouse_motion events if we're not inside the content rectangle.
if not self._window._mouse_in_window:
return
x, y = getMousePosition(self, nsevent)
dx, dy = getMouseDelta(nsevent)
self._window.dispatch_event('on_mouse_motion', x, y, dx, dy)
@PygletView.method('v@')
def scrollWheel_(self, nsevent):
x, y = getMousePosition(self, nsevent)
scroll_x, scroll_y = getMouseDelta(nsevent)
self._window.dispatch_event('on_mouse_scroll', x, y, scroll_x, scroll_y)
@PygletView.method('v@')
def mouseDown_(self, nsevent):
x, y = getMousePosition(self, nsevent)
buttons = mouse.LEFT
modifiers = getModifiers(nsevent)
self._window.dispatch_event('on_mouse_press', x, y, buttons, modifiers)
@PygletView.method('v@')
def mouseDragged_(self, nsevent):
x, y = getMousePosition(self, nsevent)
dx, dy = getMouseDelta(nsevent)
buttons = mouse.LEFT
modifiers = getModifiers(nsevent)
self._window.dispatch_event('on_mouse_drag', x, y, dx, dy, buttons, modifiers)
@PygletView.method('v@')
def mouseUp_(self, nsevent):
x, y = getMousePosition(self, nsevent)
buttons = mouse.LEFT
modifiers = getModifiers(nsevent)
self._window.dispatch_event('on_mouse_release', x, y, buttons, modifiers)
@PygletView.method('v@')
def rightMouseDown_(self, nsevent):
x, y = getMousePosition(self, nsevent)
buttons = mouse.RIGHT
modifiers = getModifiers(nsevent)
self._window.dispatch_event('on_mouse_press', x, y, buttons, modifiers)
@PygletView.method('v@')
def rightMouseDragged_(self, nsevent):
x, y = getMousePosition(self, nsevent)
dx, dy = getMouseDelta(nsevent)
buttons = mouse.RIGHT
modifiers = getModifiers(nsevent)
self._window.dispatch_event('on_mouse_drag', x, y, dx, dy, buttons, modifiers)
@PygletView.method('v@')
def rightMouseUp_(self, nsevent):
x, y = getMousePosition(self, nsevent)
buttons = mouse.RIGHT
modifiers = getModifiers(nsevent)
self._window.dispatch_event('on_mouse_release', x, y, buttons, modifiers)
@PygletView.method('v@')
def otherMouseDown_(self, nsevent):
x, y = getMousePosition(self, nsevent)
buttons = mouse.MIDDLE
modifiers = getModifiers(nsevent)
self._window.dispatch_event('on_mouse_press', x, y, buttons, modifiers)
@PygletView.method('v@')
def otherMouseDragged_(self, nsevent):
x, y = getMousePosition(self, nsevent)
dx, dy = getMouseDelta(nsevent)
buttons = mouse.MIDDLE
modifiers = getModifiers(nsevent)
self._window.dispatch_event('on_mouse_drag', x, y, dx, dy, buttons, modifiers)
@PygletView.method('v@')
def otherMouseUp_(self, nsevent):
x, y = getMousePosition(self, nsevent)
buttons = mouse.MIDDLE
modifiers = getModifiers(nsevent)
self._window.dispatch_event('on_mouse_release', x, y, buttons, modifiers)
@PygletView.method('v@')
def mouseEntered_(self, nsevent):
x, y = getMousePosition(self, nsevent)
self._window._mouse_in_window = True
# Don't call self._window.set_mouse_platform_visible() from here.
# Better to do it from cursorUpdate:
self._window.dispatch_event('on_mouse_enter', x, y)
@PygletView.method('v@')
def mouseExited_(self, nsevent):
x, y = getMousePosition(self, nsevent)
self._window._mouse_in_window = False
if not self._window._mouse_exclusive:
self._window.set_mouse_platform_visible()
self._window.dispatch_event('on_mouse_leave', x, y)
@PygletView.method('v@')
def cursorUpdate_(self, nsevent):
# Called when mouse cursor enters view. Unlike mouseEntered:,
# this method will be called if the view appears underneath a
# motionless mouse cursor, as can happen during window creation,
# or when switching into fullscreen mode.
# BUG: If the mouse enters the window via the resize control at the
# the bottom right corner, the resize control will set the cursor
# to the default arrow and screw up our cursor tracking.
self._window._mouse_in_window = True
if not self._window._mouse_exclusive:
self._window.set_mouse_platform_visible()
PygletView = cocoapy.ObjCClass('PygletView')
|