[Interest] Qt 4.8 Mac/Cocoa, want mouseDown in menubar event
Jim Prouty
jim at wavemetrics.com
Thu Nov 22 01:52:57 CET 2012
It would simplify my life immensely if my Qt 4.8 Cocoa application received a signal or event or anything that allowed me to intervene between the first mouse click in the menu bar and when the first menu is shown.
In a straight Cocoa application, I can register a routine to receive a NSMenuDidBeginTrackingNotification notification:
[[NSNotificationCenter defaultCenter]
addObserver:self // the NSObject containing the called routine
selector:@selector(begunTracking:) // the routine called with notification
name:NSMenuDidBeginTrackingNotification
object:[NSApp mainMenu]]; // the object which emits the NSMenuDidBeginTrackingNotification
which calls -calledWhenTrackingHasBegun(NSNotification *)notification on the menubar mouse down.
Note that the notification comes from a specific object: the mainMenu object returned by the cocoa application NSApp object.
This works fine in a trivial Cocoa application (first example code).
However, in a simple Qt application (such as the second example code), the notification is never received.
I'm wondering if [NSApp mainMenu] isn't returning the native menu bar that Qt is actually using?
I wonder this because the log entries generated in MainMenuObserver:init() indicate a completely different menu than the one my application built:
11/21/12 4:06:38 PM MacMenuBarMouseDown[18132] mainMenu: <NSMenu: 0x1167104a0>
Title: qt_menu
Supermenu: 0x0 (None), autoenable: YES
Items: (
"<NSMenuItem: 0x116710560 NewApplication, submenu: 0x11670d260 (NewApplication)>"
)
11/21/12 4:06:46 PM MacMenuBarMouseDown[18132] num items: 1
11/21/12 4:06:51 PM MacMenuBarMouseDown[18132] array: (
"<NSMenuItem: 0x116710560 NewApplication, submenu: 0x11670d260 (NewApplication)>"
)
Q1) How can I get the actual menubar object on which to install the observer?
Surely it exists somewhere!
(I see a few calls to [NSApp setMainMenu:menu]; in Qt 4.8.3's qmenu_mac.mm source, but darn if I can figure out what's happening in there with QCocoaMenuLoader et al.)
Better would be a Qt-supported notify event or filter API that would report a mouse down in whatever Qt uses for a menu bar.
Q2) Is there an existing Qt 4.8+ feature that detects the start of mouse tracking in the menu bar on Mac (and Win and Linux)?
--Jim
------------- Objective C Example code which works in native app, but not in Qt ----------------------
void InstallMainMenuObserver()
{
static MainMenuObserver *myObserver= nil;
if( myObserver == nil )
myObserver = [[MainMenuObserver alloc] init];
}
@implementation MainMenuObserver
- (id) init
{
self = [super init];
if (!self)
return nil;
NSMenu *menu= [NSApp mainMenu];
NSLog(@"mainMenu: %@", menu);
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(begunTracking:)
name:NSMenuDidBeginTrackingNotification
object:menu];
return self;
}
- (void) dealloc
{
// If you don't remove yourself as an observer, the Notification Center
// will continue to try and send notification objects to the deallocated
// object.
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (void)begunTracking:(NSNotification *)notification
{
if ([[notification name] isEqualToString:NSMenuDidBeginTrackingNotification])
NSLog(@"Successfully received NSMenuDidBeginTrackingNotification!");
}
@end
------------- Qt Example code ----------------------
(This program generates a simple three-menu menubar: the first is the default application menu, the second is a "Test" menu, then a "Another" menu).
+++ main.cpp:
#include <QApplication>
#include <QMenu>
#include <QMenuBar>
#ifdef DOTRACKING
#include "CocoaUtils.h"
#endif
int main( int argc, char* argv[])
{
QApplication app(argc, argv);
// "QMenuBar on Mac OS X
// If you want all windows in a Mac application to share one menu bar,
// you must create a menu bar that does not have a parent."
QMenuBar *menuBar = new QMenuBar(0); // Create a parent-less menu bar this way
bool isNative= menuBar->isNativeMenuBar(); // returns true
QMenu * menu= new QMenu("Test");
QAction *action= new QAction(QLatin1String("First menu item"), menu);
menu->addAction( action );
action= new QAction(QLatin1String("Second menu item"), menu);
menu->addAction( action );
action= new QAction(QLatin1String("Third menu item"), menu);
menu->addAction( action );
menuBar->addMenu( menu ); // first added menu (there should already be an Application menu in the menu bar)
menu= new QMenu("Another");
action= new QAction(QLatin1String("Fourth menu item"), menu);
menu->addAction( action );
menuBar->addMenu( menu ); // second added menu (there should be 3 now)
#ifdef DOTRACKING
InstallMainMenuObserver(); // CocoaUtils.h/mm
#endif
app.exec();
}
+++ CocoaUtils.h:
#if defined __cplusplus || defined __OBJC__
extern "C" {
#endif // __cplusplus
void InstallMainMenuObserver();
#if defined __OBJC__
// The definition of "slots" in Qt5 conflicts with the definition
// in CAOpenGLLayer.h, which gets included when we include Cocoa/Cocoa.h
// below. So we undefine "slots" if necessary.
#if defined(slots)
#undef slots
#endif
#import <Cocoa/Cocoa.h>
#import <AppKit/NSMenu.h>
@interface MainMenuObserver : NSObject
{
@private
}
- (void)begunTracking:(NSNotification *)notification;
@end
#endif // __OBJC__
#if defined __cplusplus || defined __OBJC__
}
#endif // __cplusplus
+++ CocoaUtils.mmm:
#import <Cocoa/Cocoa.h>
#import <AppKit/NSMenu.h>
#import "CocoaUtils.h"
void InstallMainMenuObserver()
{
static MainMenuObserver *myObserver= nil;
if( myObserver == nil )
myObserver = [[MainMenuObserver alloc] init];
}
@implementation MainMenuObserver
- (id) init
{
self = [super init];
if (!self)
return nil;
NSMenu *menu= [NSApp mainMenu];
NSLog(@"mainMenu: %@", menu);
// debugging: see if the menubar has our expected items.
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSLog(@"num items: %d", [menu numberOfItems]); // should be 2: one for the application menu and one for the "Test" menu. This is 1, instead.
NSLog(@"array: %@", [menu itemArray]);
[pool release];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(begunTracking:)
name:NSMenuDidBeginTrackingNotification
object:menu];
return self;
}
- (void) dealloc
{
// If you don't remove yourself as an observer, the Notification Center
// will continue to try and send notification objects to the deallocated
// object.
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (void)begunTracking:(NSNotification *)notification
{
if ([[notification name] isEqualToString:NSMenuDidBeginTrackingNotification]) // <<<< A BREAKPOINT HERE IS NEVER HIT
NSLog(@"Successfully received NSMenuDidBeginTrackingNotification!"); // should be seen in the Console app.
}
@end
+++ MacMenuBarMouseDown.pro:
#########################################
# MacMenuBarMouseDown.pro
#########################################
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
macx:CONFIG += x86_64
TARGET = MacMenuBarMouseDown
TEMPLATE = app
SOURCES += main.cpp
mac {
HEADERS += CocoaUtils.h
OBJECTIVE_SOURCES += CocoaUtils.mm
DEFINES += DOTRACKING # identify code related to NSMenuDidBeginTrackingNotification
}
HEADERS +=
OTHER_FILES += MacMenuBarMouseDown.pro
#########################################
# SYSTEM LIBRARIES
#########################################
#to support native drawing on Macintosh
mac:LIBS += -framework ApplicationServices
mac:LIBS += -framework Cocoa
mac:LIBS += -framework Carbon
mac:LIBS += -framework CoreAudio
mac:LIBS += -framework AudioUnit
mac:LIBS += -framework QTKit
========================================================================
Jim "How does it work?" Prouty
Voice: (503) 620-3001, FAX: (503) 620-6754
Makers of IGOR Pro, scientific data analysis and graphing for Mac and PC
http://www.wavemetrics.com
More information about the Interest
mailing list