Cocoa Keyboard Shortcuts in Dialog without an Edit Menu

前端 未结 16 1255
Happy的楠姐
Happy的楠姐 2020-12-04 07:28

I have an LSUIElement application that displays a menubar status item. The application can display a dialog window that contains a text field.

If the u

相关标签:
16条回答
  • 2020-12-04 07:42

    Xcode10/Swift 4.2 solution:

    import Cocoa
    
    extension NSTextView {
    override open func performKeyEquivalent(with event: NSEvent) -> Bool {
        let commandKey = NSEvent.ModifierFlags.command.rawValue
        let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue
        if event.type == NSEvent.EventType.keyDown {
            if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
                switch event.charactersIgnoringModifiers! {
                case "x":
                    if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return true }
                case "c":
                    if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return true }
                case "v":
                    if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return true }
                case "z":
                    if NSApp.sendAction(Selector(("undo:")), to:nil, from:self) { return true }
                case "a":
                    if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to:nil, from:self) { return true }
                default:
                    break
                }
            } else if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
                if event.charactersIgnoringModifiers == "Z" {
                    if NSApp.sendAction(Selector(("redo:")), to:nil, from:self) { return true }
                }
            }
        }
        return super.performKeyEquivalent(with: event)
    }
    }
    
    0 讨论(0)
  • 2020-12-04 07:49

    Thomas Kilian solution in swift 3.

    private let commandKey = NSEventModifierFlags.command.rawValue
    private let commandShiftKey = NSEventModifierFlags.command.rawValue | NSEventModifierFlags.shift.rawValue
    override func performKeyEquivalent(with event: NSEvent) -> Bool {
      if event.type == NSEventType.keyDown {
        if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
        switch event.charactersIgnoringModifiers! {
        case "x":
          if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return true }
        case "c":
          if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return true }
        case "v":
          if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return true }
        case "z":
          if NSApp.sendAction(Selector(("undo:")), to:nil, from:self) { return true }
        case "a":
          if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to:nil, from:self) { return true }
        default:
          break
        }
      }
      else if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
        if event.charactersIgnoringModifiers == "Z" {
          if NSApp.sendAction(Selector(("redo:")), to:nil, from:self) { return true }
        }
      }
    }
    return super.performKeyEquivalent(with: event)
    }
    
    0 讨论(0)
  • 2020-12-04 07:51

    What worked for me was using The View Solution presented in Copy and Paste Keyboard Shortcuts at CocoaRocket.

    Basically, this means subclassing NSTextField and overriding performKeyEquivalent:.

    Update: The CocoaRocket site is apparently gone. Here's the Internet Archive link: http://web.archive.org/web/20100126000339/http://www.cocoarocket.com/articles/copypaste.html

    Edit: The Swift code looks like this

    class Editing: NSTextField {
    
      private let commandKey = NSEventModifierFlags.CommandKeyMask.rawValue
      private let commandShiftKey = NSEventModifierFlags.CommandKeyMask.rawValue | NSEventModifierFlags.ShiftKeyMask.rawValue
      override func performKeyEquivalent(event: NSEvent) -> Bool {
        if event.type == NSEventType.KeyDown {
          if (event.modifierFlags.rawValue & NSEventModifierFlags.DeviceIndependentModifierFlagsMask.rawValue) == commandKey {
            switch event.charactersIgnoringModifiers! {
            case "x":
              if NSApp.sendAction(Selector("cut:"), to:nil, from:self) { return true }
            case "c":
              if NSApp.sendAction(Selector("copy:"), to:nil, from:self) { return true }
            case "v":
              if NSApp.sendAction(Selector("paste:"), to:nil, from:self) { return true }
            case "z":
              if NSApp.sendAction(Selector("undo:"), to:nil, from:self) { return true }
            case "a":
              if NSApp.sendAction(Selector("selectAll:"), to:nil, from:self) { return true }
            default:
              break
            }
          }
          else if (event.modifierFlags.rawValue & NSEventModifierFlags.DeviceIndependentModifierFlagsMask.rawValue) == commandShiftKey {
            if event.charactersIgnoringModifiers == "Z" {
              if NSApp.sendAction(Selector("redo:"), to:nil, from:self) { return true }
            }
          }
        }
        return super.performKeyEquivalent(event)
      }
    }
    

    Edit: The Swift 3 code looks like this

    class Editing: NSTextView {
    
    private let commandKey = NSEventModifierFlags.command.rawValue
    private let commandShiftKey = NSEventModifierFlags.command.rawValue | NSEventModifierFlags.shift.rawValue
    
    override func performKeyEquivalent(with event: NSEvent) -> Bool {
        if event.type == NSEventType.keyDown {
            if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
                switch event.charactersIgnoringModifiers! {
                case "x":
                    if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return true }
                case "c":
                    if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return true }
                case "v":
                    if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return true }
                case "z":
                    if NSApp.sendAction(Selector(("undo:")), to:nil, from:self) { return true }
                case "a":
                    if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to:nil, from:self) { return true }
                default:
                    break
                }
            }
            else if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
                if event.charactersIgnoringModifiers == "Z" {
                    if NSApp.sendAction(Selector(("redo:")), to:nil, from:self) { return true }
                }
            }
        }
        return super.performKeyEquivalent(with: event)
     }
    }
    
    0 讨论(0)
  • 2020-12-04 07:51

    Based on Thomas Kilian's answer, you can actually create an extension for NSTextField

    let commandKey = NSEvent.ModifierFlags.command.rawValue
    let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue
    
    extension NSTextField {
        func performEditingKeyEquivalent(with event: NSEvent) -> Bool {
            guard event.type == NSEvent.EventType.keyDown else { return false }
    
            if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
                if let character = event.charactersIgnoringModifiers {
                    switch character {
                    case "x":
                        if NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self) { return true }
                    case "c":
                        if NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self) { return true }
                    case "v":
                        if NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self) { return true }
                    case "z":
                        if NSApp.sendAction(Selector(("undo:")), to: nil, from: self) { return true }
                    case "a":
                        if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to: nil, from: self) { return true }
                    default:
                        break
                    }
                }
            } else if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
                if event.charactersIgnoringModifiers == "Z" {
                    if NSApp.sendAction(Selector(("redo:")), to: nil, from: self) { return true }
                }
            }
    
            return false
        }
    }
    

    In the followingg example, one can actually replace NSTextField with any of the NSTextField inheriting classes (e.g NSSearchField, NSSecureTextField) to have the new functionality

    class SearchField: NSTextField {
        override func performKeyEquivalent(with event: NSEvent) -> Bool {
            if performEditingKeyEquivalent(with: event) {
                return true
            }
    
            return super.performEditingKeyEquivalent(with: event)
        }
    }
    
    0 讨论(0)
  • 2020-12-04 07:55

    I had the same problem as you, and I think I've managed to find a simpler solution. You just need to leave the original main menu in MainMenu.xib - it won't be displayed, but all the actions will be handled properly. The trick is that it needs to be the original one, if you just drag a new NSMenu from the library, the app won't recognize it as Main Menu and I have no idea how to mark it as such (if you uncheck LSUIElement, you'll see that it won't show up at the top if it's not the original one). If you've already deleted it, you can create a new sample app and drag a menu from its NIB, that works too.

    0 讨论(0)
  • 2020-12-04 07:57

    Here's Travis' answer as C# for use with Xamarin.Mac:

        public override bool PerformKeyEquivalent (AppKit.NSEvent e)
        {
            if (e.Type == NSEventType.KeyDown) {
                var inputKey = e.CharactersIgnoringModifiers.ToLower ();
                if (   (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == NSEventModifierMask.CommandKeyMask
                    || (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == (NSEventModifierMask.CommandKeyMask | NSEventModifierMask.AlphaShiftKeyMask)) {
                    switch (inputKey) {
                    case "x":
                        NSApplication.SharedApplication.SendAction (new Selector ("cut:"), null, this);
                        return true;
                    case "c":
                        NSApplication.SharedApplication.SendAction (new Selector ("copy:"), null, this);
                        return true;
                    case "v":
                        NSApplication.SharedApplication.SendAction (new Selector ("paste:"), null, this);
                        return true;
                    case "z":
                        NSApplication.SharedApplication.SendAction (new Selector ("undo:"), null, this);
                        return true;
                    case "a":
                        NSApplication.SharedApplication.SendAction (new Selector ("selectAll:"), null, this);
                        return true;
                    }
                } else if (   (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == (NSEventModifierMask.CommandKeyMask | NSEventModifierMask.ShiftKeyMask)
                           || (e.ModifierFlags & NSEventModifierMask.DeviceIndependentModifierFlagsMask) == (NSEventModifierMask.CommandKeyMask | NSEventModifierMask.ShiftKeyMask | NSEventModifierMask.AlphaShiftKeyMask)) {
                    switch (inputKey) {
                    case "z":
                        NSApplication.SharedApplication.SendAction (new Selector ("redo:"), null, this);
                        return true;
                    }
                }
            }
            return base.PerformKeyEquivalent(e);
        }
    
    0 讨论(0)
提交回复
热议问题