mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-07 02:15:34 +00:00
app: [macOS] remove support for key bindings
The fix for #616 went to far by attempting to support macOS key bindings through doCommandBySelector. Issue #625 is a consequence, but more fundamentally, key bindings does not work with support for key.Release events. Remove key binding translation and fix #626, and add a check whether an IME swallowed a key event, keeping #616 fixed. While here, replace the KEY_ constants with ASCII codes. Fixes: https://todo.sr.ht/~eliasnaur/gio/625 References: https://todo.sr.ht/~eliasnaur/gio/616 Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+49
-49
@@ -39,12 +39,6 @@ import (
|
|||||||
#define MOUSE_DOWN 3
|
#define MOUSE_DOWN 3
|
||||||
#define MOUSE_SCROLL 4
|
#define MOUSE_SCROLL 4
|
||||||
|
|
||||||
#define KEY_DELETE_BACKWARD 0x7f
|
|
||||||
#define KEY_RETURN 0xd
|
|
||||||
#define KEY_ENTER 0x3
|
|
||||||
#define KEY_ESCAPE 0x1b
|
|
||||||
#define KEY_TAB 0x09
|
|
||||||
|
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_main(void);
|
__attribute__ ((visibility ("hidden"))) void gio_main(void);
|
||||||
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(int presentWithTrans);
|
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(int presentWithTrans);
|
||||||
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight);
|
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight);
|
||||||
@@ -318,6 +312,14 @@ static void invalidateCharacterCoordinates(CFTypeRef viewRef) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void interpretKeyEvents(CFTypeRef viewRef, CFTypeRef eventRef) {
|
||||||
|
@autoreleasepool {
|
||||||
|
NSView *view = (__bridge NSView *)viewRef;
|
||||||
|
NSEvent *event = (__bridge NSEvent *)eventRef;
|
||||||
|
[view interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||||
|
}
|
||||||
|
}
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
@@ -351,7 +353,10 @@ type window struct {
|
|||||||
scale float32
|
scale float32
|
||||||
config Config
|
config Config
|
||||||
|
|
||||||
cmdKeysDown map[key.Name]struct{}
|
keysDown map[key.Name]struct{}
|
||||||
|
// doCommandBySelectorCalled tracks whether a call to
|
||||||
|
// interpretKeys resulted in a callback.
|
||||||
|
doCommandBySelectorCalled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// launched is closed when applicationDidFinishLaunching is called.
|
// launched is closed when applicationDidFinishLaunching is called.
|
||||||
@@ -551,25 +556,36 @@ func (w *window) runOnMain(f func()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onKeys
|
//export gio_onKeys
|
||||||
func gio_onKeys(h C.uintptr_t, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger, keyDown C.bool) {
|
func gio_onKeys(h C.uintptr_t, event C.CFTypeRef, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger, keyDown C.bool) {
|
||||||
|
w := windowFor(h)
|
||||||
str := nsstringToString(cstr)
|
str := nsstringToString(cstr)
|
||||||
kmods := convertMods(mods)
|
kmods := convertMods(mods)
|
||||||
ks := key.Release
|
ks := key.Release
|
||||||
if keyDown {
|
if keyDown {
|
||||||
ks = key.Press
|
ks = key.Press
|
||||||
|
w.doCommandBySelectorCalled = false
|
||||||
|
C.interpretKeyEvents(w.view, event)
|
||||||
}
|
}
|
||||||
w := windowFor(h)
|
|
||||||
for _, k := range str {
|
for _, k := range str {
|
||||||
n, ok := convertKey(k)
|
if n, ok := convertKey(k); ok {
|
||||||
if !ok && ks == key.Release {
|
if keyDown {
|
||||||
// Key release events are not reported by onCommandBySelector.
|
if !w.doCommandBySelectorCalled {
|
||||||
n, ok = convertCommandKey(k)
|
if _, isCmd := convertCommandKey(k); isCmd {
|
||||||
if ok {
|
// doCommandBySelector was not called,
|
||||||
_, ok = w.cmdKeysDown[n]
|
// so this event was swallowed by an IME.
|
||||||
delete(w.cmdKeysDown, n)
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if w.keysDown == nil {
|
||||||
|
w.keysDown = make(map[key.Name]struct{})
|
||||||
|
}
|
||||||
|
w.keysDown[n] = struct{}{}
|
||||||
|
} else {
|
||||||
|
if _, pressed := w.keysDown[n]; !pressed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
delete(w.keysDown, n)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
w.ProcessEvent(key.Event{
|
w.ProcessEvent(key.Event{
|
||||||
Name: n,
|
Name: n,
|
||||||
Modifiers: kmods,
|
Modifiers: kmods,
|
||||||
@@ -580,26 +596,9 @@ func gio_onKeys(h C.uintptr_t, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger,
|
|||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onCommandBySelector
|
//export gio_onCommandBySelector
|
||||||
func gio_onCommandBySelector(h C.uintptr_t, k C.int, ti C.double, mods C.NSUInteger) {
|
func gio_onCommandBySelector(h C.uintptr_t) {
|
||||||
w := windowFor(h)
|
w := windowFor(h)
|
||||||
w.commandKey(rune(k), ti, mods)
|
w.doCommandBySelectorCalled = true
|
||||||
}
|
|
||||||
|
|
||||||
func (w *window) commandKey(k rune, ti C.double, mods C.NSUInteger) bool {
|
|
||||||
n, ok := convertCommandKey(k)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if w.cmdKeysDown == nil {
|
|
||||||
w.cmdKeysDown = make(map[key.Name]struct{})
|
|
||||||
}
|
|
||||||
w.cmdKeysDown[n] = struct{}{}
|
|
||||||
w.ProcessEvent(key.Event{
|
|
||||||
Modifiers: convertMods(mods),
|
|
||||||
Name: n,
|
|
||||||
State: key.Press,
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onFlagsChanged
|
//export gio_onFlagsChanged
|
||||||
@@ -816,18 +815,16 @@ func gio_substringForProposedRange(h C.uintptr_t, crng C.NSRange, actual C.NSRan
|
|||||||
}
|
}
|
||||||
|
|
||||||
//export gio_insertText
|
//export gio_insertText
|
||||||
func gio_insertText(h C.uintptr_t, cstr C.CFTypeRef, crng C.NSRange, ti C.double, mods C.NSUInteger) {
|
func gio_insertText(h C.uintptr_t, cstr C.CFTypeRef, crng C.NSRange) {
|
||||||
w := windowFor(h)
|
w := windowFor(h)
|
||||||
str := nsstringToString(cstr)
|
str := nsstringToString(cstr)
|
||||||
// macOS IME in some cases calls insertText for command keys such as backspace
|
// macOS IME in some cases calls insertText for command keys such as backspace
|
||||||
// instead of doCommandBySelector.
|
// instead of doCommandBySelector.
|
||||||
hadCommands := false
|
|
||||||
for _, r := range str {
|
for _, r := range str {
|
||||||
hadCommands = w.commandKey(r, ti, mods) || hadCommands
|
if _, ok := convertCommandKey(r); ok {
|
||||||
}
|
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
|
||||||
if hadCommands {
|
return
|
||||||
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
state := w.w.EditorState()
|
state := w.w.EditorState()
|
||||||
rng := state.compose
|
rng := state.compose
|
||||||
@@ -1037,7 +1034,7 @@ func osMain() {
|
|||||||
func convertCommandKey(k rune) (key.Name, bool) {
|
func convertCommandKey(k rune) (key.Name, bool) {
|
||||||
var n key.Name
|
var n key.Name
|
||||||
switch k {
|
switch k {
|
||||||
case C.KEY_ESCAPE:
|
case '\x1b': // ASCII escape.
|
||||||
n = key.NameEscape
|
n = key.NameEscape
|
||||||
case C.NSLeftArrowFunctionKey:
|
case C.NSLeftArrowFunctionKey:
|
||||||
n = key.NameLeftArrow
|
n = key.NameLeftArrow
|
||||||
@@ -1047,19 +1044,19 @@ func convertCommandKey(k rune) (key.Name, bool) {
|
|||||||
n = key.NameUpArrow
|
n = key.NameUpArrow
|
||||||
case C.NSDownArrowFunctionKey:
|
case C.NSDownArrowFunctionKey:
|
||||||
n = key.NameDownArrow
|
n = key.NameDownArrow
|
||||||
case C.KEY_RETURN:
|
case '\r':
|
||||||
n = key.NameReturn
|
n = key.NameReturn
|
||||||
case C.KEY_ENTER:
|
case '\x03':
|
||||||
n = key.NameEnter
|
n = key.NameEnter
|
||||||
case C.NSHomeFunctionKey:
|
case C.NSHomeFunctionKey:
|
||||||
n = key.NameHome
|
n = key.NameHome
|
||||||
case C.NSEndFunctionKey:
|
case C.NSEndFunctionKey:
|
||||||
n = key.NameEnd
|
n = key.NameEnd
|
||||||
case C.KEY_DELETE_BACKWARD, 0x08:
|
case '\x7f', '\b':
|
||||||
n = key.NameDeleteBackward
|
n = key.NameDeleteBackward
|
||||||
case C.NSDeleteFunctionKey:
|
case C.NSDeleteFunctionKey:
|
||||||
n = key.NameDeleteForward
|
n = key.NameDeleteForward
|
||||||
case C.KEY_TAB, 0x19:
|
case '\t', 0x19:
|
||||||
n = key.NameTab
|
n = key.NameTab
|
||||||
case C.NSPageUpFunctionKey:
|
case C.NSPageUpFunctionKey:
|
||||||
n = key.NamePageUp
|
n = key.NamePageUp
|
||||||
@@ -1072,6 +1069,9 @@ func convertCommandKey(k rune) (key.Name, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func convertKey(k rune) (key.Name, bool) {
|
func convertKey(k rune) (key.Name, bool) {
|
||||||
|
if n, ok := convertCommandKey(k); ok {
|
||||||
|
return n, true
|
||||||
|
}
|
||||||
var n key.Name
|
var n key.Name
|
||||||
switch k {
|
switch k {
|
||||||
case C.NSF1FunctionKey:
|
case C.NSF1FunctionKey:
|
||||||
|
|||||||
+4
-53
@@ -15,7 +15,6 @@ __attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(BOOL presentWi
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GioView : NSView <CALayerDelegate,NSTextInputClient>
|
@interface GioView : NSView <CALayerDelegate,NSTextInputClient>
|
||||||
@property NSEvent *lastKeyDown;
|
|
||||||
@property uintptr_t handle;
|
@property uintptr_t handle;
|
||||||
@property BOOL presentWithTrans;
|
@property BOOL presentWithTrans;
|
||||||
@end
|
@end
|
||||||
@@ -133,12 +132,8 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
|
|||||||
handleMouse(self, event, MOUSE_SCROLL, dx, dy);
|
handleMouse(self, event, MOUSE_SCROLL, dx, dy);
|
||||||
}
|
}
|
||||||
- (void)keyDown:(NSEvent *)event {
|
- (void)keyDown:(NSEvent *)event {
|
||||||
// Stash the event for use by doCommandBySelector.
|
|
||||||
self.lastKeyDown = event;
|
|
||||||
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
|
||||||
self.lastKeyDown = nil;
|
|
||||||
NSString *keys = [event charactersIgnoringModifiers];
|
NSString *keys = [event charactersIgnoringModifiers];
|
||||||
gio_onKeys(self.handle, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], true);
|
gio_onKeys(self.handle, (__bridge CFTypeRef)event, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], true);
|
||||||
}
|
}
|
||||||
- (void)flagsChanged:(NSEvent *)event {
|
- (void)flagsChanged:(NSEvent *)event {
|
||||||
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||||
@@ -146,54 +141,14 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
|
|||||||
}
|
}
|
||||||
- (void)keyUp:(NSEvent *)event {
|
- (void)keyUp:(NSEvent *)event {
|
||||||
NSString *keys = [event charactersIgnoringModifiers];
|
NSString *keys = [event charactersIgnoringModifiers];
|
||||||
gio_onKeys(self.handle, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], false);
|
gio_onKeys(self.handle, (__bridge CFTypeRef)event, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], false);
|
||||||
}
|
}
|
||||||
- (void)insertText:(id)string {
|
- (void)insertText:(id)string {
|
||||||
gio_onText(self.handle, (__bridge CFTypeRef)string);
|
gio_onText(self.handle, (__bridge CFTypeRef)string);
|
||||||
}
|
}
|
||||||
- (void)doCommandBySelector:(SEL)action {
|
- (void)doCommandBySelector:(SEL)action {
|
||||||
NSEvent *event = self.lastKeyDown;
|
gio_onCommandBySelector(self.handle);
|
||||||
if (event == nil) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int key = 0;
|
|
||||||
if (action == @selector(deleteBackward:)) {
|
|
||||||
key = KEY_DELETE_BACKWARD;
|
|
||||||
} else if (action == @selector(deleteForward:)) {
|
|
||||||
key = NSDeleteFunctionKey;
|
|
||||||
} else if (action == @selector(insertNewline:)) {
|
|
||||||
NSString *keys = [event charactersIgnoringModifiers];
|
|
||||||
if ([keys isEqualToString:@"\r"]) {
|
|
||||||
key = KEY_RETURN;
|
|
||||||
} else {
|
|
||||||
key = KEY_ENTER;
|
|
||||||
}
|
|
||||||
} else if (action == @selector(moveUp:)) {
|
|
||||||
key = NSUpArrowFunctionKey;
|
|
||||||
} else if (action == @selector(moveDown:)) {
|
|
||||||
key = NSDownArrowFunctionKey;
|
|
||||||
} else if (action == @selector(moveLeft:)) {
|
|
||||||
key = NSLeftArrowFunctionKey;
|
|
||||||
} else if (action == @selector(moveRight:)) {
|
|
||||||
key = NSRightArrowFunctionKey;
|
|
||||||
} else if (action == @selector(cancelOperation:)) {
|
|
||||||
key = KEY_ESCAPE;
|
|
||||||
} else if (action == @selector(insertTab:)) {
|
|
||||||
key = KEY_TAB;
|
|
||||||
} else if (action == @selector(scrollToBeginningOfDocument:)) {
|
|
||||||
key = NSHomeFunctionKey;
|
|
||||||
} else if (action == @selector(scrollToEndOfDocument:)) {
|
|
||||||
key = NSEndFunctionKey;
|
|
||||||
} else if (action == @selector(scrollPageUp:)) {
|
|
||||||
key = NSPageUpFunctionKey;
|
|
||||||
} else if (action == @selector(scrollPageDown:)) {
|
|
||||||
key = NSPageDownFunctionKey;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gio_onCommandBySelector(self.handle, key, [event timestamp], [event modifierFlags]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)hasMarkedText {
|
- (BOOL)hasMarkedText {
|
||||||
int res = gio_hasMarkedText(self.handle);
|
int res = gio_hasMarkedText(self.handle);
|
||||||
return res ? YES : NO;
|
return res ? YES : NO;
|
||||||
@@ -229,10 +184,6 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
|
|||||||
}
|
}
|
||||||
- (void)insertText:(id)string
|
- (void)insertText:(id)string
|
||||||
replacementRange:(NSRange)replaceRange {
|
replacementRange:(NSRange)replaceRange {
|
||||||
NSEvent *event = self.lastKeyDown;
|
|
||||||
if (event == nil) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
NSString *str;
|
NSString *str;
|
||||||
// string is either an NSAttributedString or an NSString.
|
// string is either an NSAttributedString or an NSString.
|
||||||
if ([string isKindOfClass:[NSAttributedString class]]) {
|
if ([string isKindOfClass:[NSAttributedString class]]) {
|
||||||
@@ -240,7 +191,7 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
|
|||||||
} else {
|
} else {
|
||||||
str = string;
|
str = string;
|
||||||
}
|
}
|
||||||
gio_insertText(self.handle, (__bridge CFTypeRef)str, replaceRange, [event timestamp], [event modifierFlags]);
|
gio_insertText(self.handle, (__bridge CFTypeRef)str, replaceRange);
|
||||||
}
|
}
|
||||||
- (NSUInteger)characterIndexForPoint:(NSPoint)p {
|
- (NSUInteger)characterIndexForPoint:(NSPoint)p {
|
||||||
return gio_characterIndexForPoint(self.handle, p);
|
return gio_characterIndexForPoint(self.handle, p);
|
||||||
|
|||||||
Reference in New Issue
Block a user