5#include <AppKit/AppKit.h>
15#include <QtCore/qcoreapplication.h>
16#include <QtCore/qcoreevent.h>
17#include <QtCore/qvarlengtharray.h>
18#include <QtGui/private/qapplekeymapper_p.h>
20#include <QtCore/qpointer.h>
24 if (
const int len = string.length) {
25 QVarLengthArray<unichar, 10> characters(len);
27 for (
int i = 0; i < len; i++) {
28 characters[i] = [string characterAtIndex:i];
31 if (characters[i] == NSDeleteFunctionKey) {
32 characters[i] = NSDeleteCharacter;
37 return [NSString stringWithCharacters:characters.data() length:len];
42@implementation QCocoaNSMenu
44 QPointer<QCocoaMenu> _platformMenu;
47- (instancetype)initWithPlatformMenu:(QCocoaMenu *)menu
49 if ((self = [super initWithTitle:@
"Untitled"])) {
51 self.autoenablesItems = YES;
52 self.delegate = [QCocoaNSMenuDelegate sharedMenuDelegate];
58- (instancetype)initWithoutPlatformMenu:(NSString *)title
60 if (self = [super initWithTitle:title])
61 self.delegate = [QCocoaNSMenuDelegate sharedMenuDelegate];
67 return _platformMenu.data();
72@implementation QCocoaNSMenuItem
74 QPointer<QCocoaMenuItem> _platformMenuItem;
77+ (instancetype)separatorItemWithPlatformMenuItem:(QCocoaMenuItem *)menuItem
80 auto *item = qt_objc_cast<QCocoaNSMenuItem *>([self separatorItem]);
81 Q_ASSERT_X(item, qPrintable(
__FUNCTION__),
82 "Did +[NSMenuItem separatorItem] not invoke [[self alloc] init]?");
84 item.platformMenuItem = menuItem;
89- (instancetype)initWithPlatformMenuItem:(QCocoaMenuItem *)menuItem
91 if ((self = [super initWithTitle:@
"" action:nil keyEquivalent:@
""])) {
92 _platformMenuItem = menuItem;
100 return [self initWithPlatformMenuItem:
nullptr];
105 return _platformMenuItem.data();
108- (
void)setPlatformMenuItem:(QCocoaMenuItem *)menuItem
110 _platformMenuItem = menuItem;
115#define CHECK_MENU_CLASS(menu) Q_ASSERT_X([menu isMemberOfClass:[QCocoaNSMenu class]],
116 __FUNCTION__, "Menu is not a QCocoaNSMenu")
118@implementation QCocoaNSMenuDelegate
120+ (instancetype)sharedMenuDelegate
122 static QCocoaNSMenuDelegate *shared = nil;
123 static dispatch_once_t onceToken;
124 dispatch_once(&onceToken, ^{
125 shared = [[self alloc] init];
134- (
NSInteger)numberOfItemsInMenu:(NSMenu *)menu
137 return menu.numberOfItems;
140- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel
148 const auto &platformMenu =
static_cast<QCocoaNSMenu *>(menu).platformMenu;
152 if (
auto *platformItem = qt_objc_cast<QCocoaNSMenuItem *>(item).platformMenuItem) {
153 if (platformMenu->items().contains(platformItem)) {
154 if (
auto *itemSubmenu = platformItem->menu())
155 itemSubmenu->setAttachedItem(item);
162- (
void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
165 if (
auto *platformItem = qt_objc_cast<QCocoaNSMenuItem *>(item).platformMenuItem)
166 emit platformItem->hovered();
169- (
void)menuWillOpen:(NSMenu *)menu
172 auto *platformMenu =
static_cast<QCocoaNSMenu *>(menu).platformMenu;
176 platformMenu->setIsOpen(
true);
177 platformMenu->setIsAboutToShow(
true);
178 emit platformMenu->aboutToShow();
179 platformMenu->setIsAboutToShow(
false);
182- (
void)menuDidClose:(NSMenu *)menu
185 auto *platformMenu =
static_cast<QCocoaNSMenu *>(menu).platformMenu;
189 platformMenu->setIsOpen(
false);
191 emit platformMenu->aboutToHide();
194- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action
197
198
199
200
201
202
203
204
209 static const NSUInteger mask = NSEventModifierFlagShift | NSEventModifierFlagControl
210 | NSEventModifierFlagCommand | NSEventModifierFlagOption;
213 NSString *characters = qt_mac_removePrivateUnicode(event.charactersIgnoringModifiers);
214 const auto modifiers = event.modifierFlags & mask;
215 NSMenuItem *keyEquivalentItem = [self findItemInMenu:menu
217 modifiers:modifiers];
218 if (!keyEquivalentItem) {
220 characters = qt_mac_removePrivateUnicode(event.characters);
221 keyEquivalentItem = [self findItemInMenu:menu
223 modifiers:modifiers];
226 if (keyEquivalentItem) {
227 QObject *object = qApp->focusObject();
231 ulong nativeModifiers = event.modifierFlags;
232 Qt::KeyboardModifiers modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
233 NSString *charactersIgnoringModifiers = event.charactersIgnoringModifiers;
234 NSString *characters = event.characters;
236 if (charactersIgnoringModifiers.length > 0) {
237 if ((modifiers & Qt::ControlModifier) && characters.length > 0) {
238 ch = QChar([characters characterAtIndex:0]);
240 ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
242 keyCode = QAppleKeyMapper::fromCocoaKey(ch);
245 ch = QChar::ReplacementCharacter;
246 keyCode = Qt::Key_unknown;
249 QKeyEvent accel_ev(QEvent::ShortcutOverride, (keyCode & (~Qt::KeyboardModifierMask)),
250 Qt::KeyboardModifiers(modifiers & Qt::KeyboardModifierMask));
252 QCoreApplication::sendEvent(object, &accel_ev);
253 if (accel_ev.isAccepted()) {
254 [[NSApp keyWindow] sendEvent:event];
266 forKey:(NSString *)key
267 modifiers:(NSUInteger)modifiers
274 for (NSMenuItem *item in menu.itemArray) {
275 if (!item.enabled || item.hidden || item.separatorItem)
281 NSString *menuKey = item.keyEquivalent;
282 if (menuKey && NSOrderedSame == [menuKey compare:key]
283 && modifiers == item.keyEquivalentModifierMask)
292#undef CHECK_MENU_CLASS