Qt C++ window title bar is blocked - c++

I'm very new to Qt and trying to create application, that includes main window, QDockWidget and a button.
Suppose my main window has 1280 x 720 resolution. Then I want to implement QDockWidget that pop up from the left side, width of dockWidth and height of 720 without windowTitleBar. The button has size of (buttonWidth, 720). At first its hidden, and only the button is present, when we click the button dock pops up, button changes position to the right edge of dock.
Here is my code:
window::window(unsigned int h, unsigned int v, QWidget *parent) {
this->setFixedSize(h, v);
ui.setupUi(this);
createDockWindow();
}
void window::createDockWindow() {
dock = new QDockWidget(this);
dock->setTitleBarWidget(new QMainWindow());
dock->setGeometry(QRect(this->rect().topLeft(),
QSize(dockWidth, this->height())));
dock->setFloating(true);
dock->hide();
path_button = new QPushButton(">", this);
path_button->setGeometry(QRect(this->rect().topLeft(),
QSize(buttonWidth, this->height())));
connect(path_button, SIGNAL (released()), this, SLOT (showDock()));
}
void rubrick::showDock() {
if(dock->isHidden()){
dock->show();
path_button->setGeometry(QRect(dock->rect().topRight(),
QSize(buttonWidth, this->height())));
} else {
dock->hide();
path_button->setGeometry(QRect(dock->rect().topLeft(),
QSize(buttonWidth, this->height())));
}
}
So button works perfectly, at first my app looks like that screenshot:
But when the dock shows, it blocks app window title bar, like that: screenshot
I figured, this->rect().topLeft() returns top left of screen, but doesn't take into consideration window Title Bar, I tried to get menuBar height, but it return 30, and I found out that if I move left top by (0, 45) with 0 being width and 45 being height, the dock would be perfectly in place.
What am I doing wrong and how to fix that problem?

The method you're probably looking for is QWidget::frameGeometry, which returns the geometry of the window with the frame included. The rect method returns only the internal area. If you look at QWidget::rect in Qt Assistant, you'll find a link to a "Window Geometry" description that explains all of these interactions reasonably well.

Related

How to change the color of window border and title bar background programmatically when using DWM in win32?

I want to customize the window frame using DWM in win32 to get an appearance like VS2022 (draw menu on title bar) or Chrome (draw tabs on title bar), so I use the function DwmExtendFrameIntoClientArea with margin {0, 0, caption height, 0}. Unfortunately, it shows a white window border with 1-pixel width and white title bar area. I use the function DwmDefWindowProc to handle the system messages and show the system control buttons (min, max, close button). But I have no idea how to change the color of the window border and title bar background. If I paint in WM_PAINT, it may cover the system control buttons and looks weird. If I paint in WM_NCPAINT, the window shadow with WS_THICKFRAME style may disappear and need to draw which I feel is difficult to do.
Additionally, the 1-pixel width border may come from the following code:
if (message == WM_NCCALCSIZE) {
auto client_area_needs_calculating = static_cast<bool>(wparam);
if (client_area_needs_calculating) {
auto parameters = reinterpret_cast<NCCALCSIZE_PARAMS*>(lparam);
auto& requested_client_area = parameters->rgrc[0];
requested_client_area.right -= GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
requested_client_area.left += GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
requested_client_area.bottom -= GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
return 0;
}
}

not calling mousemove event on widgets and graphics view

I have created a set of wizard pages inherited from qwizardpage. in one of my wizard page i have a graphics view and in that using graphics scene and a qrect a rectangle has been created and now i am trying to catch the callback event of mouse move when the mouse is in the graphicsview area, but outside this area only call back is happening. But mouse press event is functioning properly. could anyone suggest me any solution.
WizardPage::WizardPage(QWidget* parent)
{
m_pScene = new QGraphicsScene(this);
graphicsView->setScene(m_pScene);
m_pRect = new QRect(-25, 25, 100, 40);
m_pScene->addRect(*m_pRect);
setMouseTracking(true);
}
void EgoLeverWizardPage::mouseMoveEvent(QMouseEvent *event)
{
qDebug() << "move";
}

Qt5 QDockWidget with Fixed Height CentralWidget

I'm trying to use dock widgets on the main window class in Qt5. However, when I set the central widget to have a fixed height Qt has trouble docking the windows to the top or bottom. Basically, it looks like there is some "padding" or "margins" above and below the central widget. If I set an auto height on the widget, the docking works fine all they way edge-to-edge (top/bottom). How can I either remove the margins or enable the docking function while using a fixed height central widget?
See screenshots for example.
Dock Right w/ Auto Height (No Margins on Central Widget)
Dock Bottom w/ Auto Height (No Margins on Central Widget)
Dock Bottom w/ Fixed Height (Margins/Padding--Grey areas, won't dock)
Here is the code if that helps.
Header:
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QString);
~MainWindow();
private:
void createDockWindows();
QListWidget *m_dock_list;
QString m_directory;
QWidget *m_mainWidget;
};
Class definition:
MainWindow::MainWindow(QString program)
: m_directory(".")
{
m_mainWidget = new QWidget;
m_mainWidget->setFixedHeight(156);
m_mainWidget->setStyleSheet("background-color: blue;");
createDockWindows();
// set central widget and default size
setCentralWidget(m_mainWidget);
}
// dock functions
void MainWindow::createDockWindows()
{
QDockWidget *dock = new QDockWidget(tr("Dock List"), this);
dock->setAllowedAreas(Qt::LeftDockWidgetArea |
Qt::RightDockWidgetArea |
Qt::BottomDockWidgetArea);
m_dock_list = new QListWidget(dock);
m_dock_list->addItems(QStringList()
<< "item 1"
<< "item 2"
<< "item 3"
<< "item 4");
dock->setWidget(m_dock_list);
addDockWidget(Qt::RightDockWidgetArea, dock);
}
In case anyone else tries to figure out this behavior, here is what I've found. The top and bottom margins are just Qt trying to place the fixed size widget into a space larger than the widget (obvious). However, the restricted docking ability results from the docking widget having a minimum vertical size. Thus, when window height < central widget height + min(docking widget height) Qt does not allow the dock function. Once the threshold window height is reached (by user resize) then Qt allows the dock function.
I would prefer if the docking feature occurred regardless of window height with an automatic window resize to accommodate the docking window.
Solution using signals
Arguably a bit hacky, and may be hard to maintain if you have a complex layout. But for my use case, this is workable.
Don't enforce the size on the center widget, let it auto-fill the main window.
When the dock changes location it emits the dockLocationChanged. We can auto-size the main window if the dockable window is moved into target location. For my case this is the bottom.
Code:
void MainWindow::createDockWindows()
{
...
QObject::connect(dock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
this, SLOT(s_dock_window_resize(Qt::DockWidgetArea)));
}
// Autosize window slot on dock location change.
void MainWindow::s_dock_window_resize(Qt::DockWidgetArea area)
{
if (area == Qt::DockWidgetArea::BottomDockWidgetArea) {
// m_mainWidget_min_height is the height we would have enforced
// on the widget. Now we set it somewhere else and check it here.
int min_height = m_dock_list->height() + m_mainWidget_min_height;
if (min_height > MainWindow::height()) {
MainWindow::resize(MainWindow::width(), min_height);
}
}
}

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.

Resizing parent window after child sizes change

I have been messing with this problem for hours, and decided it's time to ask SO :)
I have a Qt program that rotates an image and then updates the size of the widget. Here is the code I'm using to do this currently.
void VideoSubWindow::showFrame(const QImage& frame)
{
QPixmap pixmap = QPixmap::fromImage(frame);
ui->videoFrameLabel->setPixmap(pixmap);
resizeWidgets(pixmap.size());
}
void VideoSubWindow::resizeWidgets(const QSize &size)
{
if(frameSize != size)
{
frameSize = size;
ui->videoFrameLabel->setFixedSize(size);
ui->scrollArea->setMinimumSize(size.width() + 2, size.height() + 2);
}
}
The widgets are structured as follows:
VideoSubWindow (QMainWindow)
-> centralWidget (QWidget) (Vertical layout is set on this)
-> scrollArea (QScrollArea)
-> videoFrameLabel (QLabel)
-> statusBar (QStatusBar)
-> menuBar (QMenuBar)
When the code above is executed, like rotating the image 90 degrees, the image will be rotated, but the window doesn't resize to fit the new pixmap size. I have tried to call adjustSize() and updateGeometry() on SubWindow and centralWidget, but those seem to have zero effect. But, if I manually resize the window with my mouse, the window snaps to the minimum size that was set for the scrollArea, so that seems to be taking effect.
Does anyone have experience with this? Thanks!
Try with the resize(...) function : Qt documentation
adjustSize() used sizeHint() function, so calling adjustSize() on SubWindow and centralWidget cannot have any effet