Thanks to visit codestin.com
Credit goes to github.com

Skip to content

MNT: Add modifier key press handling to macosx backend #21512

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 22 additions & 23 deletions doc/users/explain/event_handling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
132 changes: 98 additions & 34 deletions src/_macosx.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ---------------------------- */
Expand Down Expand Up @@ -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];
}
Expand Down Expand Up @@ -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*
Expand Down