Skip to content

Backport bitcoin#14123 and bitcoin#16720 #3463

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ case $host in
fi

AX_CHECK_LINK_FLAG([[-Wl,-headerpad_max_install_names]], [LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names"])
CPPFLAGS="$CPPFLAGS -DMAC_OSX"
CPPFLAGS="$CPPFLAGS -DMAC_OSX -DOBJC_OLD_DISPATCH_PROTOTYPES=0"
OBJCXXFLAGS="$CXXFLAGS"
;;
*linux*)
Expand Down
44 changes: 17 additions & 27 deletions src/qt/bitcoingui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,8 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *
QString userWindowTitle = QString::fromStdString(gArgs.GetArg("-windowtitle", ""));
if(!userWindowTitle.isEmpty()) windowTitle += " - " + userWindowTitle;
windowTitle += " " + networkStyle->getTitleAddText();
#ifndef Q_OS_MAC
QApplication::setWindowIcon(networkStyle->getTrayAndWindowIcon());
setWindowIcon(networkStyle->getTrayAndWindowIcon());
#else
MacDockIconHandler::instance()->setIcon(networkStyle->getAppIcon());
#endif
setWindowTitle(windowTitle);

#if defined(Q_OS_MAC) && QT_VERSION < 0x050000
Expand Down Expand Up @@ -500,7 +496,9 @@ void BitcoinGUI::createActions()
connect(changePassphraseAction, SIGNAL(triggered()), walletFrame, SLOT(changePassphrase()));
connect(unlockWalletAction, SIGNAL(triggered()), walletFrame, SLOT(unlockWallet()));
connect(lockWalletAction, SIGNAL(triggered()), walletFrame, SLOT(lockWallet()));
connect(signMessageAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab()));
connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab()));
connect(usedSendingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedSendingAddresses()));
connect(usedReceivingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedReceivingAddresses()));
Expand Down Expand Up @@ -645,8 +643,10 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
// Note: On Mac, the dock icon is also used to provide menu functionality
// similar to one for tray icon
MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
dockIconHandler->setMainWindow((QMainWindow *)this);
dockIconMenu = dockIconHandler->dockMenu();
connect(dockIconHandler, SIGNAL(dockIconClicked()), this, SLOT(macosDockIconActivated()));

dockIconMenu = new QMenu(this);
dockIconMenu->setAsDockMenu();

createIconMenu(dockIconMenu);
#endif
Expand Down Expand Up @@ -808,6 +808,12 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
toggleHidden();
}
}
#else
void BitcoinGUI::macosDockIconActivated()
{
show();
activateWindow();
}
#endif

void BitcoinGUI::optionsClicked()
Expand All @@ -831,10 +837,7 @@ void BitcoinGUI::aboutClicked()

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

void BitcoinGUI::showInfo()
Expand Down Expand Up @@ -1468,24 +1471,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 @@ -274,6 +274,9 @@ private 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
31 changes: 26 additions & 5 deletions src/qt/guiutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ extern double NSAppKitVersionNumber;
#if !defined(NSAppKitVersionNumber10_9)
#define NSAppKitVersionNumber10_9 1265
#endif

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"

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

void ForceActivation();
#endif

namespace GUIUtil {
Expand Down Expand Up @@ -471,6 +479,24 @@ bool isObscured(QWidget *w)
&& checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
}

void bringToFront(QWidget* w)
{
#ifdef Q_OS_MAC
ForceActivation();
#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 @@ -841,13 +867,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 @@ -151,6 +151,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
113 changes: 18 additions & 95 deletions src/qt/macdockiconhandler.mm
Original file line number Diff line number Diff line change
@@ -1,113 +1,37 @@
// Copyright (c) 2011-2013 The Bitcoin Core developers
// Copyright (c) 2011-2019 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>
#include <AppKit/AppKit.h>
#include <objc/runtime.h>

#if QT_VERSION < 0x050000
extern void qt_mac_set_dock_menu(QMenu *);
#endif

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();
// Return NO (false) to suppress the default OS X actions

Q_EMIT s_instance->dockIconClicked();

// 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@:");
}
Class delClass = (Class)[[[NSApplication sharedApplication] delegate] 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 < 0x050000
qt_mac_set_dock_menu(this->m_dockMenu);
#elif 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 @@ -122,13 +46,12 @@ void setupDockClickHandler() {
delete s_instance;
}

void MacDockIconHandler::handleDockIconClickEvent()
/**
* Force application activation on macOS. With Qt 5.5.1 this is required when
* an action in the Dock menu is triggered.
* TODO: Define a Qt version where it's no-longer necessary.
*/
void ForceActivation()
{
if (this->mainWindow)
{
this->mainWindow->activateWindow();
this->mainWindow->show();
}

Q_EMIT this->dockIconClicked();
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}
8 changes: 2 additions & 6 deletions src/qt/walletview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,19 +346,15 @@ void WalletView::usedSendingAddresses()
if(!walletModel)
return;

usedSendingAddressesPage->show();
usedSendingAddressesPage->raise();
usedSendingAddressesPage->activateWindow();
GUIUtil::bringToFront(usedSendingAddressesPage);
}

void WalletView::usedReceivingAddresses()
{
if(!walletModel)
return;

usedReceivingAddressesPage->show();
usedReceivingAddressesPage->raise();
usedReceivingAddressesPage->activateWindow();
GUIUtil::bringToFront(usedReceivingAddressesPage);
}

void WalletView::showProgress(const QString &title, int nProgress)
Expand Down