diff --git a/doc/users/explain/event_handling.rst b/doc/users/explain/event_handling.rst index 239c32a194bb..909e9f4b9ff8 100644 --- a/doc/users/explain/event_handling.rst +++ b/doc/users/explain/event_handling.rst @@ -82,29 +82,28 @@ Event name Class Description you may encounter inconsistencies between the different user interface toolkits that Matplotlib works with. This is due to inconsistencies/limitations of the user interface toolkit. The following table shows some basic examples of - what you may expect to receive as key(s) from the different user interface toolkits, - where a comma separates different keys: - - ============== ============================= ============================== ============================== ============================== ============================== - Key(s) Pressed WxPython Qt WebAgg Gtk Tkinter - ============== ============================= ============================== ============================== ============================== ============================== - Shift+2 shift, shift+2 shift, " shift, " shift, " shift, " - Shift+F1 shift, shift+f1 shift, shift+f1 shift, shift+f1 shift, shift+f1 shift, shift+f1 - Shift shift shift shift shift shift - Control control control control control control - Alt alt alt alt alt alt - AltGr Nothing Nothing alt iso_level3_shift iso_level3_shift - CapsLock caps_lock caps_lock caps_lock caps_lock caps_lock - A a a A A A - a a a a a a - Shift+a shift, A shift, A shift, A shift, A shift, A - Shift+A shift, A shift, A shift, a shift, a shift, a - Ctrl+Shift+Alt control, ctrl+shift, ctrl+alt control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+meta - Ctrl+Shift+a control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+a - Ctrl+Shift+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+a control, ctrl+shift, ctrl+a control, ctrl+shift, ctrl+a - F1 f1 f1 f1 f1 f1 - Ctrl+F1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 - ============== ============================= ============================== ============================== ============================== ============================== + what you may expect to receive as key(s) (using a QWERTY keyboard layout) + from the different user interface toolkits, where a comma separates different keys: + + ================ ============================= ============================== ============================== ============================== ============================== =================================== + Key(s) Pressed WxPython Qt WebAgg Gtk Tkinter macosx + ================ ============================= ============================== ============================== ============================== ============================== =================================== + Shift+2 shift, shift+2 shift, @ shift, @ shift, @ shift, @ shift, @ + Shift+F1 shift, shift+f1 shift, shift+f1 shift, shift+f1 shift, shift+f1 shift, shift+f1 shift, shift+f1 + Shift shift shift shift shift shift shift + Control control control control control control control + Alt alt alt alt alt alt alt + AltGr Nothing Nothing alt iso_level3_shift iso_level3_shift + CapsLock caps_lock caps_lock caps_lock caps_lock caps_lock caps_lock + CapsLock+a caps_lock, a caps_lock, a caps_lock, A caps_lock, A caps_lock, A caps_lock, a + a a a a a a a + Shift+a shift, A shift, A shift, A shift, A shift, A shift, A + CapsLock+Shift+a caps_lock, shift, A caps_lock, shift, A caps_lock, shift, a caps_lock, shift, a caps_lock, shift, a caps_lock, shift, A + Ctrl+Shift+Alt control, ctrl+shift, ctrl+alt control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+meta control, ctrl+shift, ctrl+alt+shift + Ctrl+Shift+a control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+A control, ctrl+shift, ctrl+a control, ctrl+shift, ctrl+A + F1 f1 f1 f1 f1 f1 f1 + Ctrl+F1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 control, ctrl+f1 control, Nothing + ================ ============================= ============================== ============================== ============================== ============================== =================================== Matplotlib attaches some keypress callbacks by default for interactivity; they are documented in the :ref:`key-event-handling` section. diff --git a/src/_macosx.m b/src/_macosx.m index 9bae57ea0447..5088cb865605 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -62,6 +62,20 @@ Needed to know when to stop the NSApp */ static long FigureWindowCount = 0; +/* Keep track of modifier key states for flagsChanged + to keep track of press vs release */ +static bool lastCommand = false; +static bool lastControl = false; +static bool lastShift = false; +static bool lastOption = false; +static bool lastCapsLock = false; +/* Keep track of whether this specific key modifier was pressed or not */ +static bool keyChangeCommand = false; +static bool keyChangeControl = false; +static bool keyChangeShift = false; +static bool keyChangeOption = false; +static bool keyChangeCapsLock = false; + /* -------------------------- Helper function ---------------------------- */ static void @@ -247,7 +261,7 @@ - (void)keyDown:(NSEvent*)event; - (void)keyUp:(NSEvent*)event; - (void)scrollWheel:(NSEvent *)event; - (BOOL)acceptsFirstResponder; -//- (void)flagsChanged:(NSEvent*)event; +- (void)flagsChanged:(NSEvent*)event; @end /* ---------------------------- Python classes ---------------------------- */ @@ -1623,26 +1637,45 @@ - (const char*)convertKeyEvent:(NSEvent*)event ]; NSMutableString* returnkey = [NSMutableString string]; - if ([event modifierFlags] & NSEventModifierFlagControl) { - [returnkey appendString:@"ctrl+" ]; - } - if ([event modifierFlags] & NSEventModifierFlagOption) { + if (keyChangeControl) { + // When control is the key that was pressed, return the full word + [returnkey appendString:@"control+"]; + } else if (([event modifierFlags] & NSEventModifierFlagControl)) { + // If control is already pressed, return the shortened version + [returnkey appendString:@"ctrl+"]; + } + if (([event modifierFlags] & NSEventModifierFlagOption) || keyChangeOption) { [returnkey appendString:@"alt+" ]; } - if ([event modifierFlags] & NSEventModifierFlagCommand) { + if (([event modifierFlags] & NSEventModifierFlagCommand) || keyChangeCommand) { [returnkey appendString:@"cmd+" ]; } + // Don't print caps_lock unless it was the key that got pressed + if (keyChangeCapsLock) { + [returnkey appendString:@"caps_lock+" ]; + } - unichar uc = [[event charactersIgnoringModifiers] characterAtIndex:0]; - NSString* specialchar = [specialkeymappings objectForKey:[NSNumber numberWithUnsignedLong:uc]]; - if (specialchar) { - if ([event modifierFlags] & NSEventModifierFlagShift) { - [returnkey appendString:@"shift+" ]; + // flagsChanged event can't handle charactersIgnoringModifiers + // because it was a modifier key that was pressed/released + if (event.type != NSEventTypeFlagsChanged) { + unichar uc = [[event charactersIgnoringModifiers] characterAtIndex:0]; + NSString *specialchar = [specialkeymappings objectForKey:[NSNumber numberWithUnsignedLong:uc]]; + if (specialchar) { + if (([event modifierFlags] & NSEventModifierFlagShift) || keyChangeShift) { + [returnkey appendString:@"shift+"]; + } + [returnkey appendString:specialchar]; + } else { + [returnkey appendString:[event charactersIgnoringModifiers]]; + } + } else { + if (([event modifierFlags] & NSEventModifierFlagShift) || keyChangeShift) { + [returnkey appendString:@"shift+"]; } - [returnkey appendString:specialchar]; + // Since it was a modifier event trim the final character of the string + // because we added in "+" earlier + [returnkey setString: [returnkey substringToIndex:[returnkey length] - 1]]; } - else - [returnkey appendString:[event charactersIgnoringModifiers]]; return [returnkey UTF8String]; } @@ -1711,29 +1744,60 @@ - (BOOL)acceptsFirstResponder return YES; } -/* This is all wrong. Address of pointer is being passed instead of pointer, keynames don't - match up with what the front-end and does the front-end even handle modifier keys by themselves? +// flagsChanged gets called whenever a modifier key is pressed OR released +// so we need to handle both cases here +- (void)flagsChanged:(NSEvent *)event +{ + bool isPress = false; // true if key is pressed, false if key was released + + // Each if clause tests the two cases for each of the keys we can handle + // 1. If the modifier flag "command key" is pressed and it was not previously + // 2. If the modifier flag "command key" is not pressed and it was previously + // !! converts the result of the bitwise & operator to a logical boolean, + // which allows us to then bitwise xor (^) the result with a boolean (lastCommand). + if (!!([event modifierFlags] & NSEventModifierFlagCommand) ^ lastCommand) { + // Command pressed/released + lastCommand = !lastCommand; + keyChangeCommand = true; + isPress = lastCommand; + } else if (!!([event modifierFlags] & NSEventModifierFlagControl) ^ lastControl) { + // Control pressed/released + lastControl = !lastControl; + keyChangeControl = true; + isPress = lastControl; + } else if (!!([event modifierFlags] & NSEventModifierFlagShift) ^ lastShift) { + // Shift pressed/released + lastShift = !lastShift; + keyChangeShift = true; + isPress = lastShift; + } else if (!!([event modifierFlags] & NSEventModifierFlagOption) ^ lastOption) { + // Option pressed/released + lastOption = !lastOption; + keyChangeOption = true; + isPress = lastOption; + } else if (!!([event modifierFlags] & NSEventModifierFlagCapsLock) ^ lastCapsLock) { + // Capslock pressed/released + lastCapsLock = !lastCapsLock; + keyChangeCapsLock = true; + isPress = lastCapsLock; + } else { + // flag we don't handle + return; + } -- (void)flagsChanged:(NSEvent*)event -{ - const char *s = NULL; - if (([event modifierFlags] & NSControlKeyMask) == NSControlKeyMask) - s = "control"; - else if (([event modifierFlags] & NSShiftKeyMask) == NSShiftKeyMask) - s = "shift"; - else if (([event modifierFlags] & NSAlternateKeyMask) == NSAlternateKeyMask) - s = "alt"; - else return; - PyGILState_STATE gstate = PyGILState_Ensure(); - PyObject* result = PyObject_CallMethod(canvas, "key_press_event", "s", &s); - if (result) - Py_DECREF(result); - else - PyErr_Print(); + if (isPress) { + [self keyDown:event]; + } else { + [self keyUp:event]; + } - PyGILState_Release(gstate); + // Reset the state for the key changes after handling the event + keyChangeCommand = false; + keyChangeControl = false; + keyChangeShift = false; + keyChangeOption = false; + keyChangeCapsLock = false; } - */ @end static PyObject*
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: