Сочетания клавиш какао в диалоге без меню редактирования - PullRequest
33 голосов
/ 09 июня 2009

У меня есть приложение LSUIElement, которое отображает элемент состояния меню. Приложение может отображать диалоговое окно, содержащее текстовое поле.

Если пользователь щелкает правой кнопкой мыши / удерживает нажатой клавишу Control текстовое поле, появляется меню, в котором можно вырезать, скопировать, вставить и т. Д. Однако стандартные сочетания клавиш Command-X, Command-C и Command-V не позволяют работа в поле. Я предполагаю, что это потому, что мое приложение не предоставляет меню «Правка» с этими ярлыками.

Я попытался добавить пункт меню «Правка» в меню моего приложения, как это было предложено в блоге Отправка некоторого кода , но это не сработало. Можно использовать пункты меню в меню «Правка», но сочетания клавиш по-прежнему не работают.

Я могу представить несколько способов взломать работу с клавиатурой, но есть ли «рекомендуемый» способ заставить это работать?

(Подробнее о приложении см. Отсчет по меню .)

Смежный вопрос: В модальном окне не работает копирование / вставка

Ответы [ 15 ]

42 голосов
/ 05 июля 2010

Улучшение этого решения CocoaRocket:

Следующее избавляет от необходимости создавать подкласс NSTextField и помнить об использовании подкласса во всем приложении; это также позволит копировать, вставлять и друзей для других респондентов, которые обрабатывают их, например. NSTextView.

Поместите это в подкласс NSApplication и соответственно измените основной класс в вашем Info.plist.

- (void) sendEvent:(NSEvent *)event {
    if ([event type] == NSKeyDown) {
        if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask) {
            if ([[event charactersIgnoringModifiers] isEqualToString:@"x"]) {
                if ([self sendAction:@selector(cut:) to:nil from:self])
                    return;
            }
            else if ([[event charactersIgnoringModifiers] isEqualToString:@"c"]) {
                if ([self sendAction:@selector(copy:) to:nil from:self])
                    return;
            }
            else if ([[event charactersIgnoringModifiers] isEqualToString:@"v"]) {
                if ([self sendAction:@selector(paste:) to:nil from:self])
                    return;
            }
            else if ([[event charactersIgnoringModifiers] isEqualToString:@"z"]) {
                if ([self sendAction:@selector(undo:) to:nil from:self])
                    return;
            }
            else if ([[event charactersIgnoringModifiers] isEqualToString:@"a"]) {
                if ([self sendAction:@selector(selectAll:) to:nil from:self])
                    return;
            }
        }
        else if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask)) {
            if ([[event charactersIgnoringModifiers] isEqualToString:@"Z"]) {
                if ([self sendAction:@selector(redo:) to:nil from:self])
                    return;
            }
        }
    }
    [super sendEvent:event];
}

// Blank Selectors to silence Xcode warnings: 'Undeclared selector undo:/redo:'
- (IBAction)undo:(id)sender {}
- (IBAction)redo:(id)sender {}
28 голосов
/ 11 июня 2009

Для меня работало Решение View , представленное в Копирование и вставка сочетаний клавиш на CocoaRocket.

По сути это означает создание подкласса NSTextField и переопределение performKeyEquivalent:.

Обновление: Сайт CocoaRocket явно пропал. Вот ссылка на Интернет-архив: http://web.archive.org/web/20100126000339/http://www.cocoarocket.com/articles/copypaste.html

Редактировать : код Swift выглядит следующим образом

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)
  }
}

Редактировать: Код Swift 3 выглядит следующим образом

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)
 }
}
14 голосов
/ 19 сентября 2010

У меня была такая же проблема, как и у вас, и я думаю, что мне удалось найти более простое решение. Вам просто нужно оставить оригинальное главное меню в MainMenu.xib - оно не будет отображаться, но все действия будут выполнены правильно. Хитрость заключается в том, что он должен быть оригинальным, если вы просто перетаскиваете новое NSMenu из библиотеки, приложение не распознает его как главное меню, и я понятия не имею, как пометить его как таковой (если вы снимите флажок LSUIElement вы увидите, что он не будет отображаться вверху, если он не является оригинальным). Если вы уже удалили его, вы можете создать новое приложение-образец и перетащить меню из его NIB, которое тоже работает.

9 голосов
/ 31 октября 2014

Я улучшил решение Адриана, чтобы оно работало, когда включен Caps Lock:

- (void)sendEvent:(NSEvent *)event
{
    if (event.type == NSKeyDown)
    {
        NSString *inputKey = [event.charactersIgnoringModifiers lowercaseString];
        if ((event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask ||
            (event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSAlphaShiftKeyMask))
        {
            if ([inputKey isEqualToString:@"x"])
            {
                if ([self sendAction:@selector(cut:) to:nil from:self])
                    return;
            }
            else if ([inputKey isEqualToString:@"c"])
            {
                if ([self sendAction:@selector(copy:) to:nil from:self])
                    return;
            }
            else if ([inputKey isEqualToString:@"v"])
            {
                if ([self sendAction:@selector(paste:) to:nil from:self])
                    return;
            }
            else if ([inputKey isEqualToString:@"z"])
            {
                if ([self sendAction:NSSelectorFromString(@"undo:") to:nil from:self])
                    return;
            }
            else if ([inputKey isEqualToString:@"a"])
            {
                if ([self sendAction:@selector(selectAll:) to:nil from:self])
                    return;
            }
        }
        else if ((event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask) ||
                 (event.modifierFlags & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask | NSAlphaShiftKeyMask))
        {
            if ([inputKey isEqualToString:@"z"])
            {
                if ([self sendAction:NSSelectorFromString(@"redo:") to:nil from:self])
                    return;
            }
        }
    }
    [super sendEvent:event];
}
7 голосов
/ 17 декабря 2016

Томас Килиан решение в 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)
}
4 голосов
/ 26 сентября 2018

Swift 4.2 для решения Томаса Килиана

class MTextField: NSSecureTextField {

    private let commandKey = NSEvent.ModifierFlags.command.rawValue
    private let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue

    override func performKeyEquivalent(with event: NSEvent) -> Bool {
        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)
    }

}
4 голосов
/ 26 марта 2015

Вот краткое пошаговое руководство для swift, основанное на превосходных ответах @Adrian, Travis B и Thomas Kilian.

Целью будет создание подкласса NSApplication вместо NSTextField. После того, как вы создали этот класс, сделайте ссылку на него в настройках «основного класса» Info.plist, как заявлено Адрианом. В отличие от людей Objective-C, мы, swiftlers, должны будем добавить дополнительный префикс к конфигурации PrincipalClass. Итак, поскольку мой проект называется «Foo», я собираюсь установить «Основной класс» в «Foo.MyApplication». В противном случае вы получите сообщение об ошибке «Class MyApplication not found».

Содержимое MyApplication читается следующим образом (скопировано и адаптировано из всех приведенных ответов)

import Cocoa

class MyApplication: NSApplication {
    override func sendEvent(event: NSEvent) {
        if event.type == NSEventType.KeyDown {
            if (event.modifierFlags & NSEventModifierFlags.DeviceIndependentModifierFlagsMask == NSEventModifierFlags.CommandKeyMask) {
                switch event.charactersIgnoringModifiers!.lowercaseString {
                case "x":
                    if NSApp.sendAction(Selector("cut:"), to:nil, from:self) { return }
                case "c":
                    if NSApp.sendAction(Selector("copy:"), to:nil, from:self) { return }
                case "v":
                    if NSApp.sendAction(Selector("paste:"), to:nil, from:self) { return }
                case "z":
                    if NSApp.sendAction(Selector("undo:"), to:nil, from:self) { return }
                case "a":
                    if NSApp.sendAction(Selector("selectAll:"), to:nil, from:self) { return }
                default:
                    break
                }
            }
            else if (event.modifierFlags & NSEventModifierFlags.DeviceIndependentModifierFlagsMask == (NSEventModifierFlags.CommandKeyMask | NSEventModifierFlags.ShiftKeyMask)) {
                if event.charactersIgnoringModifiers == "Z" {
                    if NSApp.sendAction(Selector("redo:"), to:nil, from:self) { return }
                }
            }
        }
        return super.sendEvent(event)
    }

}
3 голосов
/ 23 июля 2017

Нет необходимости добавлять новый класс, расширение или вообще какой-либо код.

  1. Просто добавьте новые элементы MenuItem в одно из ваших меню и назовите их «Копировать», «Вырезать» и «Вставить».
  2. Добавьте правильные сочетания клавиш для каждого элемента.
  3. Control + перетащите, чтобы соединить их с соответствующими методами, перечисленными в первом респонденте.

Преимущество здесь в том, что элементы не скрыты от ваших пользователей, и это занимает меньше времени, чем создание нового класса и переназначение ему всех ваших существующих текстовых полей.

3 голосов
/ 19 сентября 2016

Я объясняю, что у меня сработало в XCode 8 / Swift 3.

Я создал MyApplication.swift внутри папки моего проекта MyApp:

import Foundation
import Cocoa

class MyApplication: NSApplication {
    override func sendEvent(_ event: NSEvent) {
        if event.type == NSEventType.keyDown {

            if (event.modifierFlags.contains(NSEventModifierFlags.command)) {
                switch event.charactersIgnoringModifiers!.lowercased() {
                case "x":
                    if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return }
                case "c":
                    if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return }
                case "v":
                    if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return }
                case "a":
                    if NSApp.sendAction(#selector(NSText.selectAll(_:)), to:nil, from:self) { return }
                default:
                    break
                }
            }
        }
        return super.sendEvent(event)
    }

}

Затем измените Info.plist Principal class на MyApp.MyApplication. Создайте и запустите, чтобы проверить, что мои текстовые поля и текстовые представления поддерживают Cmd + X, Cmd + C, Cmd + V и Cmd + A.

2 голосов
/ 02 февраля 2019

Xcode10 / Swift 4.2 решение:

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)
}
}
...