Restoring Qt app after minimized to dock, using dock shortcut on OS X - c++

Single-clicking an application icon in the Dock launches that application or, if the application is already open, switches you to that application and brings forward all open windows in that application.
In my Qt app, I do not see this behavior. I have a dock shortcut, I minimize the app on dock, and if I click the minimized instance the app is restored.
If I click the application icon in the Dock (dock shortcut), nothing happens.
(I can right-click, and "Show All Windows" - but that is not similar to the behavior of all other apps)
I tried to place the code from this answer: https://stackoverflow.com/a/15363738/1217150
Placing qDebug statements,
if(test) qDebug("registered");
in the constructor, and
void MyApplictionClass::onClickOnDock()
{
qDebug("dock clicked");
}
On start-up I get both messages... but after minimizing, or any time after start-up, there is no reaction to dock shortcut being clicked or double clicked.
Full code insertion:
MyApplicationClass.h:
#include <objc/objc.h>
#include <objc/message.h>
class MyApplicationClass: public QApplication
{
....
bool dockClickHandler(id self, SEL _cmd, ...);
void onClickOnDock();
};
MyApplicationClass.cpp:
MyApplicationClass::MyApplicationClass()
{
....
objc_object* cls = objc_getClass("NSApplication");
SEL sharedApplication = sel_registerName("sharedApplication");
objc_object* appInst = objc_msgSend(cls, sharedApplication);
if(appInst != NULL)
{
objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate"));
objc_object* delClass = objc_msgSend(delegate, sel_registerName("class"));
const char* tst = class_getName(delClass->isa);
bool test = class_addMethod((objc_class*)delClass,
sel_registerName("applicationShouldHandleReopen::hasVisibleWindows:"),
(IMP)dockClickHandler(appInst, sharedApplication), "B#:");
if(!test) qDebug("not registered");
else qDebug("registered");
}
.....
}
bool MyApplicationClass::dockClickHandler(id self, SEL _cmd, ...)
{
Q_UNUSED(self)
Q_UNUSED(_cmd)
onClickOnDock();
return true;
}
void MyApplicationClass::onClickOnDock()
{
qDebug("dock clicked");
}
Perhaps I did something wrong ? There are a few items I don't understand... The referred post had dockClickHandler not part of the class... I just don't know where they have it... And I had to give it arguments when I registered it, otherwise it refused to build. Still, it seems to do... something... just not the right thing. In that code, I do not see where the app will react to clicking the dock.
Is this the correct approach ? Or how can I get my app to restore when its dock shortcut is clicked ?
(OSX 10.6-10.9, Qt 4.8)

If you are refering to this: QT on OS X, how to detect clicking the app Dock Icon, the onClickOnDock should not be a member of the class.
You CAN put it as a normal function inside the MyApplicationClass.cpp file.

There is a hack at least for Qt 5.9.1, when you can implement onClickOnDock() without platform specific code at all. See https://stackoverflow.com/a/46488514/8695355

Related

How can I create multiple windows in winui 3.0?

I would like to have a button that creates another window. I'm building off the template app you get in VS2019 Preview So far, I've created a new control BlankWindow, just the default one. I can see that in App::OnLaunched, a window is created with:
window = make<MainWindow>();
window.Activate();
So in my MainWindow.xaml.cs in my button click method, I put:
Window bWindow= make<BlankWindow>();
bWindow.Activate();
That doesn't build, I check the BlankWindow.idl file and see that BlankWindow inherits from Controls and not Windows, so I change it. Now it builds, but when I click the button in the MainWindow, I see the window flicker open and instantly close. What's going on?
Work on multi-window applications is ongoing. It's available as a Preview feature.
https://learn.microsoft.com/en-us/windows/apps/winui/winui3/#preview-features
You can find the solution to this problem in the definition of the Window Class for the Windows UI Library (WinUI) under the subtitle "Create a new Window on a new thread". To put the section in context it would help to read the entire webpage. It's not that long.
We were able to include xaml.h for the target subwindow and display the window using the make template function.
Note that the types held in the member variables and the types specified to the make template function are different.
(I am using the translation at DeepL, so the text may be slightly incorrect.)
#include "pch.h"
#include "MainWindow.xaml.h"
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#include "Window_Sub.xaml.h"
#endif
winrt::App1::Window_Sub _subWindow{ nullptr };
void MainWindow::ShowSubWindowButton_Clicked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
if (_subWindow == nullptr) {
_subWindow = make<App1::implementation::Window_Sub>();
_subWindow.Closed([](winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::WindowEventArgs const& e){
_subWindow = nullptr;
});
}
_subWindow.Activate();
}

Clicking QSystemTrayIcon brings up an empty context menu on Mate desktop

I'd like to create a tray icon for my application for showing the main window on clicking on it after the former was minimized.
Here's the implementation:
TrayIcon.h:
class TrayIcon_t : public QSystemTrayIcon {
Q_OBJECT
public:
TrayIcon_t();
};
TrayIcon.cpp:
TrayIcon_t::TrayIcon_t() {
setIcon(QIcon(":/icons/tray.ico"));
}
Main.cpp (part only, there's no more code related to the tray):
TrayIcon_t *tray = new TrayIcon_t;
QObject::connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), &MainWindow, SLOT(show()));
tray->show();
While on LXDE it works just fine, on MATE desktop it mostly opens a small menu (I think) containing no items and doesn't emit the signal required by the main window to be shown. See this picture.
Do you think I've encountered a bug in Qt 5.7.0?
Turns out that this is a bug. Reported it and became accepted at:
https://bugreports.qt.io/browse/QTBUG-55717

C++ Global Hotkeys with platform APIs

I'm working on an application for taking screenshots on Windows, OSX and Linux in C++/Qt. Now I need to set global hotkeys, so the user can take screenshots when the application is running in the background. I tried with Qxt and UGlobalHotkey, which are both Qt libraries, but neither of them seemed to work.
I tried to implement it for OSX with Carbon (tutorial), but I need to call a class member function, which just doesn't work. Could someone provide me with an example? You can find my code here. The function i need to call is new_screenshot().
Or is there any other way to achieve something like this? I really need my application to take a screenshot from the background, otherwise it's pretty useless (yes, I should probably have implemented it at the very beginning to see if it even works). Would it maybe be better to have a separate client for every platform (Cocoa Swift for OSX, GTK for Linux, C# client for Windows)? I have often thought about this the past few days.
Do I understand correctly that you want to call new_screenshot from the hot key event handler? If so, InstallApplicationEventHandler lets you pass a pointer to user data in 4th argument. Pass a pointer to your MainWindow instance (based on code from the tutorial):
MainWindow *mainWindow = ... // get main window somehow
InstallApplicationEventHandler(&MyHotKeyHandler,1,&eventType,mainWindow,NULL);
Then you can use it in the event handler.
OSStatus MyHotKeyHandler(EventHandlerCallRef nextHandler,EventRef theEvent, void *userData)
{
//Do something once the key is pressed
static_cast<MainWindow*>(userData)->new_screenshot();
return noErr;
}
I did something in the past with MFC and WIN32 API....so it only works on Windows...but pressing ALT+F10 was able to hide/show a window...
void CWinHideDlg::OnButtonActive()
{
CString tmp;
GetDlgItemText(IDC_BUTTON_ACTIVE,tmp);
if(0 == strcmp(tmp.GetBuffer(tmp.GetLength()),"Activate"))
{
m_myAtom=GlobalAddAtom("MY_GLOBAL_HOT_HIDE_KEY");
int err=RegisterHotKey(this->GetSafeHwnd(),m_myAtom,MOD_ALT,VK_F10);
SetDlgItemText(IDC_BUTTON_ACTIVE,"Stop");
CButton *pBtn = (CButton *)GetDlgItem(IDC_BUTTON_UNHIDE);
pBtn->EnableWindow(TRUE);
SetDlgItemText(IDC_STATIC_INFO,"Set the mouse over the window \nand press ALT + F10 to hide it...");
}
else
{
UnregisterHotKey(this->GetSafeHwnd(),m_myAtom);
GlobalDeleteAtom(m_myAtom);
CButton *pBtn = (CButton *)GetDlgItem(IDC_BUTTON_UNHIDE);
pBtn->EnableWindow(FALSE);
SetDlgItemText(IDC_BUTTON_ACTIVE,"Activate");
}
}
Basically this code activates/deactivates the hot key ALT+F10, once it activates you can hide/unhide a running window on the system by setting the mouse pointer over the window and press ALT+F10...
This is from the WindowProc function:
if(message == WM_HOTKEY)
{
CString tmp;
POINT pc;
GetCursorPos(&pc);
if(GetAsyncKeyState(VK_F10))
{
HWND hwnd=::WindowFromPoint(pc);
if(hwnd)
{
tmp.Format("%08Xh",hwnd);
m_HideWins.InsertString(m_HideWins.GetCount(),tmp);
::ShowWindow(hwnd,SW_HIDE);
}
}
}
You can use the code to register your own HOT Key and use it to take a screenshot...
Hope it helps...

QSystemTrayIcon handle left and right click separately?

I have a class called StatusIcon that extends QSystemTrayIcon. I want to set it up so right click opens the context menu and left click opens a window.
Currently the default behaviour seems to be both left and right click open the context menu.
I need to find a way to block the left click and run my own code instead.
From the documentation it looks like this could be achieved using eventFilter I have setup an eventFilter method on StatusIcon with a qdebug in it. This doesn't get called with a right or left click.
I installed it using a line of code like:
this->installEventFilter(this)
I'm wondering if its not working as its already overriding the virtual method as I've got QSystemTrayIcon as the super class.
Does anyone know why eventFilter is not being called?
Could anyone think of a way to achieve this functionality?
You don't need eventFilter. For left click:
//somewhere in constructor
connect(tray,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),this,SLOT(showHide(QSystemTrayIcon::ActivationReason)));
//...
void MainWindow::showHide(QSystemTrayIcon::ActivationReason r)
{
if (r == QSystemTrayIcon::Trigger)
{
if (!this->isVisible()) {
this->show();
} else {
this->hide();
}
}
}
For menu, just use setContextMenu():
QMenu *menu = new QMenu(this);
//for example
menu->addAction(showHideAct);
menu->addAction(optionAct);
menu->addAction(infoAct);
menu->addSeparator();
menu->addAction(quitAct);
tray = new QSystemTrayIcon();
tray->setIcon(QIcon("://data/tray.png"));
tray->setContextMenu(menu);//important method for you
tray->show();

Clickable menu item with submenu in Qt

I am writing in Qt 4.6. I am wondering if it's possible to achieve such a menu item, that it's possible to be triggered, but also has a submenu. Clicking it triggers associated action, hovering it causes submenu to appear.
Let me start by saying that this is not a good plan of attack. There are corner cases here that will take a rediculous amount of time and code to get just right, and will probably require per-operating system customization.
With that said, however, the actual implementation isn't too complicated. Just subclass the QMenu that you're making your submenu from, and override the event handlers, forcing the parent menu closed when a 'selection' is made. Something like the following basically works:
from PyQt4 import QtCore, QtGui
import sys
app = QtGui.QApplication(sys.argv)
widget = QtGui.QMainWindow()
widget.resize(250,150)
menu = widget.menuBar().addMenu("test")
class submenu(QtGui.QMenu): #Override the submenu class
def __init__(self,name):
QtGui.QMenu.__init__(self,name)
def mouseReleaseEvent(self,event): #catch mouseRelease Events
global menu
QtGui.QMenu.mouseReleaseEvent(self,event)
if not self.rect().contains(event.pos()):
print("Parent Selected")
menu.hide() #If the parent was selected, hide it
else: #Likely ignore these
print("Parent NOT Selected")
def c():
print("Sub-item selected")
cMenu = submenu("Sub-menu")
menu.addMenu(cMenu)
actionC = QtGui.QAction("sub-item",widget)
actionC.triggered.connect(c)
cMenu.addAction(actionC)
widget.show()
sys.exit(app.exec_())
This behavior is a bit confusing, but i am trying to develop a UI with as little clicking as possible. Although a bit unexpected, this behavior makes it a bit faster to use when you get used to it.
I haven't wrote that in my previous message, but i am writing in c++, and i have no idea about python... Anyway i managed to translate idea to c++, and it works but it's quite ugly... I found a bit better approach by looking through qt source (when i was asking this question I was hoping there is some better, "intended" method)
class MyMenu : public QMenu
{
Q_OBJECT
public:
MyMenu(QWidget* parent);
~MyMenu();
virtual void mouseReleaseEvent( QMouseEvent * event );
};
MyMenu::MyMenu(QWidget* parent):QMenu(parent)
{
}
MyMenu::~MyMenu()
{
}
void MyMenu::mouseReleaseEvent( QMouseEvent * event ){
QAction* act = menuAction();
if (act){
QMenu* men = act->menu();
act->setMenu(0);
QMenu::mouseReleaseEvent(event);
act->setMenu(men);
}else{
QMenu::mouseReleaseEvent(event);
}
}
The only disadvantage is that such a menu would react to clicking on all options with submenus, not only desired ones. Perhaps it would be a good idea to check if anything is connected to action's signals?
On my ubuntu it works. However i guess it wouldn't work on windows, where system manages menus exclusively (unless qt uses some windows with menu's look and feel, not system menus), but i am too lazy to instal windows just to check it ;)
void CustomMenu::mouseReleaseEvent( QMouseEvent* event )
{
QAction* action = menuAction();
// Filter out submenus. TODO: Is there a better way to do this?
if ( action && !rect().contains( event->pos() ) ) {
QMenu* menu = action->menu();
action->setMenu( 0 );
QMenu::mouseReleaseEvent( event );
emit customTriggeredSignal();
action->setMenu( menu );
}
else {
QMenu::mouseReleaseEvent( event );
}
}
Combining both helpful answers, from j_kubik and jkerian, this mouseReleaseEvent() in the top-level menu subclassing QMenu ignores unwanted invocations of submenus.
I will report back as to whether the approach is suitable on Windows.