Cocoa implementation of QPA menu interface.
Implement the QPA platform menu interface for Cocoa, including native menubar support and merging with the predefined menus created from the bundled .nib. Cleanup code previously used to maintain the menus, and add a manual test of the menus code. Change-Id: Ia99267ddb6485e18e05c540eb32c5aee6cbb85db Reviewed-by: Morten Johan Sørvig <morten.sorvig@nokia.com>
This commit is contained in:
parent
899f1d35a4
commit
b8246f08e4
@ -14,9 +14,12 @@ OBJECTIVE_SOURCES += main.mm \
|
||||
qcocoaglcontext.mm \
|
||||
qcocoanativeinterface.mm \
|
||||
qcocoaeventdispatcher.mm \
|
||||
qcocoamenuloader.mm \
|
||||
qcocoaapplicationdelegate.mm \
|
||||
qcocoaapplication.mm \
|
||||
qcocoamenu.mm \
|
||||
qcocoamenuitem.mm \
|
||||
qcocoamenubar.mm \
|
||||
qcocoamenuloader.mm \
|
||||
qcocoahelpers.mm \
|
||||
qmultitouch_mac.mm \
|
||||
qcocoaaccessibilityelement.mm \
|
||||
@ -46,9 +49,12 @@ HEADERS += qcocoaintegration.h \
|
||||
qcocoaglcontext.h \
|
||||
qcocoanativeinterface.h \
|
||||
qcocoaeventdispatcher.h \
|
||||
qcocoamenuloader.h \
|
||||
qcocoaapplicationdelegate.h \
|
||||
qcocoaapplication.h \
|
||||
qcocoamenu.h \
|
||||
qcocoamenuitem.h \
|
||||
qcocoamenubar.h \
|
||||
qcocoamenuloader.h \
|
||||
qcocoahelpers.h \
|
||||
qmultitouch_mac_p.h \
|
||||
qcocoaaccessibilityelement.h \
|
||||
|
@ -189,17 +189,6 @@ QT_USE_NAMESPACE
|
||||
[super sendEvent:event];
|
||||
}
|
||||
|
||||
- (void)qtDispatcherToQAction:(id)sender
|
||||
{
|
||||
// Forward actions sent from the menu bar (e.g. quit) to the menu loader.
|
||||
// Having this method here means that we are the last stop in the responder
|
||||
// chain, and that we are able to handle menu actions even when no window is
|
||||
// visible on screen. Note: If Qt is used as a plugin, Qt will not use a
|
||||
// native menu bar. Hence, we will also not need to do any redirection etc. as
|
||||
// we do with sendEvent.
|
||||
[[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
@ -355,7 +355,7 @@ static void cleanupCocoaApplicationDelegate()
|
||||
- (void)qtDispatcherToQAction:(id)sender
|
||||
{
|
||||
Q_UNUSED(sender);
|
||||
[qtMenuLoader qtDispatcherToQAction:sender];
|
||||
[qtMenuLoader qtDispatcherToQPAMenuItem:sender];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -115,6 +115,11 @@ inline NSPoint qt_mac_flipPoint(const QPointF &p)
|
||||
|
||||
NSRect qt_mac_flipRect(const QRect &rect, QWindow *window);
|
||||
|
||||
// strip out '&' characters, and convert "&&" to a single '&', in menu
|
||||
// text - since menu text is sometimes decorated with these for Windows
|
||||
// accelerators.
|
||||
QString qt_mac_removeAmpersandEscapes(QString s);
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif //QCOCOAHELPERS_H
|
||||
|
@ -581,4 +581,18 @@ CGFloat qt_mac_get_scalefactor()
|
||||
return [[NSScreen mainScreen] userSpaceScaleFactor];
|
||||
}
|
||||
|
||||
QString qt_mac_removeAmpersandEscapes(QString s)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < s.size()) {
|
||||
++i;
|
||||
if (s.at(i-1) != QLatin1Char('&'))
|
||||
continue;
|
||||
if (i < s.size() && s.at(i) == QLatin1Char('&'))
|
||||
++i;
|
||||
s.remove(i-1,1);
|
||||
}
|
||||
return s.trimmed();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -1,9 +1,10 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
@ -39,42 +40,66 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
#ifndef QCOCOAMENU_H
|
||||
#define QCOCOAMENU_H
|
||||
|
||||
#include "qmacdefines_mac.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <QtCore/QList>
|
||||
#include <qpa/qplatformmenu.h>
|
||||
#include "qcocoamenuitem.h"
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QMenu)
|
||||
QT_FORWARD_DECLARE_CLASS(QAction)
|
||||
@class NSMenuItem;
|
||||
@class NSMenu;
|
||||
@class NSObject;
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
@protocol NSMenuDelegate <NSObject>
|
||||
- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item;
|
||||
- (void)menuWillOpen:(NSMenu*)menu;
|
||||
- (void)menuDidClose:(NSMenu*)menu;
|
||||
- (BOOL)hasShortcut:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier
|
||||
whichItem:(NSMenuItem**)outItem;
|
||||
@end
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QCocoaMenu : public QPlatformMenu
|
||||
{
|
||||
public:
|
||||
QCocoaMenu();
|
||||
|
||||
inline virtual void setTag(quintptr tag)
|
||||
{ m_tag = tag; }
|
||||
inline virtual quintptr tag() const
|
||||
{ return m_tag; }
|
||||
|
||||
void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before);
|
||||
void removeMenuItem(QPlatformMenuItem *menuItem);
|
||||
void syncMenuItem(QPlatformMenuItem *menuItem);
|
||||
void setEnabled(bool enabled);
|
||||
void syncSeparatorsCollapsible(bool enable);
|
||||
|
||||
void syncModalState(bool modal);
|
||||
|
||||
virtual void setText(const QString &text);
|
||||
|
||||
void setParentItem(QCocoaMenuItem* item);
|
||||
|
||||
inline NSMenu *nsMenu() const
|
||||
{ return m_nativeMenu; }
|
||||
inline NSMenuItem *nsMenuItem() const
|
||||
{ return m_nativeItem; }
|
||||
|
||||
virtual QPlatformMenuItem *menuItemAt(int position) const;
|
||||
virtual QPlatformMenuItem *menuItemForTag(quintptr tag) const;
|
||||
|
||||
QList<QCocoaMenuItem *> merged() const;
|
||||
private:
|
||||
QCocoaMenuItem *itemOrNull(int index) const;
|
||||
void insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem);
|
||||
|
||||
QList<QCocoaMenuItem *> m_menuItems;
|
||||
NSMenu *m_nativeMenu;
|
||||
NSMenuItem *m_nativeItem;
|
||||
NSObject *m_delegate;
|
||||
bool m_enabled;
|
||||
quintptr m_tag;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif
|
||||
|
||||
@interface QT_MANGLE_NAMESPACE(QNativeCocoaMenu) : NSMenu <NSMenuDelegate>
|
||||
{
|
||||
QMenu *qmenu;
|
||||
QAction *previousAction;
|
||||
}
|
||||
- (id)initWithQMenu:(QMenu*)menu;
|
||||
- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action;
|
||||
- (NSInteger)indexOfItemWithTarget:(id)anObject andAction:(SEL)actionSelector;
|
||||
@end
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
@ -39,229 +39,244 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qapplication.h"
|
||||
#include "qvarlengtharray.h"
|
||||
#import "qcocoamenu.h"
|
||||
#import "qcocoamenuloader.h"
|
||||
#import "qcocoaapplication.h"
|
||||
#include "qcocoamenu.h"
|
||||
|
||||
#include "qcocoahelpers.h"
|
||||
#include <private/qapplication_p.h>
|
||||
#include <private/qaction_p.h>
|
||||
#include "qcocoaautoreleasepool.h"
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QAction)
|
||||
QT_FORWARD_DECLARE_CLASS(QWidget)
|
||||
QT_FORWARD_DECLARE_CLASS(QApplication)
|
||||
QT_FORWARD_DECLARE_CLASS(QCoreApplication)
|
||||
QT_FORWARD_DECLARE_CLASS(QApplicationPrivate)
|
||||
QT_FORWARD_DECLARE_CLASS(QKeyEvent)
|
||||
QT_FORWARD_DECLARE_CLASS(QEvent)
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
extern void qt_mac_menu_collapseSeparators(NSMenu *menu, bool collapse);
|
||||
void qt_mac_clear_status_text(QAction *action);
|
||||
extern void qt_mac_emit_menuSignals(QMenu *menu, bool show);
|
||||
extern void qt_mac_menu_emit_hovered(QMenu *menu, QAction *action);
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
@implementation QT_MANGLE_NAMESPACE(QNativeCocoaMenu)
|
||||
|
||||
- (id)initWithQMenu:(QMenu*)menu
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
qmenu = menu;
|
||||
previousAction = 0;
|
||||
[self setAutoenablesItems:NO];
|
||||
[self setDelegate:self];
|
||||
@interface QT_MANGLE_NAMESPACE(QCocoaMenuDelegate) : NSObject <NSMenuDelegate> {
|
||||
QCocoaMenu *m_menu;
|
||||
}
|
||||
|
||||
- (id) initWithMenu:(QCocoaMenu*) m;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QT_MANGLE_NAMESPACE(QCocoaMenuDelegate)
|
||||
|
||||
- (id) initWithMenu:(QCocoaMenu*) m
|
||||
{
|
||||
if ((self = [super init]))
|
||||
m_menu = m;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item
|
||||
- (void) menuWillOpen:(NSMenu*)m
|
||||
{
|
||||
Q_UNUSED(menu);
|
||||
|
||||
if (!item) {
|
||||
if (previousAction) {
|
||||
qt_mac_clear_status_text(previousAction);
|
||||
previousAction = 0;
|
||||
}
|
||||
return;
|
||||
Q_UNUSED(m);
|
||||
emit m_menu->aboutToShow();
|
||||
}
|
||||
|
||||
if (QAction *action = reinterpret_cast<QAction *>([item tag])) {
|
||||
QMenu *qtmenu = static_cast<QT_MANGLE_NAMESPACE(QNativeCocoaMenu) *>(menu)->qmenu;
|
||||
previousAction = action;
|
||||
action->activate(QAction::Hover);
|
||||
qt_mac_menu_emit_hovered(qtmenu, action);
|
||||
action->showStatusText(0); // 0 widget -> action's parent
|
||||
}
|
||||
}
|
||||
|
||||
- (void)menuWillOpen:(NSMenu*)menu
|
||||
- (void) menuDidClose:(NSMenu*)m
|
||||
{
|
||||
while (QWidget *popup
|
||||
= QApplication::activePopupWidget())
|
||||
popup->close();
|
||||
QMenu *qtmenu = static_cast<QT_MANGLE_NAMESPACE(QNativeCocoaMenu) *>(menu)->qmenu;
|
||||
qt_mac_emit_menuSignals(qtmenu, true);
|
||||
qt_mac_menu_collapseSeparators(menu, qtmenu->separatorsCollapsible());
|
||||
Q_UNUSED(m);
|
||||
// wrong, but it's the best we can do
|
||||
emit m_menu->aboutToHide();
|
||||
}
|
||||
|
||||
- (void)menuDidClose:(NSMenu*)menu
|
||||
- (void) itemFired:(NSMenuItem*) item
|
||||
{
|
||||
qt_mac_emit_menuSignals(((QT_MANGLE_NAMESPACE(QNativeCocoaMenu) *)menu)->qmenu, false);
|
||||
if (previousAction) {
|
||||
qt_mac_clear_status_text(previousAction);
|
||||
previousAction = 0;
|
||||
}
|
||||
QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([item tag]);
|
||||
cocoaItem->activated();
|
||||
}
|
||||
|
||||
- (BOOL)hasShortcut:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier
|
||||
whichItem:(NSMenuItem**)outItem
|
||||
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
|
||||
{
|
||||
for (NSMenuItem *item in [menu itemArray]) {
|
||||
if (![item isEnabled] || [item isHidden] || [item isSeparatorItem])
|
||||
continue;
|
||||
if ([item hasSubmenu]) {
|
||||
if ([self hasShortcut:[item submenu]
|
||||
forKey:key
|
||||
forModifiers:modifier whichItem:outItem]) {
|
||||
if (outItem)
|
||||
*outItem = item;
|
||||
if (![menuItem tag])
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
NSString *menuKey = [item keyEquivalent];
|
||||
if (menuKey && NSOrderedSame == [menuKey compare:key]
|
||||
&& (modifier == [item keyEquivalentModifierMask])) {
|
||||
if (outItem)
|
||||
*outItem = item;
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
if (outItem)
|
||||
*outItem = 0;
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *qt_mac_removePrivateUnicode(NSString* string)
|
||||
{
|
||||
int len = [string length];
|
||||
if (len) {
|
||||
QVarLengthArray <unichar, 10> characters(len);
|
||||
bool changed = false;
|
||||
for (int i = 0; i<len; i++) {
|
||||
characters[i] = [string characterAtIndex:i];
|
||||
// check if they belong to key codes in private unicode range
|
||||
// currently we need to handle only the NSDeleteFunctionKey
|
||||
if (characters[i] == NSDeleteFunctionKey) {
|
||||
characters[i] = NSDeleteCharacter;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
return [NSString stringWithCharacters:characters.data() length:len];
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action
|
||||
{
|
||||
// Check if the menu actually has a keysequence defined for this key event.
|
||||
// If it does, then we will first send the key sequence to the QWidget that has focus
|
||||
// since (in Qt's eyes) it needs to a chance at the key event first. If the widget
|
||||
// accepts the key event, we then return YES, but set the target and action to be nil,
|
||||
// which means that the action should not be triggered, and instead dispatch the event ourselves.
|
||||
// In every other case we return NO, which means that Cocoa can do as it pleases
|
||||
// (i.e., fire the menu action).
|
||||
NSMenuItem *whichItem;
|
||||
// Change the private unicode keys to the ones used in setting the "Key Equivalents"
|
||||
NSString *characters = qt_mac_removePrivateUnicode([event characters]);
|
||||
if ([self hasShortcut:menu
|
||||
forKey:characters
|
||||
// Interested only in Shift, Cmd, Ctrl & Alt Keys, so ignoring masks like, Caps lock, Num Lock ...
|
||||
forModifiers:([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask | NSAlternateKeyMask))
|
||||
whichItem:&whichItem]) {
|
||||
QWidget *widget = 0;
|
||||
QAction *qaction = 0;
|
||||
if (whichItem && [whichItem tag]) {
|
||||
qaction = reinterpret_cast<QAction *>([whichItem tag]);
|
||||
}
|
||||
if (qApp->activePopupWidget())
|
||||
widget = (qApp->activePopupWidget()->focusWidget() ?
|
||||
qApp->activePopupWidget()->focusWidget() : qApp->activePopupWidget());
|
||||
else if (QApplicationPrivate::focus_widget)
|
||||
widget = QApplicationPrivate::focus_widget;
|
||||
// If we could not find any receivers, pass it to the active window
|
||||
if (!widget)
|
||||
widget = qApp->activeWindow();
|
||||
if (qaction && widget) {
|
||||
int key = qaction->shortcut();
|
||||
QKeyEvent accel_ev(QEvent::ShortcutOverride, (key & (~Qt::KeyboardModifierMask)),
|
||||
Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask));
|
||||
accel_ev.ignore();
|
||||
|
||||
// ### qt_sendSpontaneousEvent(widget, &accel_ev);
|
||||
|
||||
if (accel_ev.isAccepted()) {
|
||||
qWarning("Unimplemented: qt_dispatchKeyEvent");
|
||||
#if 0
|
||||
qt_dispatchKeyEvent(event, widget);
|
||||
#endif
|
||||
*target = nil;
|
||||
*action = nil;
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSInteger)indexOfItemWithTarget:(id)anObject andAction:(SEL)actionSelector
|
||||
{
|
||||
NSInteger index = [super indexOfItemWithTarget:anObject andAction:actionSelector];
|
||||
static SEL selForOFCP = NSSelectorFromString(@"orderFrontCharacterPalette:");
|
||||
if (index == -1 && selForOFCP == actionSelector) {
|
||||
// Check if the 'orderFrontCharacterPalette' SEL exists for QNativeCocoaMenuLoader object
|
||||
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
|
||||
return [super indexOfItemWithTarget:loader andAction:actionSelector];
|
||||
}
|
||||
return index;
|
||||
QCocoaMenuItem* cocoaItem = reinterpret_cast<QCocoaMenuItem *>([menuItem tag]);
|
||||
return cocoaItem->isEnabled();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
extern int qt_mac_menus_open_count; // qmenu_mac.mm
|
||||
|
||||
void qt_mac_emit_menuSignals(QMenu *menu, bool show)
|
||||
QCocoaMenu::QCocoaMenu() :
|
||||
m_enabled(true),
|
||||
m_tag(0)
|
||||
{
|
||||
if (!menu)
|
||||
return;
|
||||
int delta;
|
||||
if (show) {
|
||||
emit menu->aboutToShow();
|
||||
delta = 1;
|
||||
m_delegate = [[QCocoaMenuDelegate alloc] initWithMenu:this];
|
||||
m_nativeItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||
m_nativeMenu = [[NSMenu alloc] initWithTitle:@"Untitled"];
|
||||
[m_nativeMenu setAutoenablesItems:YES];
|
||||
m_nativeMenu.delegate = (QCocoaMenuDelegate *) m_delegate;
|
||||
[m_nativeItem setSubmenu:m_nativeMenu];
|
||||
}
|
||||
|
||||
void QCocoaMenu::setText(const QString &text)
|
||||
{
|
||||
QString stripped = qt_mac_removeAmpersandEscapes(text);
|
||||
[m_nativeMenu setTitle:QCFString::toNSString(stripped)];
|
||||
[m_nativeItem setTitle:QCFString::toNSString(stripped)];
|
||||
}
|
||||
|
||||
void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
|
||||
{
|
||||
QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem);
|
||||
QCocoaMenuItem *beforeItem = static_cast<QCocoaMenuItem *>(before);
|
||||
|
||||
cocoaItem->sync();
|
||||
if (beforeItem) {
|
||||
int index = m_menuItems.indexOf(beforeItem);
|
||||
// if a before item is supplied, it should be in the menu
|
||||
Q_ASSERT(index >= 0);
|
||||
m_menuItems.insert(index, cocoaItem);
|
||||
} else {
|
||||
emit menu->aboutToHide();
|
||||
delta = -1;
|
||||
}
|
||||
qt_mac_menus_open_count += delta;
|
||||
m_menuItems.append(cocoaItem);
|
||||
}
|
||||
|
||||
void qt_mac_clear_status_text(QAction *action)
|
||||
insertNative(cocoaItem, beforeItem);
|
||||
}
|
||||
|
||||
void QCocoaMenu::insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem)
|
||||
{
|
||||
action->d_func()->showStatusText(0, QString());
|
||||
[item->nsItem() setTarget:m_delegate];
|
||||
[item->nsItem() setAction:@selector(itemFired:)];
|
||||
|
||||
if (item->isMerged())
|
||||
return;
|
||||
|
||||
// if the item we're inserting before is merged, skip along until
|
||||
// we find a non-merged real item to insert ahead of.
|
||||
while (beforeItem && beforeItem->isMerged()) {
|
||||
beforeItem = itemOrNull(m_menuItems.indexOf(beforeItem) + 1);
|
||||
}
|
||||
|
||||
void qt_mac_menu_emit_hovered(QMenu *menu, QAction *action)
|
||||
if (beforeItem) {
|
||||
Q_ASSERT(!beforeItem->isMerged());
|
||||
NSUInteger nativeIndex = [m_nativeMenu indexOfItem:beforeItem->nsItem()];
|
||||
[m_nativeMenu insertItem: item->nsItem() atIndex: nativeIndex];
|
||||
} else {
|
||||
[m_nativeMenu addItem: item->nsItem()];
|
||||
}
|
||||
}
|
||||
|
||||
void QCocoaMenu::removeMenuItem(QPlatformMenuItem *menuItem)
|
||||
{
|
||||
emit menu->hovered(action);
|
||||
QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem);
|
||||
Q_ASSERT(m_menuItems.contains(cocoaItem));
|
||||
m_menuItems.removeOne(cocoaItem);
|
||||
if (!cocoaItem->isMerged()) {
|
||||
[m_nativeMenu removeItem: cocoaItem->nsItem()];
|
||||
}
|
||||
}
|
||||
|
||||
QCocoaMenuItem *QCocoaMenu::itemOrNull(int index) const
|
||||
{
|
||||
if ((index < 0) || (index >= m_menuItems.size()))
|
||||
return 0;
|
||||
|
||||
QT_END_NAMESPACE
|
||||
return m_menuItems.at(index);
|
||||
}
|
||||
|
||||
void QCocoaMenu::syncMenuItem(QPlatformMenuItem *menuItem)
|
||||
{
|
||||
QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem);
|
||||
Q_ASSERT(m_menuItems.contains(cocoaItem));
|
||||
|
||||
bool wasMerged = cocoaItem->isMerged();
|
||||
NSMenuItem *oldItem = [m_nativeMenu itemWithTag:(NSInteger) cocoaItem];
|
||||
|
||||
if (cocoaItem->sync() != oldItem) {
|
||||
// native item was changed for some reason
|
||||
if (!wasMerged) {
|
||||
[m_nativeMenu removeItem:oldItem];
|
||||
}
|
||||
|
||||
QCocoaMenuItem* beforeItem = itemOrNull(m_menuItems.indexOf(cocoaItem) + 1);
|
||||
insertNative(cocoaItem, beforeItem);
|
||||
}
|
||||
}
|
||||
|
||||
void QCocoaMenu::syncSeparatorsCollapsible(bool enable)
|
||||
{
|
||||
QCocoaAutoReleasePool pool;
|
||||
if (enable) {
|
||||
bool previousIsSeparator = true; // setting to true kills all the separators placed at the top.
|
||||
NSMenuItem *previousItem = nil;
|
||||
|
||||
NSArray *itemArray = [m_nativeMenu itemArray];
|
||||
for (unsigned int i = 0; i < [itemArray count]; ++i) {
|
||||
NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]);
|
||||
if ([item isSeparatorItem])
|
||||
[item setHidden:previousIsSeparator];
|
||||
|
||||
if (![item isHidden]) {
|
||||
previousItem = item;
|
||||
previousIsSeparator = ([previousItem isSeparatorItem]);
|
||||
}
|
||||
}
|
||||
|
||||
// We now need to check the final item since we don't want any separators at the end of the list.
|
||||
if (previousItem && previousIsSeparator)
|
||||
[previousItem setHidden:YES];
|
||||
} else {
|
||||
foreach (QCocoaMenuItem *item, m_menuItems) {
|
||||
if (!item->isSeparator())
|
||||
continue;
|
||||
|
||||
// sync the visiblity directly
|
||||
item->sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QCocoaMenu::setParentItem(QCocoaMenuItem *item)
|
||||
{
|
||||
Q_UNUSED(item);
|
||||
}
|
||||
|
||||
void QCocoaMenu::setEnabled(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
}
|
||||
|
||||
QPlatformMenuItem *QCocoaMenu::menuItemAt(int position) const
|
||||
{
|
||||
return m_menuItems.at(position);
|
||||
}
|
||||
|
||||
QPlatformMenuItem *QCocoaMenu::menuItemForTag(quintptr tag) const
|
||||
{
|
||||
foreach (QCocoaMenuItem *item, m_menuItems) {
|
||||
if (item->tag() == tag)
|
||||
return item;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QList<QCocoaMenuItem *> QCocoaMenu::merged() const
|
||||
{
|
||||
QList<QCocoaMenuItem *> result;
|
||||
foreach (QCocoaMenuItem *item, m_menuItems) {
|
||||
if (item->menu()) { // recurse into submenus
|
||||
result.append(item->menu()->merged());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->isMerged())
|
||||
result.append(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void QCocoaMenu::syncModalState(bool modal)
|
||||
{
|
||||
if (!m_enabled)
|
||||
modal = true;
|
||||
|
||||
[m_nativeItem setEnabled:!modal];
|
||||
|
||||
foreach (QCocoaMenuItem *item, m_menuItems) {
|
||||
if (item->menu()) { // recurse into submenus
|
||||
item->menu()->syncModalState(modal);
|
||||
continue;
|
||||
}
|
||||
|
||||
item->syncModalState(modal);
|
||||
}
|
||||
}
|
||||
|
82
src/plugins/platforms/cocoa/qcocoamenubar.h
Normal file
82
src/plugins/platforms/cocoa/qcocoamenubar.h
Normal file
@ -0,0 +1,82 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the plugins module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QCOCOAMENUBAR_H
|
||||
#define QCOCOAMENUBAR_H
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <qpa/qplatformmenu.h>
|
||||
#include "qcocoamenu.h"
|
||||
|
||||
@class NSMenu;
|
||||
class QCocoaWindow;
|
||||
|
||||
class QCocoaMenuBar : public QPlatformMenuBar
|
||||
{
|
||||
public:
|
||||
QCocoaMenuBar();
|
||||
virtual ~QCocoaMenuBar();
|
||||
|
||||
virtual void insertMenu(QPlatformMenu *menu, QPlatformMenu* before);
|
||||
virtual void removeMenu(QPlatformMenu *menu);
|
||||
virtual void syncMenu(QPlatformMenuItem *menuItem);
|
||||
virtual void handleReparent(QWindow *newParentWindow);
|
||||
virtual QPlatformMenu *menuForTag(quintptr tag) const;
|
||||
|
||||
inline NSMenu *nsMenu() const
|
||||
{ return m_nativeMenu; }
|
||||
|
||||
static void updateMenuBarImmediately();
|
||||
|
||||
QList<QCocoaMenuItem*> merged() const;
|
||||
private:
|
||||
static QCocoaWindow *findWindowForMenubar();
|
||||
static QCocoaMenuBar *findGlobalMenubar();
|
||||
|
||||
bool shouldDisable(QCocoaWindow *active) const;
|
||||
|
||||
QList<QCocoaMenu*> m_menus;
|
||||
NSMenu *m_nativeMenu;
|
||||
QCocoaWindow *m_window;
|
||||
};
|
||||
|
||||
#endif
|
254
src/plugins/platforms/cocoa/qcocoamenubar.mm
Normal file
254
src/plugins/platforms/cocoa/qcocoamenubar.mm
Normal file
@ -0,0 +1,254 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include "qcocoamenubar.h"
|
||||
#include "qcocoawindow.h"
|
||||
#include "qcocoamenuloader.h"
|
||||
#include "qcocoaapplication.h" // for custom application category
|
||||
#include "qcocoaautoreleasepool.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
static QList<QCocoaMenuBar*> static_menubars;
|
||||
|
||||
static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
|
||||
{
|
||||
return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
|
||||
}
|
||||
|
||||
|
||||
QCocoaMenuBar::QCocoaMenuBar() :
|
||||
m_window(0)
|
||||
{
|
||||
static_menubars.append(this);
|
||||
|
||||
m_nativeMenu = [[NSMenu alloc] init];
|
||||
#ifdef QT_COCOA_ENABLE_MENU_DEBUG
|
||||
qDebug() << "Construct QCocoaMenuBar" << this << m_nativeMenu;
|
||||
#endif
|
||||
}
|
||||
|
||||
QCocoaMenuBar::~QCocoaMenuBar()
|
||||
{
|
||||
#ifdef QT_COCOA_ENABLE_MENU_DEBUG
|
||||
qDebug() << "~QCocoaMenuBar" << this;
|
||||
#endif
|
||||
[m_nativeMenu release];
|
||||
static_menubars.removeOne(this);
|
||||
|
||||
if (m_window)
|
||||
m_window->setMenubar(0);
|
||||
}
|
||||
|
||||
void QCocoaMenuBar::insertMenu(QPlatformMenu *platformMenu, QPlatformMenu *before)
|
||||
{
|
||||
QCocoaMenu *menu = static_cast<QCocoaMenu *>(platformMenu);
|
||||
QCocoaMenu *beforeMenu = static_cast<QCocoaMenu *>(before);
|
||||
#ifdef QT_COCOA_ENABLE_MENU_DEBUG
|
||||
qDebug() << "QCocoaMenuBar" << this << "insertMenu" << menu << "before" << before;
|
||||
#endif
|
||||
|
||||
Q_ASSERT(!m_menus.contains(menu));
|
||||
if (beforeMenu) {
|
||||
Q_ASSERT(m_menus.contains(beforeMenu));
|
||||
m_menus.insert(m_menus.indexOf(beforeMenu), menu);
|
||||
NSUInteger nativeIndex = [m_nativeMenu indexOfItem:beforeMenu->nsMenuItem()];
|
||||
[m_nativeMenu insertItem: menu->nsMenuItem() atIndex: nativeIndex];
|
||||
} else {
|
||||
m_menus.append(menu);
|
||||
[m_nativeMenu addItem: menu->nsMenuItem()];
|
||||
}
|
||||
|
||||
[m_nativeMenu setSubmenu: menu->nsMenu() forItem: menu->nsMenuItem()];
|
||||
}
|
||||
|
||||
void QCocoaMenuBar::removeMenu(QPlatformMenu *platformMenu)
|
||||
{
|
||||
QCocoaMenu *menu = static_cast<QCocoaMenu *>(platformMenu);
|
||||
Q_ASSERT(m_menus.contains(menu));
|
||||
m_menus.removeOne(menu);
|
||||
|
||||
NSUInteger realIndex = [m_nativeMenu indexOfItem:menu->nsMenuItem()];
|
||||
[m_nativeMenu removeItemAtIndex: realIndex];
|
||||
}
|
||||
|
||||
void QCocoaMenuBar::syncMenu(QPlatformMenuItem *menuItem)
|
||||
{
|
||||
Q_UNUSED(menuItem);
|
||||
}
|
||||
|
||||
void QCocoaMenuBar::handleReparent(QWindow *newParentWindow)
|
||||
{
|
||||
#ifdef QT_COCOA_ENABLE_MENU_DEBUG
|
||||
qDebug() << "QCocoaMenuBar" << this << "handleReparent" << newParentWindow;
|
||||
#endif
|
||||
|
||||
if (m_window)
|
||||
m_window->setMenubar(NULL);
|
||||
|
||||
if (newParentWindow == NULL) {
|
||||
m_window = NULL;
|
||||
} else {
|
||||
m_window = static_cast<QCocoaWindow*>(newParentWindow->handle());
|
||||
m_window->setMenubar(this);
|
||||
}
|
||||
|
||||
updateMenuBarImmediately();
|
||||
}
|
||||
|
||||
QCocoaWindow *QCocoaMenuBar::findWindowForMenubar()
|
||||
{
|
||||
if (qApp->focusWindow())
|
||||
return static_cast<QCocoaWindow*>(qApp->focusWindow()->handle());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QCocoaMenuBar *QCocoaMenuBar::findGlobalMenubar()
|
||||
{
|
||||
foreach (QCocoaMenuBar *mb, static_menubars) {
|
||||
if (mb->m_window == NULL)
|
||||
return mb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void QCocoaMenuBar::updateMenuBarImmediately()
|
||||
{
|
||||
QCocoaAutoReleasePool pool;
|
||||
QCocoaMenuBar *mb = findGlobalMenubar();
|
||||
QCocoaWindow *cw = findWindowForMenubar();
|
||||
if (cw && cw->menubar())
|
||||
mb = cw->menubar();
|
||||
|
||||
if (!mb)
|
||||
return;
|
||||
|
||||
#ifdef QT_COCOA_ENABLE_MENU_DEBUG
|
||||
qDebug() << "QCocoaMenuBar" << "updateMenuBarImmediately" << cw;
|
||||
#endif
|
||||
bool disableForModal = mb->shouldDisable(cw);
|
||||
// force a sync?
|
||||
foreach (QCocoaMenu *m, mb->m_menus) {
|
||||
mb->syncMenu(m);
|
||||
m->syncModalState(disableForModal);
|
||||
}
|
||||
|
||||
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
|
||||
[loader ensureAppMenuInMenu:mb->nsMenu()];
|
||||
|
||||
NSMutableSet *mergedItems = [[NSMutableSet setWithCapacity:0] retain];
|
||||
foreach (QCocoaMenuItem *m, mb->merged()) {
|
||||
[mergedItems addObject:m->nsItem()];
|
||||
m->syncMerged();
|
||||
}
|
||||
|
||||
// hide+disable all mergeable items we're not currently using
|
||||
for (NSMenuItem *mergeable in [loader mergeable]) {
|
||||
if (![mergedItems containsObject:mergeable]) {
|
||||
[mergeable setHidden:YES];
|
||||
[mergeable setEnabled:NO];
|
||||
}
|
||||
}
|
||||
|
||||
[mergedItems release];
|
||||
[NSApp setMainMenu:mb->nsMenu()];
|
||||
[loader qtTranslateApplicationMenu];
|
||||
}
|
||||
|
||||
QList<QCocoaMenuItem*> QCocoaMenuBar::merged() const
|
||||
{
|
||||
QList<QCocoaMenuItem*> r;
|
||||
foreach (QCocoaMenu* menu, m_menus)
|
||||
r.append(menu->merged());
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
bool QCocoaMenuBar::shouldDisable(QCocoaWindow *active) const
|
||||
{
|
||||
if (active && (active->window()->windowModality() == Qt::NonModal))
|
||||
return false;
|
||||
|
||||
if (m_window == active) {
|
||||
// modal window owns us, we should be enabled!
|
||||
return false;
|
||||
}
|
||||
|
||||
QWindowList topWindows(qApp->topLevelWindows());
|
||||
// When there is an application modal window on screen, the entries of
|
||||
// the menubar should be disabled. The exception in Qt is that if the
|
||||
// modal window is the only window on screen, then we enable the menu bar.
|
||||
foreach (QWindow *w, topWindows) {
|
||||
if (w->isVisible() && w->windowModality() == Qt::ApplicationModal) {
|
||||
// check for other visible windows
|
||||
foreach (QWindow *other, topWindows) {
|
||||
if ((w != other) && (other->isVisible())) {
|
||||
// INVARIANT: we found another visible window
|
||||
// on screen other than our modalWidget. We therefore
|
||||
// disable the menu bar to follow normal modality logic:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// INVARIANT: We have only one window on screen that happends
|
||||
// to be application modal. We choose to enable the menu bar
|
||||
// in that case to e.g. enable the quit menu item.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QPlatformMenu *QCocoaMenuBar::menuForTag(quintptr tag) const
|
||||
{
|
||||
foreach (QCocoaMenu *menu, m_menus) {
|
||||
if (menu->tag() == tag)
|
||||
return menu;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
117
src/plugins/platforms/cocoa/qcocoamenuitem.h
Normal file
117
src/plugins/platforms/cocoa/qcocoamenuitem.h
Normal file
@ -0,0 +1,117 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QCOCOAMENUITEM_H
|
||||
#define QCOCOAMENUITEM_H
|
||||
|
||||
#include <qpa/qplatformmenu.h>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
//#define QT_COCOA_ENABLE_MENU_DEBUG
|
||||
|
||||
@class NSMenuItem;
|
||||
@class NSMenu;
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QCocoaMenu;
|
||||
|
||||
class QCocoaMenuItem : public QPlatformMenuItem
|
||||
{
|
||||
public:
|
||||
QCocoaMenuItem();
|
||||
virtual ~QCocoaMenuItem();
|
||||
|
||||
inline virtual void setTag(quintptr tag)
|
||||
{ m_tag = tag; }
|
||||
inline virtual quintptr tag() const
|
||||
{ return m_tag; }
|
||||
|
||||
void setText(const QString &text);
|
||||
void setIcon(const QImage &icon);
|
||||
void setMenu(QPlatformMenu *menu);
|
||||
void setVisible(bool isVisible);
|
||||
void setIsSeparator(bool isSeparator);
|
||||
void setFont(const QFont &font);
|
||||
void setRole(MenuRole role);
|
||||
void setShortcut(const QKeySequence& shortcut);
|
||||
void setChecked(bool isChecked);
|
||||
void setEnabled(bool isEnabled);
|
||||
|
||||
inline QString text() const { return m_text; }
|
||||
inline NSMenuItem * nsItem() { return m_native; }
|
||||
NSMenuItem *sync();
|
||||
|
||||
void syncMerged();
|
||||
void syncModalState(bool modal);
|
||||
|
||||
inline bool isMerged() const { return m_merged; }
|
||||
inline bool isEnabled() const { return m_enabled; }
|
||||
inline bool isSeparator() const { return m_isSeparator; }
|
||||
|
||||
QCocoaMenu *menu() const { return m_menu; }
|
||||
private:
|
||||
QString mergeText();
|
||||
QKeySequence mergeAccel();
|
||||
|
||||
NSMenuItem *m_native;
|
||||
QString m_text;
|
||||
QImage m_icon;
|
||||
QCocoaMenu *m_menu;
|
||||
bool m_isVisible;
|
||||
bool m_enabled;
|
||||
bool m_isSeparator;
|
||||
QFont m_font;
|
||||
MenuRole m_role;
|
||||
QKeySequence m_shortcut;
|
||||
bool m_checked;
|
||||
bool m_merged;
|
||||
quintptr m_tag;
|
||||
};
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
358
src/plugins/platforms/cocoa/qcocoamenuitem.mm
Normal file
358
src/plugins/platforms/cocoa/qcocoamenuitem.mm
Normal file
@ -0,0 +1,358 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qcocoamenuitem.h"
|
||||
|
||||
#include "qcocoamenu.h"
|
||||
#include "qcocoahelpers.h"
|
||||
#include "qcocoaautoreleasepool.h"
|
||||
#include "qt_mac_p.h"
|
||||
#include "qcocoaapplication.h" // for custom application category
|
||||
#include "qcocoamenuloader.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
|
||||
{
|
||||
return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
|
||||
}
|
||||
|
||||
|
||||
static quint32 constructModifierMask(quint32 accel_key)
|
||||
{
|
||||
quint32 ret = 0;
|
||||
const bool dontSwap = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta);
|
||||
if ((accel_key & Qt::CTRL) == Qt::CTRL)
|
||||
ret |= (dontSwap ? NSControlKeyMask : NSCommandKeyMask);
|
||||
if ((accel_key & Qt::META) == Qt::META)
|
||||
ret |= (dontSwap ? NSCommandKeyMask : NSControlKeyMask);
|
||||
if ((accel_key & Qt::ALT) == Qt::ALT)
|
||||
ret |= NSAlternateKeyMask;
|
||||
if ((accel_key & Qt::SHIFT) == Qt::SHIFT)
|
||||
ret |= NSShiftKeyMask;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return an autoreleased string given a QKeySequence (currently only looks at the first one).
|
||||
NSString *keySequenceToKeyEqivalent(const QKeySequence &accel)
|
||||
{
|
||||
quint32 accel_key = (accel[0] & ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL));
|
||||
QChar cocoa_key = qt_mac_qtKey2CocoaKey(Qt::Key(accel_key));
|
||||
if (cocoa_key.isNull())
|
||||
cocoa_key = QChar(accel_key).toLower().unicode();
|
||||
return [NSString stringWithCharacters:&cocoa_key.unicode() length:1];
|
||||
}
|
||||
|
||||
// return the cocoa modifier mask for the QKeySequence (currently only looks at the first one).
|
||||
NSUInteger keySequenceModifierMask(const QKeySequence &accel)
|
||||
{
|
||||
return constructModifierMask(accel[0]);
|
||||
}
|
||||
|
||||
QCocoaMenuItem::QCocoaMenuItem() :
|
||||
m_native(NULL),
|
||||
m_menu(NULL),
|
||||
m_isVisible(true),
|
||||
m_enabled(true),
|
||||
m_isSeparator(false),
|
||||
m_role(NoRole),
|
||||
m_checked(false),
|
||||
m_merged(false),
|
||||
m_tag(0)
|
||||
{
|
||||
}
|
||||
|
||||
QCocoaMenuItem::~QCocoaMenuItem()
|
||||
{
|
||||
if (m_merged) {
|
||||
[m_native setHidden:YES];
|
||||
}
|
||||
|
||||
[m_native release];
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::setText(const QString &text)
|
||||
{
|
||||
m_text = qt_mac_removeAmpersandEscapes(text);
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::setIcon(const QImage &icon)
|
||||
{
|
||||
m_icon = icon;
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::setMenu(QPlatformMenu *menu)
|
||||
{
|
||||
if (menu == m_menu)
|
||||
return;
|
||||
|
||||
QCocoaAutoReleasePool pool;
|
||||
m_menu = static_cast<QCocoaMenu *>(menu);
|
||||
if (m_menu) {
|
||||
m_menu->setParentItem(this);
|
||||
} else {
|
||||
// we previously had a menu, but no longer
|
||||
// clear out our item so the nexy sync() call builds a new one
|
||||
[m_native release];
|
||||
m_native = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::setVisible(bool isVisible)
|
||||
{
|
||||
m_isVisible = isVisible;
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::setIsSeparator(bool isSeparator)
|
||||
{
|
||||
m_isSeparator = isSeparator;
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::setFont(const QFont &font)
|
||||
{
|
||||
m_font = font;
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::setRole(MenuRole role)
|
||||
{
|
||||
m_role = role;
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::setShortcut(const QKeySequence& shortcut)
|
||||
{
|
||||
m_shortcut = shortcut;
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::setChecked(bool isChecked)
|
||||
{
|
||||
m_checked = isChecked;
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::setEnabled(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
}
|
||||
|
||||
NSMenuItem *QCocoaMenuItem::sync()
|
||||
{
|
||||
if (m_isSeparator != [m_native isSeparatorItem]) {
|
||||
[m_native release];
|
||||
if (m_isSeparator) {
|
||||
m_native = [[NSMenuItem separatorItem] retain];
|
||||
[m_native setTag:reinterpret_cast<NSInteger>(this)];
|
||||
} else
|
||||
m_native = nil;
|
||||
}
|
||||
|
||||
if (m_menu) {
|
||||
if (m_native != m_menu->nsMenuItem()) {
|
||||
[m_native release];
|
||||
m_native = [m_menu->nsMenuItem() retain];
|
||||
[m_native setTag:reinterpret_cast<NSInteger>(this)];
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_role != NoRole) || m_merged) {
|
||||
NSMenuItem *mergeItem = nil;
|
||||
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
|
||||
switch (m_role) {
|
||||
case ApplicationSpecificRole:
|
||||
mergeItem = [loader appSpecificMenuItem];
|
||||
break;
|
||||
case AboutRole:
|
||||
mergeItem = [loader aboutMenuItem];
|
||||
break;
|
||||
case AboutQtRole:
|
||||
mergeItem = [loader aboutQtMenuItem];
|
||||
break;
|
||||
case QuitRole:
|
||||
mergeItem = [loader quitMenuItem];
|
||||
break;
|
||||
case PreferencesRole:
|
||||
mergeItem = [loader preferencesMenuItem];
|
||||
break;
|
||||
case TextHeuristicRole: {
|
||||
QString aboutString = tr("About").toLower();
|
||||
|
||||
if (m_text.startsWith(aboutString) || m_text.endsWith(aboutString)) {
|
||||
if (m_text.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1)
|
||||
mergeItem = [loader aboutMenuItem];
|
||||
else
|
||||
mergeItem = [loader aboutQtMenuItem];
|
||||
|
||||
m_merged = true;
|
||||
} else if (m_text.startsWith(tr("Config").toLower())
|
||||
|| m_text.startsWith(tr("Preference").toLower())
|
||||
|| m_text.startsWith(tr("Options").toLower())
|
||||
|| m_text.startsWith(tr("Setting").toLower())
|
||||
|| m_text.startsWith(tr("Setup").toLower())) {
|
||||
mergeItem = [loader preferencesMenuItem];
|
||||
} else if (m_text.startsWith(tr("Quit").toLower())
|
||||
|| m_text.startsWith(tr("Exit").toLower())) {
|
||||
mergeItem = [loader quitMenuItem];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
qWarning() << Q_FUNC_INFO << "unsupported role" << (int) m_role;
|
||||
}
|
||||
|
||||
if (mergeItem) {
|
||||
m_merged = true;
|
||||
[m_native release];
|
||||
m_native = mergeItem;
|
||||
[m_native retain]; // balance out release!
|
||||
[m_native setTag:reinterpret_cast<NSInteger>(this)];
|
||||
} else if (m_merged) {
|
||||
// was previously merged, but no longer
|
||||
[m_native release];
|
||||
m_native = nil; // create item below
|
||||
m_merged = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_native) {
|
||||
m_native = [[NSMenuItem alloc] initWithTitle:QCFString::toNSString(m_text)
|
||||
action:nil
|
||||
keyEquivalent:@""];
|
||||
[m_native retain];
|
||||
[m_native setTag:reinterpret_cast<NSInteger>(this)];
|
||||
}
|
||||
|
||||
// [m_native setHidden:YES];
|
||||
// [m_native setHidden:NO];
|
||||
[m_native setHidden: !m_isVisible];
|
||||
|
||||
QString text = m_text;
|
||||
QKeySequence accel = m_shortcut;
|
||||
|
||||
{
|
||||
int st = text.lastIndexOf(QLatin1Char('\t'));
|
||||
if (st != -1) {
|
||||
accel = QKeySequence(text.right(text.length()-(st+1)));
|
||||
text.remove(st, text.length()-st);
|
||||
}
|
||||
}
|
||||
|
||||
text = mergeText();
|
||||
accel = mergeAccel();
|
||||
|
||||
// Show multiple key sequences as part of the menu text.
|
||||
if (accel.count() > 1)
|
||||
text += QLatin1String(" (") + accel.toString(QKeySequence::NativeText) + QLatin1String(")");
|
||||
|
||||
QString finalString = qt_mac_removeMnemonics(text);
|
||||
// Cocoa Font and title
|
||||
if (m_font.resolve()) {
|
||||
NSFont *customMenuFont = [NSFont fontWithName:QCFString::toNSString(m_font.family())
|
||||
size:m_font.pointSize()];
|
||||
NSArray *keys = [NSArray arrayWithObjects:NSFontAttributeName, nil];
|
||||
NSArray *objects = [NSArray arrayWithObjects:customMenuFont, nil];
|
||||
NSDictionary *attributes = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
|
||||
NSAttributedString *str = [[[NSAttributedString alloc] initWithString:QCFString::toNSString(finalString)
|
||||
attributes:attributes] autorelease];
|
||||
[m_native setAttributedTitle: str];
|
||||
} else {
|
||||
[m_native setTitle: QCFString::toNSString(finalString)];
|
||||
}
|
||||
|
||||
if (accel.count() == 1) {
|
||||
[m_native setKeyEquivalent:keySequenceToKeyEqivalent(accel)];
|
||||
[m_native setKeyEquivalentModifierMask:keySequenceModifierMask(accel)];
|
||||
} else {
|
||||
[m_native setKeyEquivalent:@""];
|
||||
[m_native setKeyEquivalentModifierMask:NSCommandKeyMask];
|
||||
}
|
||||
|
||||
if (!m_icon.isNull()) {
|
||||
NSImage *img = qt_mac_cgimage_to_nsimage(qt_mac_image_to_cgimage(m_icon));
|
||||
[m_native setImage: img];
|
||||
}
|
||||
|
||||
[m_native setState:m_checked ? NSOnState : NSOffState];
|
||||
return m_native;
|
||||
}
|
||||
|
||||
QString QCocoaMenuItem::mergeText()
|
||||
{
|
||||
extern QString qt_mac_applicationmenu_string(int type);
|
||||
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
|
||||
if (m_native == [loader aboutMenuItem]) {
|
||||
return qt_mac_applicationmenu_string(6).arg(qt_mac_applicationName());
|
||||
} else if (m_native== [loader aboutQtMenuItem]) {
|
||||
if (m_text == QString("About Qt"))
|
||||
return tr("About Qt");
|
||||
else
|
||||
return m_text;
|
||||
} else if (m_native == [loader preferencesMenuItem]) {
|
||||
return qt_mac_applicationmenu_string(4);
|
||||
} else if (m_native == [loader quitMenuItem]) {
|
||||
return qt_mac_applicationmenu_string(5).arg(qt_mac_applicationName());
|
||||
}
|
||||
return m_text;
|
||||
}
|
||||
|
||||
QKeySequence QCocoaMenuItem::mergeAccel()
|
||||
{
|
||||
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
|
||||
if (m_native == [loader preferencesMenuItem])
|
||||
return QKeySequence(QKeySequence::Preferences);
|
||||
else if (m_native == [loader quitMenuItem])
|
||||
return QKeySequence(QKeySequence::Quit);
|
||||
|
||||
return m_shortcut;
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::syncMerged()
|
||||
{
|
||||
Q_ASSERT(m_merged);
|
||||
[m_native setTag:reinterpret_cast<NSInteger>(this)];
|
||||
[m_native setHidden: !m_isVisible];
|
||||
}
|
||||
|
||||
void QCocoaMenuItem::syncModalState(bool modal)
|
||||
{
|
||||
if (modal)
|
||||
[m_native setEnabled:NO];
|
||||
else
|
||||
[m_native setEnabled:m_enabled];
|
||||
}
|
@ -85,10 +85,11 @@
|
||||
- (IBAction)hideOtherApplications:(id)sender;
|
||||
- (IBAction)unhideAllApplications:(id)sender;
|
||||
- (IBAction)hide:(id)sender;
|
||||
- (IBAction)qtDispatcherToQAction:(id)sender;
|
||||
- (void)qtUpdateMenubar;
|
||||
- (IBAction)qtDispatcherToQPAMenuItem:(id)sender;
|
||||
- (void)orderFrontCharacterPalette:(id)sender;
|
||||
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem;
|
||||
- (void)qtTranslateApplicationMenu;
|
||||
- (NSArray *)mergeable;
|
||||
@end
|
||||
|
||||
void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader);
|
||||
|
@ -42,6 +42,8 @@
|
||||
#include "qcocoamenuloader.h"
|
||||
|
||||
#include "qcocoahelpers.h"
|
||||
#include "qcocoamenubar.h"
|
||||
#include "qcocoamenuitem.h"
|
||||
|
||||
#include <QtCore/private/qcore_mac_p.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
@ -52,14 +54,33 @@
|
||||
QT_FORWARD_DECLARE_CLASS(QCFString)
|
||||
QT_FORWARD_DECLARE_CLASS(QString)
|
||||
|
||||
#ifndef QT_NO_TRANSLATION
|
||||
QT_BEGIN_NAMESPACE
|
||||
extern QString qt_mac_applicationmenu_string(int type);
|
||||
QT_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
#ifndef QT_NO_TRANSLATION
|
||||
static const char *application_menu_strings[] = {
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Services"),
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Hide %1"),
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Hide Others"),
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Show All"),
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Preferences..."),
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Quit %1"),
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","About %1")
|
||||
};
|
||||
|
||||
QString qt_mac_applicationmenu_string(int type)
|
||||
{
|
||||
QString menuString = QString::fromLatin1(application_menu_strings[type]);
|
||||
QString translated = qApp->translate("QMenuBar", application_menu_strings[type]);
|
||||
if (translated != menuString) {
|
||||
return translated;
|
||||
} else {
|
||||
return qApp->translate("MAC_APPLICATION_MENU",
|
||||
application_menu_strings[type]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Loads and instantiates the main app menu from the menu nib file(s).
|
||||
|
||||
@ -127,6 +148,11 @@ void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
|
||||
// They should get synced back in.
|
||||
[preferencesItem setEnabled:NO];
|
||||
[preferencesItem setHidden:YES];
|
||||
|
||||
// should set this in the NIB
|
||||
[preferencesItem setTarget: self];
|
||||
[preferencesItem setAction: @selector(qtDispatcherToQPAMenuItem:)];
|
||||
|
||||
[aboutItem setEnabled:NO];
|
||||
[aboutItem setHidden:YES];
|
||||
}
|
||||
@ -269,19 +295,10 @@ void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
|
||||
[NSApp hide:sender];
|
||||
}
|
||||
|
||||
- (void)qtUpdateMenubar
|
||||
{
|
||||
#if 0
|
||||
QCocoaMenuBar::macUpdateMenuBarImmediatly();
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)qtTranslateApplicationMenu
|
||||
{
|
||||
|
||||
qDebug() << "qtTranslateApplicationMenu";
|
||||
#if 0
|
||||
//#ifndef QT_NO_TRANSLATION
|
||||
#ifndef QT_NO_TRANSLATION
|
||||
[servicesItem setTitle: QCFString::toNSString(qt_mac_applicationmenu_string(0))];
|
||||
[hideItem setTitle: QCFString::toNSString(qt_mac_applicationmenu_string(1).arg(qt_mac_applicationName()))];
|
||||
[hideAllOthersItem setTitle: QCFString::toNSString(qt_mac_applicationmenu_string(2))];
|
||||
@ -292,21 +309,21 @@ void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
|
||||
#endif
|
||||
}
|
||||
|
||||
- (IBAction)qtDispatcherToQAction:(id)sender
|
||||
- (IBAction)qtDispatcherToQPAMenuItem:(id)sender
|
||||
{
|
||||
#if 0
|
||||
//
|
||||
//QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
|
||||
NSMenuItem *item = static_cast<NSMenuItem *>(sender);
|
||||
if (QAction *action = reinterpret_cast<QAction *>([item tag])) {
|
||||
action->trigger();
|
||||
} else if (item == quitItem) {
|
||||
if (item == quitItem) {
|
||||
// We got here because someone was once the quitItem, but it has been
|
||||
// abandoned (e.g., the menubar was deleted). In the meantime, just do
|
||||
// normal QApplication::quit().
|
||||
qApp->quit();
|
||||
return;
|
||||
}
|
||||
|
||||
if ([item tag]) {
|
||||
QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([item tag]);
|
||||
cocoaItem->activated();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)orderFrontCharacterPalette:(id)sender
|
||||
@ -320,9 +337,18 @@ void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
|
||||
|| [menuItem action] == @selector(hideOtherApplications:)
|
||||
|| [menuItem action] == @selector(unhideAllApplications:)) {
|
||||
return [NSApp validateMenuItem:menuItem];
|
||||
} else if ([menuItem tag]) {
|
||||
QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([menuItem tag]);
|
||||
return cocoaItem->isEnabled();
|
||||
} else {
|
||||
return [menuItem isEnabled];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray*) mergeable
|
||||
{
|
||||
// don't include the quitItem here, since we want it always visible and enabled regardless
|
||||
return [NSArray arrayWithObjects:preferencesItem, aboutItem, aboutQtItem, lastAppSpecificItem, nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -53,8 +53,13 @@ class QCocoaNativeInterface : public QPlatformNativeInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QCocoaNativeInterface();
|
||||
|
||||
void *nativeResourceForWindow(const QByteArray &resourceString, QWindow *window);
|
||||
|
||||
public Q_SLOTS:
|
||||
void onAppFocusWindowChanged(QWindow *window);
|
||||
|
||||
private:
|
||||
/*
|
||||
"Virtual" function to create the platform printer support
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "qcocoaglcontext.h"
|
||||
#include "qcocoawindow.h"
|
||||
#include "qcocoaprintersupport.h"
|
||||
#include "qcocoamenubar.h"
|
||||
|
||||
#include <qbytearray.h>
|
||||
#include <qwindow.h>
|
||||
@ -50,12 +51,17 @@
|
||||
#include "qsurfaceformat.h"
|
||||
#include <qpa/qplatformopenglcontext.h>
|
||||
#include "qopenglcontext.h"
|
||||
#include "qguiapplication.h"
|
||||
#include <qdebug.h>
|
||||
|
||||
#include "qprintengine_mac_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QCocoaNativeInterface::QCocoaNativeInterface()
|
||||
{
|
||||
}
|
||||
|
||||
void *QCocoaNativeInterface::nativeResourceForWindow(const QByteArray &resourceString, QWindow *window)
|
||||
{
|
||||
if (!window->handle()) {
|
||||
@ -84,4 +90,9 @@ void *QCocoaNativeInterface::NSPrintInfoForPrintEngine(QPrintEngine *printEngine
|
||||
return macPrintEngine->d_func()->printInfo;
|
||||
}
|
||||
|
||||
void QCocoaNativeInterface::onAppFocusWindowChanged(QWindow *window)
|
||||
{
|
||||
QCocoaMenuBar::updateMenuBarImmediately();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -54,6 +54,10 @@ public:
|
||||
QCocoaTheme();
|
||||
~QCocoaTheme();
|
||||
|
||||
virtual QPlatformMenuItem* createPlatformMenuItem() const;
|
||||
virtual QPlatformMenu* createPlatformMenu() const;
|
||||
virtual QPlatformMenuBar* createPlatformMenuBar() const;
|
||||
|
||||
bool usePlatformNativeDialog(DialogType dialogType) const;
|
||||
QPlatformDialogHelper *createPlatformDialogHelper(DialogType dialogType) const;
|
||||
|
||||
|
@ -41,12 +41,19 @@
|
||||
|
||||
#include "qcocoatheme.h"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QtCore/QVariant>
|
||||
|
||||
#include "qcocoacolordialoghelper.h"
|
||||
#include "qcocoafiledialoghelper.h"
|
||||
#include "qcocoafontdialoghelper.h"
|
||||
#include "qcocoasystemsettings.h"
|
||||
#include "qcocoamenuitem.h"
|
||||
#include "qcocoamenu.h"
|
||||
#include "qcocoamenubar.h"
|
||||
|
||||
#include <QtGui/private/qguiapplication_p.h>
|
||||
#include <QtGui/QPlatformIntegration>
|
||||
#include <QtGui/QPlatformNativeInterface>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@ -133,4 +140,27 @@ QVariant QCocoaTheme::themeHint(ThemeHint hint) const
|
||||
return QPlatformTheme::themeHint(hint);
|
||||
}
|
||||
|
||||
QPlatformMenuItem *QCocoaTheme::createPlatformMenuItem() const
|
||||
{
|
||||
return new QCocoaMenuItem();
|
||||
}
|
||||
|
||||
QPlatformMenu *QCocoaTheme::createPlatformMenu() const
|
||||
{
|
||||
return new QCocoaMenu();
|
||||
}
|
||||
|
||||
QPlatformMenuBar *QCocoaTheme::createPlatformMenuBar() const
|
||||
{
|
||||
static bool haveMenubar = false;
|
||||
if (!haveMenubar) {
|
||||
haveMenubar = true;
|
||||
QObject::connect(qGuiApp, SIGNAL(focusWindowChanged(QWindow*)),
|
||||
QGuiApplicationPrivate::platformIntegration()->nativeInterface(),
|
||||
SLOT(onAppFocusWindowChanged(QWindow*)));
|
||||
}
|
||||
|
||||
return new QCocoaMenuBar();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -85,6 +85,8 @@ QT_BEGIN_NAMESPACE
|
||||
// See the qt_on_cocoa manual tests for a working example, located
|
||||
// in tests/manual/cocoa at the time of writing.
|
||||
|
||||
class QCocoaMenuBar;
|
||||
|
||||
class QCocoaWindow : public QPlatformWindow
|
||||
{
|
||||
public:
|
||||
@ -120,6 +122,8 @@ public:
|
||||
|
||||
bool setWindowModified(bool modified) Q_DECL_OVERRIDE;
|
||||
|
||||
void setMenubar(QCocoaMenuBar *mb);
|
||||
QCocoaMenuBar *menubar() const;
|
||||
protected:
|
||||
// NSWindow handling. The QCocoaWindow/QNSView can either be displayed
|
||||
// in an existing NSWindow or in one created by Qt.
|
||||
@ -145,6 +149,7 @@ public: // for QNSView
|
||||
|
||||
bool m_inConstructor;
|
||||
QCocoaGLContext *m_glContext;
|
||||
QCocoaMenuBar *m_menubar;
|
||||
|
||||
bool m_hasModalSession;
|
||||
};
|
||||
|
@ -100,8 +100,12 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw)
|
||||
, m_synchedWindowState(Qt::WindowActive)
|
||||
, m_inConstructor(true)
|
||||
, m_glContext(0)
|
||||
, m_menubar(0)
|
||||
, m_hasModalSession(false)
|
||||
{
|
||||
#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
|
||||
qDebug() << "QCocoaWindow::QCocoaWindow" << this;
|
||||
#endif
|
||||
QCocoaAutoReleasePool pool;
|
||||
|
||||
m_contentView = [[QNSView alloc] initWithQWindow:tlw platformWindow:this];
|
||||
@ -577,3 +581,13 @@ bool QCocoaWindow::setWindowModified(bool modified)
|
||||
[m_nsWindow setDocumentEdited:(modified?YES:NO)];
|
||||
return true;
|
||||
}
|
||||
|
||||
void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
|
||||
{
|
||||
m_menubar = mb;
|
||||
}
|
||||
|
||||
QCocoaMenuBar *QCocoaWindow::menubar() const
|
||||
{
|
||||
return m_menubar;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
<string>id</string>
|
||||
<key>orderFrontStandardAboutPanel</key>
|
||||
<string>id</string>
|
||||
<key>qtDispatcherToQAction</key>
|
||||
<key>qtDispatcherToQPAMenuItem</key>
|
||||
<string>id</string>
|
||||
<key>terminate</key>
|
||||
<string>id</string>
|
||||
|
201
tests/manual/cocoa/menus/main.cpp
Normal file
201
tests/manual/cocoa/menus/main.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 KDAB
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtGui>
|
||||
#include <QtWidgets>
|
||||
|
||||
|
||||
class Responder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Responder(QObject *pr) :
|
||||
QObject(pr)
|
||||
{
|
||||
}
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
void toggleChecked(bool b)
|
||||
{
|
||||
QAction *a = qobject_cast<QAction *>(sender());
|
||||
}
|
||||
|
||||
void showModalDialog()
|
||||
{
|
||||
QMessageBox::information(NULL, "Something", "Something happened. Modally.");
|
||||
}
|
||||
|
||||
void doPreferences()
|
||||
{
|
||||
qDebug() << "show preferences";
|
||||
}
|
||||
|
||||
void aboutToShowSubmenu()
|
||||
{
|
||||
QMenu* m = (QMenu*) sender();
|
||||
qDebug() << "will show" << m;
|
||||
|
||||
m->clear();
|
||||
|
||||
for (int i=0; i<10; ++i) {
|
||||
m->addAction(QString("Recent File %1").arg(i + 1));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void createWindow1()
|
||||
{
|
||||
|
||||
QMainWindow *window = new QMainWindow;
|
||||
QMenu *menu = new QMenu("TestMenu", window);
|
||||
|
||||
window->menuBar()->addMenu(menu);
|
||||
|
||||
Responder *r = new Responder(window);
|
||||
|
||||
QAction *a = menu->addAction("TestMenuItem1");
|
||||
a->setShortcut( Qt::Key_A | Qt::SHIFT | Qt::CTRL );
|
||||
QObject::connect(a, SIGNAL(triggered()),
|
||||
r, SLOT(showModalDialog()));
|
||||
|
||||
|
||||
menu->addAction("T&estMenuItem2");
|
||||
a = menu->addAction("Preferences");
|
||||
a->setMenuRole(QAction::PreferencesRole);
|
||||
QObject::connect(a, SIGNAL(triggered()),
|
||||
r, SLOT(doPreferences()));
|
||||
|
||||
a = menu->addAction("TestMenuItem4");
|
||||
a->setShortcut( Qt::Key_W | Qt::CTRL);
|
||||
|
||||
QMenu *menu2 = new QMenu("SecondMenu", window);
|
||||
window->menuBar()->addMenu(menu2);
|
||||
|
||||
menu2->addAction("Yellow");
|
||||
a = menu2->addAction("Mau&ve");
|
||||
|
||||
QFont f;
|
||||
f.setPointSize(9);
|
||||
a->setFont(f);
|
||||
|
||||
menu2->addAction("Taupe");
|
||||
|
||||
QMenu *submenu1 = new QMenu("Submenu", window);
|
||||
submenu1->addAction("Sub Item 1");
|
||||
submenu1->addAction("Sub Item 2");
|
||||
submenu1->addAction("Sub Item 3");
|
||||
menu2->addMenu(submenu1);
|
||||
|
||||
QMenu *submenu2 = new QMenu("Deeper", window);
|
||||
submenu2->addAction("Sub Sub Item 1");
|
||||
submenu2->addAction("Sub Sub Item 2");
|
||||
submenu2->addAction("Sub Sub Item 3");
|
||||
submenu1->addMenu(submenu2);
|
||||
|
||||
QMenu *menu3 = new QMenu("A Third Menu", window);
|
||||
|
||||
menu3->addAction("Eins");
|
||||
|
||||
QMenu *submenu3 = new QMenu("Dynamic", window);
|
||||
QObject::connect(submenu3, SIGNAL(aboutToShow()), r, SLOT(aboutToShowSubmenu()));
|
||||
menu3->addMenu(submenu3);
|
||||
|
||||
a = menu3->addAction("Zwei");
|
||||
a->setShortcut( Qt::Key_3 | Qt::ALT);
|
||||
a = menu3->addAction("About Drei...");
|
||||
a->setMenuRole(QAction::AboutRole);
|
||||
|
||||
window->menuBar()->addMenu(menu3);
|
||||
|
||||
QAction *checkableAction = new QAction("Thing Enabled", window);
|
||||
checkableAction->setCheckable(true);
|
||||
checkableAction->setChecked(true);
|
||||
QObject::connect(checkableAction, SIGNAL(triggered(bool)),
|
||||
r, SLOT(toggleChecked(bool)));
|
||||
|
||||
menu2->addAction(checkableAction);
|
||||
|
||||
window->show();
|
||||
|
||||
}
|
||||
|
||||
void createWindow2()
|
||||
{
|
||||
QMainWindow *window = new QMainWindow;
|
||||
QMenu *menu = new QMenu("Nuts", window);
|
||||
|
||||
window->menuBar()->addMenu(menu);
|
||||
|
||||
menu->addAction("Peanuts");
|
||||
menu->addAction("Walnuts");
|
||||
|
||||
QMenu *menu2 = new QMenu("Colours", window);
|
||||
window->menuBar()->addMenu(menu2);
|
||||
|
||||
menu2->addAction("Pink");
|
||||
menu2->addAction("Yellow");
|
||||
menu2->addAction("Grape");
|
||||
|
||||
QMenu *menu3 = new QMenu("Edit", window);
|
||||
menu3->addAction("Cut");
|
||||
menu3->addAction("Copy boring way");
|
||||
menu3->addAction("Copy awesomely");
|
||||
menu3->addAction("Paste");
|
||||
|
||||
window->menuBar()->addMenu(menu3);
|
||||
window->show();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
QApplication app(argc, argv);
|
||||
|
||||
app.setApplicationName("Banana");
|
||||
|
||||
createWindow1();
|
||||
createWindow2();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
#include "main.moc"
|
5
tests/manual/cocoa/menus/menus.pro
Normal file
5
tests/manual/cocoa/menus/menus.pro
Normal file
@ -0,0 +1,5 @@
|
||||
TEMPLATE = app
|
||||
|
||||
SOURCES += main.cpp
|
||||
QT += gui widgets
|
||||
CONFIG -=app_bundle
|
Loading…
Reference in New Issue
Block a user