Customize window frame



  • Is it somehow possible to customize the window frame?

    Such as changing the color to make it more similar to the new look of Photoshop and Visual Studio 2012.

    Customize Qt window frame



  • Probably QStyle class is what you are looking for.


  • Moderators

    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.



  • Uuups, I misunderstood the problem. Thanks for clearing this!



  • Thanks! A bit harder than I had anticipated, but glad to know it's possible, even though it's OS specific.


  • Moderators

    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 Qt4

    Framless native windows Qt4

    Qt5
    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.

    Framless windows Qt5





  • 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.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.