(SOLVED) SystemTrayIcon does not get updated via callBack function


#1

Hi everybody,
I am working on an MacOS JUCE app (MacOS 10.12.2) and I am having the following issue:

I am trying to update the system tray icon of my application inside a timerCallback function and the icon does get updated like it should be. If I then click on the tray icon, the correct icon is shown, although nothing happens inside the mouseDown function.

It is really easy to replicate this behaviour. Just replace the DemoTaskbarComponent in the MainWindow.cpp of the JUCE Demo app with this piece of code:

// Just add a simple icon to the Window system tray area or Mac menu bar..
class DemoTaskbarComponent  : public SystemTrayIconComponent,
                              private Timer
{
public:
    Image iconimage;
    
    DemoTaskbarComponent()
{
    setIconImage (ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize));
    setIconTooltip ("Juce Demo App!");
    
    //Load an icon to test.
    iconimage = Image(ImageFileFormat::loadFrom(File::getSpecialLocation(File::currentApplicationFile).getChildFile("Contents/Resources/iconimage.png")));
    
    startTimer(2000); //in 2 seconds set the new tray icon!
}

void mouseDown (const MouseEvent&) override
{
    // On OSX, there can be problems launching a menu when we're not the foreground
    // process, so just in case, we'll first make our process active, and then use a
    // timer to wait a moment before opening our menu, which gives the OS some time to
    // get its act together and bring our windows to the front.

//  Process::makeForegroundProcess();
//  startTimer (50);
}

// This is invoked when the menu is clicked or dismissed
static void menuInvocationCallback (int chosenItemID, DemoTaskbarComponent*)
{
if (chosenItemID == 1)
{
    JUCEApplication::getInstance()->systemRequestedQuit();
}
}

private:
void timerCallback() override
{
stopTimer();

    //set the icon! Icon does not change :(
    setIconImage(iconimage);
    
    /*
    PopupMenu m;
    m.addItem (1, "Quit the Juce demo");

    // It's always better to open menus asynchronously when possible.
    m.showMenuAsync (PopupMenu::Options(),
                     ModalCallbackFunction::forComponent (menuInvocationCallback, this));
    */
}
};

Can it be that only the main thread is able to change the icon? The same behaviour also occurs when the setIconImage is called inside an actionListenerCallback function. The same code works just fine in Windows by the way.

Any suggestions? Your help will be much appreciated!
Thanks


#2

I have found a fix:

In juce_mac_SystemTrayIcon.cpp you have to set the view of the statusItem again for the OS to update the icon, even if the application is running in the background.

This is how the updateIcon function should look like:

void updateIcon (const Image& newImage)
    {
        [statusIcon release];
        statusIcon = MouseCursorHelpers::createNSImage (newImage);
        setIconSize();
        SystemTrayViewClass::setImage (view, statusIcon);
         
//just added the following line:
        [statusItem setView: view];
    }

#3

I have also noticed that the showInfoBubble is not implemented for Mac in JUCE v4.3.0.
This works fine:

void SystemTrayIconComponent::showInfoBubble (const String& title, const String& content)
{
    NSUserNotification *notification = [[NSUserNotification alloc] init];
    notification.title =  juceStringToNS(title);
    notification.informativeText = juceStringToNS(content);
    [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
}

I am not sure if it leaks though…


#4

I’ve added your change to updateIcon to the develop branch. Thank you.

The OS X system tray code needs some work, I’ll add this to our backlog.