Skip to content

Commit 4822325

Browse files
committed
Merge #14123: gui: Add GUIUtil::bringToFront
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
2 parents ae32806 + 0a656f8 commit 4822325

File tree

7 files changed

+76
-159
lines changed

7 files changed

+76
-159
lines changed

src/qt/bitcoingui.cpp

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,8 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
9292
windowTitle += tr("Node");
9393
}
9494
windowTitle += " " + networkStyle->getTitleAddText();
95-
#ifndef Q_OS_MAC
9695
QApplication::setWindowIcon(networkStyle->getTrayAndWindowIcon());
9796
setWindowIcon(networkStyle->getTrayAndWindowIcon());
98-
#else
99-
MacDockIconHandler::instance()->setIcon(networkStyle->getAppIcon());
100-
#endif
10197
setWindowTitle(windowTitle);
10298

10399
rpcConsole = new RPCConsole(node, _platformStyle, 0);
@@ -278,17 +274,17 @@ void BitcoinGUI::createActions()
278274
#ifdef ENABLE_WALLET
279275
// These showNormalIfMinimized are needed because Send Coins and Receive Coins
280276
// can be triggered from the tray menu, and need to show the GUI to be useful.
281-
connect(overviewAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
277+
connect(overviewAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
282278
connect(overviewAction, &QAction::triggered, this, &BitcoinGUI::gotoOverviewPage);
283-
connect(sendCoinsAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
279+
connect(sendCoinsAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
284280
connect(sendCoinsAction, &QAction::triggered, [this]{ gotoSendCoinsPage(); });
285-
connect(sendCoinsMenuAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
281+
connect(sendCoinsMenuAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
286282
connect(sendCoinsMenuAction, &QAction::triggered, [this]{ gotoSendCoinsPage(); });
287-
connect(receiveCoinsAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
283+
connect(receiveCoinsAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
288284
connect(receiveCoinsAction, &QAction::triggered, this, &BitcoinGUI::gotoReceiveCoinsPage);
289-
connect(receiveCoinsMenuAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
285+
connect(receiveCoinsMenuAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
290286
connect(receiveCoinsMenuAction, &QAction::triggered, this, &BitcoinGUI::gotoReceiveCoinsPage);
291-
connect(historyAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
287+
connect(historyAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
292288
connect(historyAction, &QAction::triggered, this, &BitcoinGUI::gotoHistoryPage);
293289
#endif // ENABLE_WALLET
294290

@@ -355,7 +351,9 @@ void BitcoinGUI::createActions()
355351
connect(encryptWalletAction, &QAction::triggered, walletFrame, &WalletFrame::encryptWallet);
356352
connect(backupWalletAction, &QAction::triggered, walletFrame, &WalletFrame::backupWallet);
357353
connect(changePassphraseAction, &QAction::triggered, walletFrame, &WalletFrame::changePassphrase);
354+
connect(signMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
358355
connect(signMessageAction, &QAction::triggered, [this]{ gotoSignMessageTab(); });
356+
connect(verifyMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
359357
connect(verifyMessageAction, &QAction::triggered, [this]{ gotoVerifyMessageTab(); });
360358
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
361359
connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses);
@@ -604,7 +602,7 @@ void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle)
604602
void BitcoinGUI::createTrayIconMenu()
605603
{
606604
#ifndef Q_OS_MAC
607-
// return if trayIcon is unset (only on non-Mac OSes)
605+
// return if trayIcon is unset (only on non-macOSes)
608606
if (!trayIcon)
609607
return;
610608

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

614612
connect(trayIcon, &QSystemTrayIcon::activated, this, &BitcoinGUI::trayIconActivated);
615613
#else
616-
// Note: On Mac, the dock icon is used to provide the tray's functionality.
614+
// Note: On macOS, the Dock icon is used to provide the tray's functionality.
617615
MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
618-
dockIconHandler->setMainWindow(static_cast<QMainWindow*>(this));
619-
trayIconMenu = dockIconHandler->dockMenu();
616+
connect(dockIconHandler, &MacDockIconHandler::dockIconClicked, this, &BitcoinGUI::macosDockIconActivated);
617+
618+
trayIconMenu = new QMenu(this);
619+
trayIconMenu->setAsDockMenu();
620620
#endif
621621

622-
// Configuration of the tray icon (or dock icon) icon menu
622+
// Configuration of the tray icon (or Dock icon) menu
623623
#ifndef Q_OS_MAC
624-
// Note: On Mac, the dock icon's menu already has show / hide action.
624+
// Note: On macOS, the Dock icon's menu already has Show / Hide action.
625625
trayIconMenu->addAction(toggleHideAction);
626626
trayIconMenu->addSeparator();
627627
#endif
@@ -635,7 +635,7 @@ void BitcoinGUI::createTrayIconMenu()
635635
trayIconMenu->addAction(openRPCConsoleAction);
636636
}
637637
trayIconMenu->addAction(optionsAction);
638-
#ifndef Q_OS_MAC // This is built-in on Mac
638+
#ifndef Q_OS_MAC // This is built-in on macOS
639639
trayIconMenu->addSeparator();
640640
trayIconMenu->addAction(quitAction);
641641
#endif
@@ -650,6 +650,12 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
650650
toggleHidden();
651651
}
652652
}
653+
#else
654+
void BitcoinGUI::macosDockIconActivated()
655+
{
656+
show();
657+
activateWindow();
658+
}
653659
#endif
654660

655661
void BitcoinGUI::optionsClicked()
@@ -668,10 +674,7 @@ void BitcoinGUI::aboutClicked()
668674

669675
void BitcoinGUI::showDebugWindow()
670676
{
671-
rpcConsole->showNormal();
672-
rpcConsole->show();
673-
rpcConsole->raise();
674-
rpcConsole->activateWindow();
677+
GUIUtil::bringToFront(rpcConsole);
675678
}
676679

677680
void BitcoinGUI::showDebugWindowActivateConsole()
@@ -1158,24 +1161,11 @@ void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
11581161
if(!clientModel)
11591162
return;
11601163

1161-
// activateWindow() (sometimes) helps with keyboard focus on Windows
1162-
if (isHidden())
1163-
{
1164-
show();
1165-
activateWindow();
1166-
}
1167-
else if (isMinimized())
1168-
{
1169-
showNormal();
1170-
activateWindow();
1171-
}
1172-
else if (GUIUtil::isObscured(this))
1173-
{
1174-
raise();
1175-
activateWindow();
1176-
}
1177-
else if(fToggleHidden)
1164+
if (!isHidden() && !isMinimized() && !GUIUtil::isObscured(this) && fToggleHidden) {
11781165
hide();
1166+
} else {
1167+
GUIUtil::bringToFront(this);
1168+
}
11791169
}
11801170

11811171
void BitcoinGUI::toggleHidden()

src/qt/bitcoingui.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ public Q_SLOTS:
268268
#ifndef Q_OS_MAC
269269
/** Handle tray icon clicked */
270270
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
271+
#else
272+
/** Handle macOS Dock icon clicked */
273+
void macosDockIconActivated();
271274
#endif
272275

273276
/** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */

src/qt/guiutil.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@
6060
#include <QFontDatabase>
6161
#endif
6262

63+
#if defined(Q_OS_MAC)
64+
#pragma GCC diagnostic push
65+
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
66+
67+
#include <objc/objc-runtime.h>
68+
#include <CoreServices/CoreServices.h>
69+
#endif
70+
6371
namespace GUIUtil {
6472

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

364+
void bringToFront(QWidget* w)
365+
{
366+
#ifdef Q_OS_MAC
367+
// Force application activation on macOS. With Qt 5.4 this is required when
368+
// an action in the dock menu is triggered.
369+
id app = objc_msgSend((id) objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
370+
objc_msgSend(app, sel_registerName("activateIgnoringOtherApps:"), YES);
371+
#endif
372+
373+
if (w) {
374+
// activateWindow() (sometimes) helps with keyboard focus on Windows
375+
if (w->isMinimized()) {
376+
w->showNormal();
377+
} else {
378+
w->show();
379+
}
380+
w->activateWindow();
381+
w->raise();
382+
}
383+
}
384+
356385
void openDebugLogfile()
357386
{
358387
fs::path pathDebug = GetDataDir() / "debug.log";
@@ -663,13 +692,8 @@ bool SetStartOnSystemStartup(bool fAutoStart)
663692

664693

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

670-
#include <CoreFoundation/CoreFoundation.h>
671-
#include <CoreServices/CoreServices.h>
672-
673697
LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl);
674698
LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl)
675699
{

src/qt/guiutil.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ namespace GUIUtil
115115
// Determine whether a widget is hidden behind other windows
116116
bool isObscured(QWidget *w);
117117

118+
// Activate, show and raise the widget
119+
void bringToFront(QWidget* w);
120+
118121
// Open debug.log
119122
void openDebugLogfile();
120123

src/qt/macdockiconhandler.h

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,27 @@
1-
// Copyright (c) 2011-2015 The Bitcoin Core developers
1+
// Copyright (c) 2011-2018 The Bitcoin Core developers
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#ifndef BITCOIN_QT_MACDOCKICONHANDLER_H
66
#define BITCOIN_QT_MACDOCKICONHANDLER_H
77

8-
#include <QMainWindow>
98
#include <QObject>
109

11-
QT_BEGIN_NAMESPACE
12-
class QIcon;
13-
class QMenu;
14-
class QWidget;
15-
QT_END_NAMESPACE
16-
17-
/** Macintosh-specific dock icon handler.
10+
/** macOS-specific Dock icon handler.
1811
*/
1912
class MacDockIconHandler : public QObject
2013
{
2114
Q_OBJECT
2215

2316
public:
24-
~MacDockIconHandler();
25-
26-
QMenu *dockMenu();
27-
void setIcon(const QIcon &icon);
28-
void setMainWindow(QMainWindow *window);
2917
static MacDockIconHandler *instance();
3018
static void cleanup();
31-
void handleDockIconClickEvent();
3219

3320
Q_SIGNALS:
3421
void dockIconClicked();
3522

3623
private:
3724
MacDockIconHandler();
38-
39-
QWidget *m_dummyWidget;
40-
QMenu *m_dockMenu;
41-
QMainWindow *mainWindow;
4225
};
4326

4427
#endif // BITCOIN_QT_MACDOCKICONHANDLER_H

src/qt/macdockiconhandler.mm

Lines changed: 9 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,36 @@
1-
// Copyright (c) 2011-2013 The Bitcoin Core developers
1+
// Copyright (c) 2011-2018 The Bitcoin Core developers
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include "macdockiconhandler.h"
66

7-
#include <QImageWriter>
8-
#include <QMenu>
9-
#include <QBuffer>
10-
#include <QWidget>
11-
127
#undef slots
13-
#include <Cocoa/Cocoa.h>
148
#include <objc/objc.h>
159
#include <objc/message.h>
1610

1711
static MacDockIconHandler *s_instance = nullptr;
1812

19-
bool dockClickHandler(id self,SEL _cmd,...) {
13+
bool dockClickHandler(id self, SEL _cmd, ...) {
2014
Q_UNUSED(self)
2115
Q_UNUSED(_cmd)
2216

23-
s_instance->handleDockIconClickEvent();
17+
Q_EMIT s_instance->dockIconClicked();
2418

25-
// Return NO (false) to suppress the default OS X actions
19+
// Return NO (false) to suppress the default macOS actions
2620
return false;
2721
}
2822

2923
void setupDockClickHandler() {
30-
Class cls = objc_getClass("NSApplication");
31-
id appInst = objc_msgSend((id)cls, sel_registerName("sharedApplication"));
32-
33-
if (appInst != nullptr) {
34-
id delegate = objc_msgSend(appInst, sel_registerName("delegate"));
35-
Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
36-
SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
37-
if (class_getInstanceMethod(delClass, shouldHandle))
38-
class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:");
39-
else
40-
class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler,"B@:");
41-
}
24+
id app = objc_msgSend((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
25+
id delegate = objc_msgSend(app, sel_registerName("delegate"));
26+
Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
27+
SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
28+
class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:");
4229
}
4330

44-
4531
MacDockIconHandler::MacDockIconHandler() : QObject()
4632
{
47-
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
48-
4933
setupDockClickHandler();
50-
this->m_dummyWidget = new QWidget();
51-
this->m_dockMenu = new QMenu(this->m_dummyWidget);
52-
this->setMainWindow(nullptr);
53-
#if QT_VERSION >= 0x050200
54-
this->m_dockMenu->setAsDockMenu();
55-
#endif
56-
[pool release];
57-
}
58-
59-
void MacDockIconHandler::setMainWindow(QMainWindow *window) {
60-
this->mainWindow = window;
61-
}
62-
63-
MacDockIconHandler::~MacDockIconHandler()
64-
{
65-
delete this->m_dummyWidget;
66-
this->setMainWindow(nullptr);
67-
}
68-
69-
QMenu *MacDockIconHandler::dockMenu()
70-
{
71-
return this->m_dockMenu;
72-
}
73-
74-
void MacDockIconHandler::setIcon(const QIcon &icon)
75-
{
76-
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
77-
NSImage *image = nil;
78-
if (icon.isNull())
79-
image = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
80-
else {
81-
// generate NSImage from QIcon and use this as dock icon.
82-
QSize size = icon.actualSize(QSize(128, 128));
83-
QPixmap pixmap = icon.pixmap(size);
84-
85-
// Write image into a R/W buffer from raw pixmap, then save the image.
86-
QBuffer notificationBuffer;
87-
if (!pixmap.isNull() && notificationBuffer.open(QIODevice::ReadWrite)) {
88-
QImageWriter writer(&notificationBuffer, "PNG");
89-
if (writer.write(pixmap.toImage())) {
90-
NSData* macImgData = [NSData dataWithBytes:notificationBuffer.buffer().data()
91-
length:notificationBuffer.buffer().size()];
92-
image = [[NSImage alloc] initWithData:macImgData];
93-
}
94-
}
95-
96-
if(!image) {
97-
// if testnet image could not be created, load std. app icon
98-
image = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
99-
}
100-
}
101-
102-
[NSApp setApplicationIconImage:image];
103-
[image release];
104-
[pool release];
10534
}
10635

10736
MacDockIconHandler *MacDockIconHandler::instance()
@@ -115,14 +44,3 @@ void setupDockClickHandler() {
11544
{
11645
delete s_instance;
11746
}
118-
119-
void MacDockIconHandler::handleDockIconClickEvent()
120-
{
121-
if (this->mainWindow)
122-
{
123-
this->mainWindow->activateWindow();
124-
this->mainWindow->show();
125-
}
126-
127-
Q_EMIT this->dockIconClicked();
128-
}

0 commit comments

Comments
 (0)