@@ -312,6 +312,21 @@ static void invalidateCharacterCoordinates(CFTypeRef viewRef) {
312312 }
313313 }
314314}
315+
316+ static void interpretKeyEvents(CFTypeRef viewRef, CFTypeRef eventRef) {
317+ @autoreleasepool {
318+ NSView *view = (__bridge NSView *)viewRef;
319+ NSEvent *event = (__bridge NSEvent *)eventRef;
320+ [view interpretKeyEvents:[NSArray arrayWithObject:event]];
321+ }
322+ }
323+
324+ static int isMiniaturized(CFTypeRef windowRef) {
325+ @autoreleasepool {
326+ NSWindow *window = (__bridge NSWindow *)windowRef;
327+ return window.miniaturized ? 1 : 0;
328+ }
329+ }
315330*/
316331import "C"
317332
@@ -340,9 +355,20 @@ type window struct {
340355 cursor pointer.Cursor
341356 pointerBtns pointer.Buttons
342357 loop * eventLoop
358+ lastMods C.NSUInteger
343359
344360 scale float32
345361 config Config
362+
363+ keysDown map [key.Name ]struct {}
364+ // cmdKeys is for storing the current key event while
365+ // waiting for a doCommandBySelector.
366+ cmdKeys cmdKeys
367+ }
368+
369+ type cmdKeys struct {
370+ eventStr string
371+ eventMods key.Modifiers
346372}
347373
348374// launched is closed when applicationDidFinishLaunching is called.
@@ -511,7 +537,7 @@ func (w *window) SetCursor(cursor pointer.Cursor) {
511537}
512538
513539func (w * window ) EditorStateChanged (old , new editorState ) {
514- if old .Selection .Range != new .Selection .Range || old .Snippet != new .Snippet {
540+ if old .Selection .Range != new .Selection .Range || ! areSnippetsConsistent ( old .Snippet , new .Snippet ) {
515541 C .discardMarkedText (w .view )
516542 w .w .SetComposingRegion (key.Range {Start : - 1 , End : - 1 })
517543 }
@@ -527,7 +553,7 @@ func (w *window) SetInputHint(_ key.InputHint) {}
527553func (w * window ) SetAnimating (anim bool ) {
528554 w .anim = anim
529555 window := C .windowForView (w .view )
530- if w .anim && window != 0 {
556+ if w .anim && window != 0 && C . isMiniaturized ( window ) == 0 {
531557 w .displayLink .Start ()
532558 } else {
533559 w .displayLink .Stop ()
@@ -545,23 +571,92 @@ func (w *window) runOnMain(f func()) {
545571}
546572
547573//export gio_onKeys
548- func gio_onKeys (h C.uintptr_t , cstr C.CFTypeRef , ti C.double , mods C.NSUInteger , keyDown C.bool ) {
574+ func gio_onKeys (h C.uintptr_t , event C.CFTypeRef , cstr C.CFTypeRef , ti C.double , mods C.NSUInteger , keyDown C.bool ) {
575+ w := windowFor (h )
576+ if w .keysDown == nil {
577+ w .keysDown = make (map [key.Name ]struct {})
578+ }
549579 str := nsstringToString (cstr )
550580 kmods := convertMods (mods )
551581 ks := key .Release
552582 if keyDown {
553583 ks = key .Press
584+ w .cmdKeys .eventStr = str
585+ w .cmdKeys .eventMods = kmods
586+ C .interpretKeyEvents (w .view , event )
554587 }
555- w := windowFor (h )
556588 for _ , k := range str {
557589 if n , ok := convertKey (k ); ok {
558- w . ProcessEvent ( key.Event {
590+ ke := key.Event {
559591 Name : n ,
560592 Modifiers : kmods ,
561593 State : ks ,
594+ }
595+ if keyDown {
596+ w .keysDown [ke .Name ] = struct {}{}
597+ if _ , isCmd := convertCommandKey (k ); isCmd || kmods .Contain (key .ModCommand ) {
598+ // doCommandBySelector already processed the event.
599+ return
600+ }
601+ } else {
602+ if _ , pressed := w .keysDown [n ]; ! pressed {
603+ continue
604+ }
605+ delete (w .keysDown , n )
606+ }
607+ w .ProcessEvent (ke )
608+ }
609+ }
610+ }
611+
612+ //export gio_onCommandBySelector
613+ func gio_onCommandBySelector (h C.uintptr_t ) C.bool {
614+ w := windowFor (h )
615+ ev := w .cmdKeys
616+ w .cmdKeys = cmdKeys {}
617+ handled := false
618+ for _ , k := range ev .eventStr {
619+ n , ok := convertCommandKey (k )
620+ if ! ok && ev .eventMods .Contain (key .ModCommand ) {
621+ n , ok = convertKey (k )
622+ }
623+ if ! ok {
624+ continue
625+ }
626+ ke := key.Event {
627+ Name : n ,
628+ Modifiers : ev .eventMods ,
629+ State : key .Press ,
630+ }
631+ handled = w .processEvent (ke ) || handled
632+ }
633+ return C .bool (handled )
634+ }
635+
636+ //export gio_onFlagsChanged
637+ func gio_onFlagsChanged (h C.uintptr_t , curMods C.NSUInteger ) {
638+ w := windowFor (h )
639+
640+ mods := []C.NSUInteger {C .NSControlKeyMask , C .NSAlternateKeyMask , C .NSShiftKeyMask , C .NSCommandKeyMask }
641+ keys := []key.Name {key .NameCtrl , key .NameAlt , key .NameShift , key .NameCommand }
642+
643+ for i , mod := range mods {
644+ wasPressed := w .lastMods & mod != 0
645+ isPressed := curMods & mod != 0
646+
647+ if wasPressed != isPressed {
648+ st := key .Release
649+ if isPressed {
650+ st = key .Press
651+ }
652+ w .ProcessEvent (key.Event {
653+ Name : keys [i ],
654+ State : st ,
562655 })
563656 }
564657 }
658+
659+ w .lastMods = curMods
565660}
566661
567662//export gio_onText
@@ -754,6 +849,15 @@ func gio_substringForProposedRange(h C.uintptr_t, crng C.NSRange, actual C.NSRan
754849//export gio_insertText
755850func gio_insertText (h C.uintptr_t , cstr C.CFTypeRef , crng C.NSRange ) {
756851 w := windowFor (h )
852+ str := nsstringToString (cstr )
853+ // macOS IME in some cases calls insertText for command keys such as backspace
854+ // instead of doCommandBySelector.
855+ for _ , r := range str {
856+ if _ , ok := convertCommandKey (r ); ok {
857+ w .w .SetComposingRegion (key.Range {Start : - 1 , End : - 1 })
858+ return
859+ }
860+ }
757861 state := w .w .EditorState ()
758862 rng := state .compose
759863 if rng .Start == - 1 {
@@ -765,7 +869,6 @@ func gio_insertText(h C.uintptr_t, cstr C.CFTypeRef, crng C.NSRange) {
765869 End : state .RunesIndex (int (crng .location + crng .length )),
766870 }
767871 }
768- str := nsstringToString (cstr )
769872 w .w .EditorReplace (rng , str )
770873 w .w .SetComposingRegion (key.Range {Start : - 1 , End : - 1 })
771874 start := rng .Start
@@ -834,8 +937,13 @@ func (w *window) draw() {
834937}
835938
836939func (w * window ) ProcessEvent (e event.Event ) {
837- w .w .ProcessEvent (e )
940+ w .processEvent (e )
941+ }
942+
943+ func (w * window ) processEvent (e event.Event ) bool {
944+ handled := w .w .ProcessEvent (e )
838945 w .loop .FlushEvents ()
946+ return handled
839947}
840948
841949func (w * window ) Event () event.Event {
@@ -960,10 +1068,10 @@ func osMain() {
9601068 C .gio_main ()
9611069}
9621070
963- func convertKey (k rune ) (key.Name , bool ) {
1071+ func convertCommandKey (k rune ) (key.Name , bool ) {
9641072 var n key.Name
9651073 switch k {
966- case 0x1b :
1074+ case '\x1b' : // ASCII escape.
9671075 n = key .NameEscape
9681076 case C .NSLeftArrowFunctionKey :
9691077 n = key .NameLeftArrow
@@ -973,22 +1081,36 @@ func convertKey(k rune) (key.Name, bool) {
9731081 n = key .NameUpArrow
9741082 case C .NSDownArrowFunctionKey :
9751083 n = key .NameDownArrow
976- case 0xd :
1084+ case '\r' :
9771085 n = key .NameReturn
978- case 0x3 :
1086+ case '\x03' :
9791087 n = key .NameEnter
9801088 case C .NSHomeFunctionKey :
9811089 n = key .NameHome
9821090 case C .NSEndFunctionKey :
9831091 n = key .NameEnd
984- case 0x7f :
1092+ case '\x7f' , '\b' :
9851093 n = key .NameDeleteBackward
9861094 case C .NSDeleteFunctionKey :
9871095 n = key .NameDeleteForward
1096+ case '\t' , 0x19 :
1097+ n = key .NameTab
9881098 case C .NSPageUpFunctionKey :
9891099 n = key .NamePageUp
9901100 case C .NSPageDownFunctionKey :
9911101 n = key .NamePageDown
1102+ default :
1103+ return "" , false
1104+ }
1105+ return n , true
1106+ }
1107+
1108+ func convertKey (k rune ) (key.Name , bool ) {
1109+ if n , ok := convertCommandKey (k ); ok {
1110+ return n , true
1111+ }
1112+ var n key.Name
1113+ switch k {
9921114 case C .NSF1FunctionKey :
9931115 n = key .NameF1
9941116 case C .NSF2FunctionKey :
@@ -1013,8 +1135,6 @@ func convertKey(k rune) (key.Name, bool) {
10131135 n = key .NameF11
10141136 case C .NSF12FunctionKey :
10151137 n = key .NameF12
1016- case 0x09 , 0x19 :
1017- n = key .NameTab
10181138 case 0x20 :
10191139 n = key .NameSpace
10201140 default :
0 commit comments