[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