Skip to content

Commit

Permalink
Merge #14123: gui: Add GUIUtil::bringToFront
Browse files Browse the repository at this point in the history
0a656f8 qt: All tray menu actions call showNormalIfMinimized (João Barbosa)
6fc21ac qt: Use GUIUtil::bringToFront where possible (João Barbosa)
5796671 qt: Add GUIUtil::bringToFront (João Barbosa)
6b1d297 Remove obj_c for macOS Dock icon menu (Hennadii Stepanov)
2464925 Use Qt signal for macOS Dock icon click event (Hennadii Stepanov)
53bb6be Remove obj_c for macOS Dock icon setting (Hennadii Stepanov)

Pull request description:

  The sequence `show -> raise -> activateWindow` is factored out to the new function `GUIUtil::bringToFront` where a macOS fix is added in order to fix #13829.

  Also included:
   - simplification of `BitcoinGUI::showNormalIfMinimized`
   - simplified some connections to `BitcoinGUI::showNormalIfMinimized`
   - added missing connections to `BitcoinGUI::showNormalIfMinimized`.

Tree-SHA512: a8e301aebc359aa353821e2af352ae356f44555724921b01da907e128653ef9dc55d8764a1bff72a579e5ff96df8a681f6804bfe83acba441da92fedff974a55
  • Loading branch information
laanwj committed Nov 12, 2018
2 parents ae32806 + 0a656f8 commit 4822325
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 159 deletions.
66 changes: 28 additions & 38 deletions src/qt/bitcoingui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,8 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
windowTitle += tr("Node");
}
windowTitle += " " + networkStyle->getTitleAddText();
#ifndef Q_OS_MAC
QApplication::setWindowIcon(networkStyle->getTrayAndWindowIcon());
setWindowIcon(networkStyle->getTrayAndWindowIcon());
#else
MacDockIconHandler::instance()->setIcon(networkStyle->getAppIcon());
#endif
setWindowTitle(windowTitle);

rpcConsole = new RPCConsole(node, _platformStyle, 0);
Expand Down Expand Up @@ -278,17 +274,17 @@ void BitcoinGUI::createActions()
#ifdef ENABLE_WALLET
// These showNormalIfMinimized are needed because Send Coins and Receive Coins
// can be triggered from the tray menu, and need to show the GUI to be useful.
connect(overviewAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
connect(overviewAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(overviewAction, &QAction::triggered, this, &BitcoinGUI::gotoOverviewPage);
connect(sendCoinsAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
connect(sendCoinsAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(sendCoinsAction, &QAction::triggered, [this]{ gotoSendCoinsPage(); });
connect(sendCoinsMenuAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
connect(sendCoinsMenuAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(sendCoinsMenuAction, &QAction::triggered, [this]{ gotoSendCoinsPage(); });
connect(receiveCoinsAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
connect(receiveCoinsAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(receiveCoinsAction, &QAction::triggered, this, &BitcoinGUI::gotoReceiveCoinsPage);
connect(receiveCoinsMenuAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
connect(receiveCoinsMenuAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(receiveCoinsMenuAction, &QAction::triggered, this, &BitcoinGUI::gotoReceiveCoinsPage);
connect(historyAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
connect(historyAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(historyAction, &QAction::triggered, this, &BitcoinGUI::gotoHistoryPage);
#endif // ENABLE_WALLET

Expand Down Expand Up @@ -355,7 +351,9 @@ void BitcoinGUI::createActions()
connect(encryptWalletAction, &QAction::triggered, walletFrame, &WalletFrame::encryptWallet);
connect(backupWalletAction, &QAction::triggered, walletFrame, &WalletFrame::backupWallet);
connect(changePassphraseAction, &QAction::triggered, walletFrame, &WalletFrame::changePassphrase);
connect(signMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(signMessageAction, &QAction::triggered, [this]{ gotoSignMessageTab(); });
connect(verifyMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(verifyMessageAction, &QAction::triggered, [this]{ gotoVerifyMessageTab(); });
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses);
Expand Down Expand Up @@ -604,7 +602,7 @@ void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle)
void BitcoinGUI::createTrayIconMenu()
{
#ifndef Q_OS_MAC
// return if trayIcon is unset (only on non-Mac OSes)
// return if trayIcon is unset (only on non-macOSes)
if (!trayIcon)
return;

Expand All @@ -613,15 +611,17 @@ void BitcoinGUI::createTrayIconMenu()

connect(trayIcon, &QSystemTrayIcon::activated, this, &BitcoinGUI::trayIconActivated);
#else
// Note: On Mac, the dock icon is used to provide the tray's functionality.
// Note: On macOS, the Dock icon is used to provide the tray's functionality.
MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
dockIconHandler->setMainWindow(static_cast<QMainWindow*>(this));
trayIconMenu = dockIconHandler->dockMenu();
connect(dockIconHandler, &MacDockIconHandler::dockIconClicked, this, &BitcoinGUI::macosDockIconActivated);

trayIconMenu = new QMenu(this);
trayIconMenu->setAsDockMenu();
#endif

// Configuration of the tray icon (or dock icon) icon menu
// Configuration of the tray icon (or Dock icon) menu
#ifndef Q_OS_MAC
// Note: On Mac, the dock icon's menu already has show / hide action.
// Note: On macOS, the Dock icon's menu already has Show / Hide action.
trayIconMenu->addAction(toggleHideAction);
trayIconMenu->addSeparator();
#endif
Expand All @@ -635,7 +635,7 @@ void BitcoinGUI::createTrayIconMenu()
trayIconMenu->addAction(openRPCConsoleAction);
}
trayIconMenu->addAction(optionsAction);
#ifndef Q_OS_MAC // This is built-in on Mac
#ifndef Q_OS_MAC // This is built-in on macOS
trayIconMenu->addSeparator();
trayIconMenu->addAction(quitAction);
#endif
Expand All @@ -650,6 +650,12 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
toggleHidden();
}
}
#else
void BitcoinGUI::macosDockIconActivated()
{
show();
activateWindow();
}
#endif

void BitcoinGUI::optionsClicked()
Expand All @@ -668,10 +674,7 @@ void BitcoinGUI::aboutClicked()

void BitcoinGUI::showDebugWindow()
{
rpcConsole->showNormal();
rpcConsole->show();
rpcConsole->raise();
rpcConsole->activateWindow();
GUIUtil::bringToFront(rpcConsole);
}

void BitcoinGUI::showDebugWindowActivateConsole()
Expand Down Expand Up @@ -1158,24 +1161,11 @@ void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
if(!clientModel)
return;

// activateWindow() (sometimes) helps with keyboard focus on Windows
if (isHidden())
{
show();
activateWindow();
}
else if (isMinimized())
{
showNormal();
activateWindow();
}
else if (GUIUtil::isObscured(this))
{
raise();
activateWindow();
}
else if(fToggleHidden)
if (!isHidden() && !isMinimized() && !GUIUtil::isObscured(this) && fToggleHidden) {
hide();
} else {
GUIUtil::bringToFront(this);
}
}

void BitcoinGUI::toggleHidden()
Expand Down
3 changes: 3 additions & 0 deletions src/qt/bitcoingui.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ public Q_SLOTS:
#ifndef Q_OS_MAC
/** Handle tray icon clicked */
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
#else
/** Handle macOS Dock icon clicked */
void macosDockIconActivated();
#endif

/** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */
Expand Down
34 changes: 29 additions & 5 deletions src/qt/guiutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@
#include <QFontDatabase>
#endif

#if defined(Q_OS_MAC)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"

#include <objc/objc-runtime.h>
#include <CoreServices/CoreServices.h>
#endif

namespace GUIUtil {

QString dateTimeStr(const QDateTime &date)
Expand Down Expand Up @@ -353,6 +361,27 @@ bool isObscured(QWidget *w)
&& checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
}

void bringToFront(QWidget* w)
{
#ifdef Q_OS_MAC
// Force application activation on macOS. With Qt 5.4 this is required when
// an action in the dock menu is triggered.
id app = objc_msgSend((id) objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
objc_msgSend(app, sel_registerName("activateIgnoringOtherApps:"), YES);
#endif

if (w) {
// activateWindow() (sometimes) helps with keyboard focus on Windows
if (w->isMinimized()) {
w->showNormal();
} else {
w->show();
}
w->activateWindow();
w->raise();
}
}

void openDebugLogfile()
{
fs::path pathDebug = GetDataDir() / "debug.log";
Expand Down Expand Up @@ -663,13 +692,8 @@ bool SetStartOnSystemStartup(bool fAutoStart)


#elif defined(Q_OS_MAC)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
// based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m

#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>

LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl);
LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl)
{
Expand Down
3 changes: 3 additions & 0 deletions src/qt/guiutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ namespace GUIUtil
// Determine whether a widget is hidden behind other windows
bool isObscured(QWidget *w);

// Activate, show and raise the widget
void bringToFront(QWidget* w);

// Open debug.log
void openDebugLogfile();

Expand Down
21 changes: 2 additions & 19 deletions src/qt/macdockiconhandler.h
Original file line number Diff line number Diff line change
@@ -1,44 +1,27 @@
// Copyright (c) 2011-2015 The Bitcoin Core developers
// Copyright (c) 2011-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_QT_MACDOCKICONHANDLER_H
#define BITCOIN_QT_MACDOCKICONHANDLER_H

#include <QMainWindow>
#include <QObject>

QT_BEGIN_NAMESPACE
class QIcon;
class QMenu;
class QWidget;
QT_END_NAMESPACE

/** Macintosh-specific dock icon handler.
/** macOS-specific Dock icon handler.
*/
class MacDockIconHandler : public QObject
{
Q_OBJECT

public:
~MacDockIconHandler();

QMenu *dockMenu();
void setIcon(const QIcon &icon);
void setMainWindow(QMainWindow *window);
static MacDockIconHandler *instance();
static void cleanup();
void handleDockIconClickEvent();

Q_SIGNALS:
void dockIconClicked();

private:
MacDockIconHandler();

QWidget *m_dummyWidget;
QMenu *m_dockMenu;
QMainWindow *mainWindow;
};

#endif // BITCOIN_QT_MACDOCKICONHANDLER_H
100 changes: 9 additions & 91 deletions src/qt/macdockiconhandler.mm
Original file line number Diff line number Diff line change
@@ -1,107 +1,36 @@
// Copyright (c) 2011-2013 The Bitcoin Core developers
// Copyright (c) 2011-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "macdockiconhandler.h"

#include <QImageWriter>
#include <QMenu>
#include <QBuffer>
#include <QWidget>

#undef slots
#include <Cocoa/Cocoa.h>
#include <objc/objc.h>
#include <objc/message.h>

static MacDockIconHandler *s_instance = nullptr;

bool dockClickHandler(id self,SEL _cmd,...) {
bool dockClickHandler(id self, SEL _cmd, ...) {
Q_UNUSED(self)
Q_UNUSED(_cmd)

s_instance->handleDockIconClickEvent();
Q_EMIT s_instance->dockIconClicked();

// Return NO (false) to suppress the default OS X actions
// Return NO (false) to suppress the default macOS actions
return false;
}

void setupDockClickHandler() {
Class cls = objc_getClass("NSApplication");
id appInst = objc_msgSend((id)cls, sel_registerName("sharedApplication"));

if (appInst != nullptr) {
id delegate = objc_msgSend(appInst, sel_registerName("delegate"));
Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
if (class_getInstanceMethod(delClass, shouldHandle))
class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:");
else
class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler,"B@:");
}
id app = objc_msgSend((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
id delegate = objc_msgSend(app, sel_registerName("delegate"));
Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:");
}


MacDockIconHandler::MacDockIconHandler() : QObject()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

setupDockClickHandler();
this->m_dummyWidget = new QWidget();
this->m_dockMenu = new QMenu(this->m_dummyWidget);
this->setMainWindow(nullptr);
#if QT_VERSION >= 0x050200
this->m_dockMenu->setAsDockMenu();
#endif
[pool release];
}

void MacDockIconHandler::setMainWindow(QMainWindow *window) {
this->mainWindow = window;
}

MacDockIconHandler::~MacDockIconHandler()
{
delete this->m_dummyWidget;
this->setMainWindow(nullptr);
}

QMenu *MacDockIconHandler::dockMenu()
{
return this->m_dockMenu;
}

void MacDockIconHandler::setIcon(const QIcon &icon)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSImage *image = nil;
if (icon.isNull())
image = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
else {
// generate NSImage from QIcon and use this as dock icon.
QSize size = icon.actualSize(QSize(128, 128));
QPixmap pixmap = icon.pixmap(size);

// Write image into a R/W buffer from raw pixmap, then save the image.
QBuffer notificationBuffer;
if (!pixmap.isNull() && notificationBuffer.open(QIODevice::ReadWrite)) {
QImageWriter writer(&notificationBuffer, "PNG");
if (writer.write(pixmap.toImage())) {
NSData* macImgData = [NSData dataWithBytes:notificationBuffer.buffer().data()
length:notificationBuffer.buffer().size()];
image = [[NSImage alloc] initWithData:macImgData];
}
}

if(!image) {
// if testnet image could not be created, load std. app icon
image = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
}
}

[NSApp setApplicationIconImage:image];
[image release];
[pool release];
}

MacDockIconHandler *MacDockIconHandler::instance()
Expand All @@ -115,14 +44,3 @@ void setupDockClickHandler() {
{
delete s_instance;
}

void MacDockIconHandler::handleDockIconClickEvent()
{
if (this->mainWindow)
{
this->mainWindow->activateWindow();
this->mainWindow->show();
}

Q_EMIT this->dockIconClicked();
}
Loading

0 comments on commit 4822325

Please sign in to comment.