Customize window frame
-
No, it's not. QStyle is for providing custom styles to the widgets. Window frame is an OS specific thing (some OSs don't have frames or windows) and is not drawn by Qt.
On Windows you need to dive a little lower level than Qt offers, by responding to the native messages your window is getting. Implement the "nativeEvent":http://qt-project.org/doc/qt-5.0/qtwidgets/qwidget.html#nativeEvent and handle messages like "WM_NCPAINT":http://msdn.microsoft.com/pl-pl/library/windows/desktop/dd145212.aspx and "WM_NCHITTEST":http://msdn.microsoft.com/en-us/library/windows/desktop/ms645618.aspx
Google for "drawing in non-client area", just keep in mind that it's not a Qt specific task.Other approach is disabling window frame entirely by setting Qt::FramelessWindowHint flag on your widget, and drawing the frame and buttons yourself. This gives you the ultimate control of the look of the frame, but is also a lot of work, because you need to handle all the windowing behavior like resizing, moving, maximizing or Aero Shake yourself.
-
You don't really need to go that deep into the OS.
Use "QWidget::setWindowFlags()":http://qt-project.org/doc/qt-4.8/qwidget.html#windowFlags-prop and set it to Qt::Window | Qt::FramelessWindowHint on your top-level widget.
Now you need to do all the painting and handling (moving, resizing, minimizing, maximizing, closing) yourself, but this shouldn't be that hard to handle the specific events.EDIT: oops...sorry Krzysztof Kawa overread your last paragraph.
-
I dont know if this helps, but may be you can have a look at "Qt Tutorial: Creating a custom window":http://qt.developpez.com/tutoriels/braindeadbzh/customwindow/ , its in French just open the link in google chrome and it will translate to English.
-
#include "mainwindow.h" #ifdef Q_OS_WIN #include <WinUser.h> #include <windowsx.h> #include <dwmapi.h> #include <gdiplus.h> #include <GdiPlusColor.h> #endif MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { setMinimumSize(450,405); } void MainWindow::showEvent(QShowEvent *event) { QWidget::showEvent(event); #ifdef Q_OS_WIN window_borderless(); #endif } #ifdef Q_OS_WIN void MainWindow::window_borderless() { if (isVisible()) { //defaultStyle = (WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME) SetWindowLongPtr(winId(), GWL_STYLE, WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX ); window_shadow(); SetWindowPos(winId(), 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE); } } void MainWindow::window_shadow() { const MARGINS shadow = { 1, 1, 1, 1 }; DwmExtendFrameIntoClientArea(winId(), &shadow); } bool MainWindow::winEvent(MSG *msg, long *result) { switch (msg->message) { case WM_NCCALCSIZE: { //this kills the window frame and title bar we added with //WS_THICKFRAME and WS_CAPTION *result = 0; return true; break; } case WM_NCHITTEST: { *result = 0; const LONG border_width = 8; //in pixels RECT winrect; GetWindowRect(winId(), &winrect); long x = GET_X_LPARAM(msg->lParam); long y = GET_Y_LPARAM(msg->lParam); bool resizeWidth = minimumWidth() != maximumWidth(); bool resizeHeight = minimumHeight() != maximumHeight(); if(resizeWidth) { //left border if (x >= winrect.left && x < winrect.left + border_width) { *result = HTLEFT; } //right border if (x < winrect.right && x >= winrect.right - border_width) { *result = HTRIGHT; } } if(resizeHeight) { //bottom border if (y < winrect.bottom && y >= winrect.bottom - border_width) { *result = HTBOTTOM; } //top border if (y >= winrect.top && y < winrect.top + border_width) { *result = HTTOP; } } if(resizeWidth && resizeHeight) { //bottom left corner if (x >= winrect.left && x < winrect.left + border_width && y < winrect.bottom && y >= winrect.bottom - border_width) { *result = HTBOTTOMLEFT; } //bottom right corner if (x < winrect.right && x >= winrect.right - border_width && y < winrect.bottom && y >= winrect.bottom - border_width) { *result = HTBOTTOMRIGHT; } //top left corner if (x >= winrect.left && x < winrect.left + border_width && y >= winrect.top && y < winrect.top + border_width) { *result = HTTOPLEFT; } //top right corner if (x < winrect.right && x >= winrect.right - border_width && y >= winrect.top && y < winrect.top + border_width) { *result = HTTOPRIGHT; } } //TODO: allow move? if(*result==0) *result = HTCAPTION ; return true; break; } //end case WM_NCHITTEST case WM_CLOSE: { return close(); break; } default: return QWidget::winEvent(msg,result); } return false; } #endif
-
Interesting @TDHound. A bit beyond my understanding, but I guess you can do anything with the native window exposed if you know what you are doing.
I tried out your code. I had to make some adjustments to make it work:
#ifdef Q_OS_WIN #include <windows.h> // Fixes error C1189: #error : "No Target Architecture" #include <WinUser.h> #include <windowsx.h> #include <dwmapi.h> #include <objidl.h> // Fixes error C2504: 'IUnknown' : base class undefined #include <gdiplus.h> #include <GdiPlusColor.h> #pragma comment (lib,"Dwmapi.lib") // Adds missing library, fixes error LNK2019: unresolved external symbol __imp__DwmExtendFrameIntoClientArea #endif
Qt4
This is what my result looks like in Qt4Qt5
Any idea if it can work in Qt 5 as well? "winEvent()" has been replaced in Qt5 with nativeEvent(), and I made it compile by making some changes.bool MainWindow::nativeEvent(const QByteArray & eventType, MSG *msg, long *result) // Replaces winEvent()
return QWidget::nativeEvent(eventType, msg, result); // Replaces winEvent()
However it doesn't give the intended result.
-
-
Whenever you use the FramelessWindowHint, you lose all Windows frame related features, such as docking, shortcuts, maximizing, etc.
You can implement some of them yourself, with great effort in some cases, other things will still just not work. Example: A frameless window cannot be maximized, it will instead go fullscreen, hiding the Windows task bar.
It's all a mess. -
That's why you explicitly override the native window style flags after setting Qt::FramelessWIndowHint. By restoring the style flags and handling the related native frame (nonclient area in Windows terms) events, you can have a custom frame (or no frame) and still retain the normal window behavior.