Restore maximized/normal window after launching minimized app in QT - c++

I have a problem with my Qt app. I can save and restore qeometry of my app without any problem. The problem is when I try to saveGeometry of maximized app in close event and after that I try to start the some app minimized (something like "Run mimimized on startup"). When I do that and I clicked icon on a taskbar the window is open as normal window (not maximized).
I can image (and write) some workaround but with all Gui logic it's easy to do some mistake that I prefere some native way. I'm looking for something like parametr when I could set that window after minimalization should be maximized or normal.
I don't have this problem when I start app maximized, next I minimize window and again maximize. In this case everything is ok. Problem is only on startup.
It look like small detail but it is big think if we think on app which people use everyday and they can't have everyday the some window.
EDIT:
After few hours I give up.
Here is my workaround. Not deeply tested yet, but mayby somebody needs it in the future.
I'll edit it if I find some error or better solution.
#include "mainwindow.h"
#include <QSettings>
#include <QCloseEvent>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), settings("MyCompany", "MyApp") {
settingsReaded = false;
startMinimized = true;
if(startMinimized) showMinimized();
else show();
}
void MainWindow::closeEvent(QCloseEvent *event) {
settings.setValue("geometry", saveGeometry());
showMinimized();
event->ignore();
}
void MainWindow::readSettings() {
restoreGeometry(settings.value("geometry").toByteArray());
settingsReaded = true;
}
void MainWindow::changeEvent(QEvent * event) {
bool isActive = (event->type() == QEvent::ActivationChange && isActiveWindow());
//load settings on first window activation (click on icon of minimized window or
//open window if we have startMinimized = false
//settingsReaded - determine that the settings was loaded only once.
if(isActive && !settingsReaded) {
readSettings();
}
}

Related

Maximize window maintaining taskbar limits

I want to create a Qt application without the windows title bar (I want to create a customized one).
I've created three buttons for minimizing, maximizing and closing the window. Everything works except for considering that when I maximize the window, application doesn't take into account the taskbar, and the maximized window takes the entire screen, going under the taskbar. A normal maximize command from windows instead maximizes the application window avoiding to go under the taskbar.
If I don't use the Qt::CustomizeWindowHint the window title bar appears, and maximizing behaviour is correct; but if I use this flag, the title bar disappears and the application goes under the window: here you can find two screenshots explaning the behaviour:
With Windows title:
Without Windows title:
As you can see in latter case che "Close" button goes inside the taskbar because the application takes the entire screen.
How can I avoid this behaviour without using windows title bar? I want to recreate the same behaviour as with the window title bar.
SampleWindow.h
#ifndef SAMPLEWINDOW_H_
#define SAMPLEWINDOW_H_
#include <QMainWindow>
#include <QPushButton>
#include <QHBoxLayout>
class SampleWindow : public QMainWindow {
Q_OBJECT
public:
SampleWindow();
virtual ~SampleWindow() = default;
};
#endif // !SAMPLEWINDOW_H_
SampleWindow.cpp
#include "SampleWindow.h"
#include <QCoreApplication>
SampleWindow::SampleWindow() :
QMainWindow() {
// With uncommenting this line the title bar disappears
// but application goes under the taskbar when maximized
//
//setWindowFlags(Qt::CustomizeWindowHint);
auto centralWidget = new QWidget(this);
auto layout = new QHBoxLayout(this);
auto minimizeButton = new QPushButton("Minimize", this);
auto maximizeButton = new QPushButton("Maximize", this);
auto closeButton = new QPushButton("Close", this);
layout->addWidget(minimizeButton);
layout->addWidget(maximizeButton);
layout->addWidget(closeButton);
centralWidget->setLayout(layout);
setCentralWidget(centralWidget);
connect(closeButton, &QPushButton::clicked, [=]() {QCoreApplication::quit();});
connect(minimizeButton, &QPushButton::clicked, this, [=]() {setWindowState(Qt::WindowMinimized);});
connect(maximizeButton, &QPushButton::clicked, this, [=]() {setWindowState(Qt::WindowMaximized);});
}
Main.cpp
#include <QApplication>
#include "SampleWindow.h"
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
SampleWindow mainWindow;
mainWindow.show();
return app.exec();
}
This behavior depends on system. I tested your code on Windows 7 and Linux Mint KDE and behavior was different. In Windows 7 taskbar has hidden and window has filled area of taskbar. In KDE I have noticed that window maximizes correctly (avoids widget panels and not hides them).
However when I try to run code in Windows 10 with compatibility mode, I was able to repeat behavior of Win7 only in compatibility with Windows Vista and older versions.
For Windows 10 I found another solution: you can maximize your window in fullscreen if that suits you:
mainWindow.showFullScreen();
or
setWindowState(Qt::WindowFullScreen);
UPD:
In addition to your solution I found another one:
setGeometry(QApplication::desktop()->availableGeometry().x(),
QApplication::desktop()->availableGeometry().y(),
QApplication::desktop()->availableGeometry().width(),
QApplication::desktop()->availableGeometry().height());
I think that I've found a solution by using this slot when maximize button is clicked:
void SampleWindow::maximize() {
//setWindowState(Qt::WindowFullScreen);
QDesktopWidget *desktop = QApplication::desktop();
QRect desktopGeometry = desktop->availableGeometry();
int desktopHeight = desktopGeometry.height();
int desktopWidth = desktopGeometry.width();
int padx = (frameGeometry().width() - geometry().width()) / 2;
setFixedSize(desktopWidth, desktopHeight);
move(-padx,0);
}
I need to test it more but at the moment the area seems correct.

Qt Dialog X Button Override Reject Not Working As Expected

I have been trying to hide a stand alone dialog application when the user hits the typical close button (The one with the X in the corner usually next to the minimize button) I cam across this post:
Qt: How do I handle the event of the user pressing the 'X' (close) button?
which I thought would have my solution, but I get strange behavior when I implement it.
void MyDialog::reject()
{
this->hide()
}
When I hit the X Button the whole application closes (the process disappears) which is not what I want. Since my gui spawns with a command line, I setup a test system where I can tell my dialog to hide via a text command where I call the same 'this->hide()' instruction, and everything works fine. The dialog hides and then shows back up when I tell it to show.
Any ideas why the reject method is closing my app completely even when I don't explicitly tell it to?
Override the virtual function "virtual void closeEvent(QCloseEvent * e)" in your dialog class. The code comment will explain in detail.
Dialog::Dialog(QWidget *parent) :QDialog(parent), ui(new Ui::Dialog){
ui->setupUi(this);
}
Dialog::~Dialog(){
delete ui;
}
//SLOT
void Dialog::fnShow(){
//Show the dialog
this->show();
}
void Dialog::closeEvent(QCloseEvent *e){
QMessageBox::StandardButton resBtn = QMessageBox::question( this, "APP_NAME",
tr("Are you sure?\n"),
QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes,
QMessageBox::Yes);
if (resBtn != QMessageBox::Yes){
//Hiding the dialog when the close button clicked
this->hide();
//event ignored
e->ignore();
//Testing. To show the dialog again after 2 seconds
QTimer *qtimer = new QTimer(this);
qtimer->singleShot(2000,this,SLOT(fnShow()));
qtimer->deleteLater();
}
//below code is for understanding
//as by default it is e->accept();
else{
//close forever
e->accept();
}
}

mfc dialog opens only after mouse move when using QT

I have an interesting issue. My MFC dialog CManageDlg is calling another MFC dialog CmyMfcDlg using this call, on press of a button
void CManageDlg::OnBnClickedBt()
{
CmyMfcDlg ipmfc;
if ( ipmfc.DoModal() != IDOK )
{
return MyError;
}
}
Here is :
BOOL CmyMfcDlg ::OnInitDialog()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CDialog::OnInitDialog();
CString tmpStr;
UpdateData(FALSE);
CDC dc;
dc.Attach(::GetDC(this->m_hWnd));
int mx = dc.GetDeviceCaps(HORZRES);
int my = dc.GetDeviceCaps(VERTRES);
// lots of initializations
}
The problem is once OnBnClickedBt() is triggered by press of a button (ON_BN_CLICKED), CmyMfcDlg wait and does not open until mouse is moved! I do not know how these two are connected. I meant mouse move and opening the dialog.
EDIT1:
it turns out that this issue only happened when using QT User Interface, if I called the same function using UI written in MFC, it works fine with no problem!
Edit2:
I noticed also that this issue happened only when you open the dialog on the stack (modal) ipmfc.DoModal(), on the heap (modelless) everything is fine!

How to write a simple resident program in windows (or via Qt)

Maybe it is my English key word being wrong.
The "resident program" is a program would be zoom out in the hidden icon zone, if user press "close" or "minimized" button.
For example, the skype, audio control panel, or (high-end) mouse adjustment panel would be resident program.
May Anyone give me a windows example or keyword for windows/Qt api ?
The concept you're looking for is called the system tray icon. The QSystemTrayIcon class implements it portably.
Small example as addition to first answer:
Somewhere in constructor:
tray = new QSystemTrayIcon();
tray->setIcon(QIcon("://data/tray.png"));
//tray->setContextMenu(menu);//you can also attach menu if you want
tray->show();
Closing:
In header:
#include <QCloseEvent>
#include <QMessageBox>
#include <QSystemTrayIcon>
//...
protected:
void closeEvent(QCloseEvent *event);
private:
QSystemTrayIcon *tray;
In cpp:
void MainWindow::closeEvent(QCloseEvent *event)
{
event->ignore();
QMessageBox ms;
ms.setText(tr("Are you sure?"));
ms.setWindowTitle(tr("Title"));
ms.setWindowIcon(QIcon("://data/tray.png"));
ms.setIcon(QMessageBox::Question);
QAbstractButton *y = ms.addButton("Quit",QMessageBox::YesRole);
QAbstractButton *m = ms.addButton("Hide",QMessageBox::NoRole);
QAbstractButton *n = ms.addButton("Cancel",QMessageBox::NoRole);
ms.exec();
if(ms.clickedButton() == y)
{
tray->hide();
QCoreApplication::quit();
}
else
if(ms.clickedButton() == m) this->hide();
}

Programmatically invoke Snap/Aero maximize

Is there a way to programmatically invoke the Aera maximize effect using C or C++ for a specific window/window ID?
For example:
or
(source: thebuzzmedia.com)
I am using a border-less Qt window and Qt has an API for getting the window ID. I want to programmatically trigger the windows effects without the known triggers.
I don't want to talk about every single detail involved in achieving this effect, not only there's a lot that goes on but you also mentioned you understand the logic to place the windows at their specific locations. In this answer I'll address what I believe are the 2 main challenges:
How to receive and handle a maximize event?
How to create an approximation of the aero snap effect?
In order to answer the first question, we must analyze which event handlers are triggered when the window is maximized:
void resizeEvent(QResizeEvent* evt); // Invoked first,
void paintEvent(QPaintEvent* event); // then second,
void changeEvent(QEvent* evt); // and at last.
A Qt application is first notified of a resizeEvent(), which is followed by a paintEvent() to draw the window (or widget), and only after everything has been displayed, changeEvent() is invoked to let you know the widget was maximized (maybe it's a little bit late to receive such notification, I don't know).
Of all these, the only one we care about is resizeEvent(). This event handler informs the new window/widget size that can be used for comparison with the desktop size, thus allowing us to know if the event was actually a maximize request. Once we identify a maximize request, we can figure out whether the application should be maximized (and anchored) to right, left or to the center of the screen.
This would be the time to create the aero snap widget and place it on the screen as a visual clue to the user.
To answer the second question, I don't think is possible to call the native Windows API and ask it politely to perform this effect on your window. The only other logical choice is to write a code that approximates this effect ourselves.
The visual appearance can be replicated by drawing a transparent window with a shadow-ish border. The approach demonstrated in the source code below, creates and customizes a QWidget to make it behave and look like a aero snap window:
It's not the most beautiful thing in the world, I know. This demo creates a regular window for the user to interact with, and once it's maximized, it places itself to the left of the screen. To the right size of the screen it displays something that resembles an aero snap window (shown above).
The idea behind the aero snap widget is very simple: a QWidget with transparent background and a custom painting procedure. In other words, it's a transparent window which draws a rounded rectangle with a shadow and that's it.
To make it a bit more realistic, you should add some animation to resize the widget little by little. A for loop might do the trick, but if you need something fancy you'll end up using timers. If you take a look here, you can see the quickest & dirtiest method to perform animation with Qt in action, and better ways to deal with animation. However, for simple tasks like this, stick with frame-based animation.
main.cpp:
#include "window.h"
#include <QApplication>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}
window.h:
#pragma once
#include "snapwindow.h"
#include <QMainWindow>
#include <QEvent>
class Window : public QMainWindow
{
public:
Window();
void resizeEvent(QResizeEvent* evt);
//void paintEvent(QPaintEvent* event);
void changeEvent(QEvent* evt);
private:
SnapWindow* _sw;
};
window.cpp:
#include "window.h"
#include "snapwindow.h"
#include <QDebug>
#include <QWindowStateChangeEvent>
#include <QApplication>
#include <QDesktopWidget>
Window::Window()
{
setWindowTitle("AeroSnap");
resize(300, 300);
_sw = new SnapWindow(this);
_sw->hide();
}
void Window::changeEvent(QEvent* evt)
{
if (evt->type() == QEvent::WindowStateChange)
{
QWindowStateChangeEvent* event = static_cast<QWindowStateChangeEvent*>(evt);
if (event->oldState() == Qt::WindowNoState &&
windowState() == Qt::WindowMaximized)
{
qDebug() << "changeEvent: window is now maximized!";
}
}
}
// resizeEvent is triggered before window_maximized event
void Window::resizeEvent(QResizeEvent* evt)
{
qDebug() << "resizeEvent: request to resize window to: " << evt->size();
QSize desktop_sz = QApplication::desktop()->size();
//qDebug() << "resizeEvent: desktop sz " << desktop_sz.width() << "x" << desktop_sz.height();
// Apparently, the maximum size a window can have in my system (1920x1080)
// is actually 1920x990. I suspect this happens because the taskbar has 90px of height:
desktop_sz.setHeight(desktop_sz.height() - 90);
// If this not a request to maximize the window, don't do anything crazy.
if (desktop_sz.width() != evt->size().width() ||
desktop_sz.height() != evt->size().height())
return;
// Alright, now we known it's a maximize request:
qDebug() << "resizeEvent: maximize this window to the left";
// so we update the window geometry (i.e. size and position)
// to what we think it's appropriate: half width to the left
int new_width = evt->size().width();
int new_height = evt->size().height();
int x_offset = 10;
setGeometry(x_offset, 45, new_width/2, new_height-45); // y 45 and height -45 are due to the 90px problem
/* Draw aero snap widget */
_sw->setGeometry(new_width/2-x_offset, 0, new_width/2, new_height);
_sw->show();
// paintEvent() will be called automatically after this method ends,
// and will draw this window with the appropriate geometry.
}
snapwindow.h:
#pragma once
#include <QWidget>
class SnapWindow : public QWidget
{
public:
SnapWindow(QWidget* parent = 0);
void paintEvent(QPaintEvent *event);
};
snapwindow.cpp:
#include "snapwindow.h"
#include <QPainter>
#include <QGraphicsDropShadowEffect>
SnapWindow::SnapWindow(QWidget* parent)
: QWidget(parent)
{
// Set this widget as top-level (i.e. owned by user)
setParent(0);
/* Behold: the magic of creating transparent windows */
setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
setStyleSheet("background:transparent;");
setAttribute(Qt::WA_NoSystemBackground, true); // speed up drawing by removing unnecessary background initialization
setAttribute(Qt::WA_TranslucentBackground);
//setAutoFillBackground(true);
/* Use Qt tricks to paint stuff with shadows */
QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect();
effect->setBlurRadius(12);
effect->setOffset(0);
effect->setColor(QColor(0, 0, 0, 255));
setGraphicsEffect(effect);
}
void SnapWindow::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
/* Lazy way of painting a shadow */
QPainter painter(this);
QPen pen(QColor(180, 180, 180, 200));
pen.setWidth(3);
painter.setPen(pen);
// Offset 6 and 9 pixels so the shadow shows up properly
painter.drawRoundedRect(QRect(6, 6, (width()-1)-9, (height()-1)-9), 18, 18);
}
This is just a quick demo to point you to the right direction. It is by no means a complete implementation of the effect you are looking for.
Maybe it is not what you need, but this effect is just resizing and moving window then try use Qt methods to do this.
bool left = false;
QSize size = QApplication::desktop()->size();//resolution of current screen
if(left)
{//left side
this->setGeometry(0, 0, size.width()/2, size.height());//(maybe need do some changes)
}
else
{//right side
this->setGeometry(size.width()/2, 0, size.width()/2, size.height());
}
With QApplication::desktop() it will work properly on screen with different resolutions.
In web I found something similar in winapi, but it didn't work properly:
HWND act = GetForegroundWindow();
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
The best way
Combine this approaches. For example:
HWND act = GetForegroundWindow();
bool left = false;
QSize size = QApplication::desktop()->size();
if(left)
{
this->move(0,0);
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
this->resize(size.width()/2,QApplication::desktop()->height());
}
else
{
this->move(size.width()/2,0);
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
this->resize(size.width()/2,QApplication::desktop()->height());
}
Why? Because move() regulate left and right sides, but PostMessage (winapi) set window's height properly on every screen (window will not locate lower then taskbar, as in your example)
EDIT
I changed code a little and now it is better. Yes, it is resizing again, but now it hasn't winapi code (PostMessage etc), so Photoshop doesn't catch it, there is one interesting method in Qt which called availableGeometry. It return normal height of screen which we need, with this method borderless windows perfectly simulates Aero Snap effects in different directions. It is works, maybe don't so good, but as I can see, there isn't API for Aero effects. Maybe this approach will be normal for yoo.
There is Aero Peek in Qt : http://qt-project.org/doc/qt-5/qtwinextras-overview.html , but it is can't solve this problem too.
Code:
bool left = true;
bool upper = true;
if(upper)
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
this->setGeometry(rect);
}
else if(left)
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
rect.setWidth(rect.width()/2);
this->setGeometry(rect);
}
else
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
int half = rect.width()/2;
rect.setX(half);
rect.setWidth(half);
this->setGeometry(rect);
}
Try it with frameless window! You should choose one direction or let user choose it.