[vlc-commits] [Git][videolan/vlc][master] 6 commits: qml: add and use `MainCtx::platformHandlesResizeWithCSD()`

Steve Lhomme (@robUx4) gitlab at videolan.org
Tue Jan 28 06:28:46 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
0a467a88 by Fatih Uzunoglu at 2025-01-28T05:48:40+00:00
qml: add and use `MainCtx::platformHandlesResizeWithCSD()`

- - - - -
be610ea9 by Fatih Uzunoglu at 2025-01-28T05:48:40+00:00
qml: add and use `MainCtx::platformHandlesTitleBarButtonsWithCSD()`

- - - - -
51d337ab by Fatih Uzunoglu at 2025-01-28T05:48:40+00:00
qml: add and use `MainCtx::platformHandlesShadowsWithCSD()`

- - - - -
8ee4cf62 by Fatih Uzunoglu at 2025-01-28T05:48:40+00:00
qt: consider scale properly in `CSDWin32EventHandler::resizeBorder{Width/Height}()`

- - - - -
fca405da by Fatih Uzunoglu at 2025-01-28T05:48:40+00:00
qt: handle resize outside the client area on windows

We can use the transparent area to restrict the client
size and use that remaining extra size for resizing.

This size needs to be adjusted with regard to the dpi.

Note that on Windows 10 and Windows 7, this brings
borders.

- - - - -
1a408ca8 by Fatih Uzunoglu at 2025-01-28T05:48:40+00:00
qt: do not use `CSDWin32EventHandler` on Windows 7

- - - - -


13 changed files:

- modules/gui/qt/maininterface/compositor_dummy.cpp
- modules/gui/qt/maininterface/compositor_wayland.cpp
- modules/gui/qt/maininterface/compositor_x11.cpp
- modules/gui/qt/maininterface/compositor_x11_renderwindow.cpp
- modules/gui/qt/maininterface/compositor_x11_renderwindow.hpp
- modules/gui/qt/maininterface/interface_window_handler.cpp
- modules/gui/qt/maininterface/interface_window_handler.hpp
- modules/gui/qt/maininterface/mainctx.cpp
- modules/gui/qt/maininterface/mainctx.hpp
- modules/gui/qt/maininterface/mainctx_win32.cpp
- modules/gui/qt/maininterface/mainctx_win32.hpp
- modules/gui/qt/maininterface/qml/BannerSources.qml
- modules/gui/qt/maininterface/qml/MainInterface.qml


Changes:

=====================================
modules/gui/qt/maininterface/compositor_dummy.cpp
=====================================
@@ -21,6 +21,10 @@
 #include "maininterface/mainui.hpp"
 #include "maininterface/interface_window_handler.hpp"
 
+#ifdef _WIN32
+#include "maininterface/mainctx_win32.hpp"
+#endif
+
 namespace vlc {
 
 CompositorDummy::CompositorDummy(qt_intf_t *p_intf, QObject* parent)
@@ -45,11 +49,15 @@ bool CompositorDummy::makeMainInterface(MainCtx* mainCtx)
     QQuickWindow::setDefaultAlphaBuffer(false);
 
     m_qmlWidget = std::make_unique<QQuickView>();
-    if (m_mainCtx->useClientSideDecoration())
-        m_qmlWidget->setFlag(Qt::FramelessWindowHint);
     m_qmlWidget->setResizeMode(QQuickView::SizeRootObjectToView);
 
-    m_intfWindowHandler = std::make_unique<InterfaceWindowHandler>(m_intf, m_mainCtx, m_qmlWidget.get(), nullptr);
+    m_qmlWidget->create();
+
+#ifdef _WIN32
+    m_intfWindowHandler = std::make_unique<InterfaceWindowHandlerWin32>(m_intf, m_mainCtx, m_qmlWidget.get());
+#else
+    m_intfWindowHandler = std::make_unique<InterfaceWindowHandler>(m_intf, m_mainCtx, m_qmlWidget.get());
+#endif
 
     MainUI* ui = new MainUI(m_intf, m_mainCtx, m_qmlWidget.get(), m_qmlWidget.get());
     if (!ui->setup(m_qmlWidget->engine()))


=====================================
modules/gui/qt/maininterface/compositor_wayland.cpp
=====================================
@@ -103,8 +103,6 @@ bool CompositorWayland::makeMainInterface(MainCtx* mainCtx)
     m_mainCtx = mainCtx;
 
     m_qmlView = std::make_unique<QQuickView>();
-    if (m_mainCtx->useClientSideDecoration())
-        m_qmlView->setFlag(Qt::FramelessWindowHint);
     m_qmlView->setResizeMode(QQuickView::SizeRootObjectToView);
     m_qmlView->setColor(QColor(Qt::transparent));
 


=====================================
modules/gui/qt/maininterface/compositor_x11.cpp
=====================================
@@ -170,9 +170,7 @@ bool CompositorX11::makeMainInterface(MainCtx* mainCtx)
 {
     m_mainCtx = mainCtx;
 
-
-    bool useCSD = m_mainCtx->useClientSideDecoration();
-    m_renderWindow = std::make_unique<vlc::CompositorX11RenderWindow>(m_intf, m_conn, useCSD);
+    m_renderWindow = std::make_unique<vlc::CompositorX11RenderWindow>(m_intf, m_conn);
     if (!m_renderWindow->init())
         return false;
 


=====================================
modules/gui/qt/maininterface/compositor_x11_renderwindow.cpp
=====================================
@@ -343,14 +343,11 @@ void X11DamageObserver::onEvent()
 
 //// CompositorX11RenderWindow
 
-CompositorX11RenderWindow::CompositorX11RenderWindow(qt_intf_t* p_intf, xcb_connection_t* conn, bool useCSD, QWindow* parent)
+CompositorX11RenderWindow::CompositorX11RenderWindow(qt_intf_t* p_intf, xcb_connection_t* conn, QWindow* parent)
     : DummyRenderWindow(parent)
     , m_intf(p_intf)
     , m_conn(conn)
 {
-    if (useCSD)
-        setFlag(Qt::FramelessWindowHint);
-
     winId();
     setVisible(true);
 


=====================================
modules/gui/qt/maininterface/compositor_x11_renderwindow.hpp
=====================================
@@ -155,7 +155,7 @@ class CompositorX11RenderWindow : public DummyRenderWindow, public AccessibleRen
 
     Q_OBJECT
 public:
-    explicit CompositorX11RenderWindow(qt_intf_t* p_intf, xcb_connection_t* conn, bool useCSD, QWindow* parent = nullptr);
+    explicit CompositorX11RenderWindow(qt_intf_t* p_intf, xcb_connection_t* conn, QWindow* parent = nullptr);
     ~CompositorX11RenderWindow();
 
     bool init();


=====================================
modules/gui/qt/maininterface/interface_window_handler.cpp
=====================================
@@ -52,6 +52,9 @@ InterfaceWindowHandler::InterfaceWindowHandler(qt_intf_t *_p_intf, MainCtx* main
     assert(m_window);
     assert(m_mainCtx);
 
+    if (m_mainCtx->useClientSideDecoration())
+        m_window->setFlag(Qt::FramelessWindowHint);
+
     /* */
     m_pauseOnMinimize = var_InheritBool( p_intf, "qt-pause-minimized" );
 


=====================================
modules/gui/qt/maininterface/interface_window_handler.hpp
=====================================
@@ -64,6 +64,7 @@ signals:
 private:
     bool applyKeyEvent(QKeyEvent * event) const;
 
+protected:
     virtual void updateCSDWindowSettings();
 
 protected:


=====================================
modules/gui/qt/maininterface/mainctx.cpp
=====================================
@@ -1074,13 +1074,13 @@ bool WindowStateHolder::holdFullscreen(QWindow *window, Source source, bool hold
     else
         newflags = oldflags & ~Qt::WindowFullScreen;
 
+    window->setProperty("__windowFullScreen", QVariant::fromValue(fullscreenCounter));
+
     if( newflags != oldflags )
     {
         window->setWindowStates( newflags );
     }
 
-    window->setProperty("__windowFullScreen", QVariant::fromValue(fullscreenCounter));
-
     return fullscreenCounter != 0;
 }
 


=====================================
modules/gui/qt/maininterface/mainctx.hpp
=====================================
@@ -328,6 +328,10 @@ public:
         return 0.0;
     }
 
+    Q_INVOKABLE virtual bool platformHandlesResizeWithCSD() const { return false; };
+    Q_INVOKABLE virtual bool platformHandlesTitleBarButtonsWithCSD() const { return false; };
+    Q_INVOKABLE virtual bool platformHandlesShadowsWithCSD() const { return false; };
+
     /**
      * @brief ask for the application to terminate
      */


=====================================
modules/gui/qt/maininterface/mainctx_win32.cpp
=====================================
@@ -125,25 +125,36 @@ public:
         , m_useClientSideDecoration {mainctx->useClientSideDecoration()}
         , m_window {window}
         , m_buttonmodel {mainctx->csdButtonModel()}
+        , m_mainCtx(mainctx)
+        , m_platformHandlesResize(m_mainCtx->platformHandlesResizeWithCSD())
     {
+        assert(m_mainCtx);
+
+        // CSDWin32EventHandler does not support Qt::FramelessWindowHint:
+        m_window->setFlag(Qt::FramelessWindowHint, false);
+
         QApplication::instance()->installNativeEventFilter(this);
         updateCSDSettings();
     }
 
     static int resizeBorderWidth(QWindow *window)
     {
+        // NOTE: When possible, Qt makes the application DPI aware, so `GetSystemMetrics()` would
+        //       already take the screen DPI into account.
         const int result = GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
         if (result > 0)
-            return qRound(static_cast<qreal>(result) / window->devicePixelRatio());
+            return qRound(static_cast<qreal>(result) * (window->devicePixelRatio() / window->screen()->devicePixelRatio()));
         else
             return qRound(static_cast<qreal>(8) * window->devicePixelRatio());
     }
 
     static int resizeBorderHeight(QWindow *window)
     {
+        // NOTE: Qt makes the application DPI aware, so `GetSystemMetrics()` would already take
+        //       already take the screen DPI into account.
         const int result = GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
         if (result > 0)
-            return qRound(static_cast<qreal>(result) / window->devicePixelRatio());
+            return qRound(static_cast<qreal>(result) * (window->devicePixelRatio() / window->screen()->devicePixelRatio()));
         else
             return qRound(static_cast<qreal>(8) * window->devicePixelRatio());
     }
@@ -200,6 +211,24 @@ public:
                 clientRect->right -= rbw;
                 nonClientAreaExists = true;
             }
+            else if (m_platformHandlesResize && !IsZoomed(msg->hwnd))
+            {
+                // Resize outside client area:
+
+                // Before entering to fullscreen, Windows sends a WM_NCCALCSIZE message to determine
+                // the window geometry for fullscreen. In this case, we should not make an adjustment
+                // so that the window gets placed appropriately. At the same time, when getting
+                // restored from full screen, we should make this adjustment (state is still fullscreen):
+                if (m_window->property("__windowFullScreen").toInt() == 0) // not about to enter full screen OR about to restore from full screen OR stateless window
+                {
+                    // Do not adjust top, top is resized within the caption area. This is the behavior of SSD as well.
+                    const int resizeWidth = resizeBorderWidth(m_window);
+                    clientRect->left += resizeWidth;
+                    clientRect->right -= resizeWidth;
+                    clientRect->bottom -= resizeBorderHeight(m_window);
+                    nonClientAreaExists = true;
+                }
+            }
 
             *result = nonClientAreaExists ? 0 : WVR_REDRAW;
             return true;
@@ -228,19 +257,57 @@ public:
             // Map the point to client coordinates.
             ::MapWindowPoints(nullptr, msg->hwnd, &point, 1);
 
-            // exclude resize handle area
-            if ((m_window->windowState() != Qt::WindowFullScreen)
-                && (point.y < resizeBorderHeight(m_window)
-                    || point.x > (m_window->width() * m_window->devicePixelRatio() - resizeBorderWidth(m_window))))
-                return false;
-
             const double scaleFactor = m_window->devicePixelRatio();
 
             //divide by scale factor as buttons coordinates will be in dpr
             const QPoint qtPoint {static_cast<int>(point.x / scaleFactor), static_cast<int>(point.y / scaleFactor)};
             auto button = overlappingButton(qtPoint);
             if (!button)
+            {
+                if (!m_platformHandlesResize)
+                    return false;
+
+                if (!IsZoomed(msg->hwnd) && (m_window->windowState() != Qt::WindowFullScreen))
+                {
+                    // Top is resized within the caption area (even though it is client area), but still
+                    // handled by the platform. This is also the behavior with SSD and we can not have
+                    // the resize handle outside the window frame for the top side, because the top side
+                    // does not have transparent area but rather system caption is shown instead:
+
+                    int resizeHandleSize;
+                    const double intfScaleFactor = m_mainCtx->getIntfScaleFactor();
+                    const int unscaledResizeHandleSize = resizeBorderHeight(m_window);
+                    if (intfScaleFactor < 1.0)
+                    {
+                        // Make the top resize handle smaller if necessary, but not bigger (as the other edge
+                        // handles can't be made bigger), if the interface scale factor is small so that the
+                        // buttons in the caption area can be clicked more comfortably. This does not apply
+                        // to the minimize/maximize/close buttons, in that case resize handle should be the
+                        // minimal size regardless of the interface scale:
+                        resizeHandleSize = std::max<int>(2, unscaledResizeHandleSize * m_mainCtx->getIntfScaleFactor());
+                    }
+                    else
+                        resizeHandleSize = unscaledResizeHandleSize;
+
+                    if (point.y >= 0 && point.y <= resizeHandleSize)
+                    {
+                        const auto windowWidth = m_window->width() * m_window->devicePixelRatio();
+                        if (point.x >= -unscaledResizeHandleSize && point.x <= resizeHandleSize)
+                            *result = HTTOPLEFT;
+                        else if ((point.x >= windowWidth - resizeHandleSize) && (point.x <= windowWidth + unscaledResizeHandleSize))
+                            *result = HTTOPRIGHT;
+                        else if ((point.x > resizeHandleSize) && (point.x < windowWidth - resizeHandleSize))
+                            *result = HTTOP;
+                        else
+                            return false;
+
+                        // The rest (left, right, bottom) is handled by the system outside the client area
+                        // Only top resize should be within the client area, as that area corresponds to the caption.
+                        return true;
+                    }
+                }
                 return false;
+            }
 
             switch (button->type())
             {
@@ -325,7 +392,7 @@ public:
                 break;
             default:
                 resetPressedState();
-                break;
+                return false;
             }
 
 
@@ -359,6 +426,12 @@ public:
 private:
     void updateClientFrame()
     {
+        // DwmExtendFrameIntoClientArea() is not necessary when WS_POPUP is not
+        // used and platform handles resize (outside), where there is non-client
+        // area
+        if (m_platformHandlesResize)
+            return;
+
         auto hwnd = (HWND)m_window->winId();
 
         MARGINS margin {};
@@ -396,7 +469,9 @@ private:
     {
         for (auto button : m_buttonmodel->windowCSDButtons())
         {
-            if (button->rect().contains(point))
+            QRect rect = button->rect();
+            rect.setY(rect.y() + 2); // leave a small gap for top resize which is always within the caption area
+            if (rect.contains(point))
                 return button;
         }
         return nullptr;
@@ -446,6 +521,8 @@ private:
     QWindow *m_window;
     CSDButtonModel *m_buttonmodel;
     bool m_trackingMouse = false;
+    MainCtx *m_mainCtx = nullptr;
+    const bool m_platformHandlesResize;
 };
 
 }
@@ -721,8 +798,14 @@ bool MainCtxWin32::getDisableVolumeKeys() const
 
 InterfaceWindowHandlerWin32::InterfaceWindowHandlerWin32(qt_intf_t *_p_intf, MainCtx* mainCtx, QWindow* window, QObject *parent)
     : InterfaceWindowHandler(_p_intf, mainCtx, window, parent)
-    , m_CSDWindowEventHandler(new CSDWin32EventHandler(mainCtx, window, window))
 {
+    assert(mainCtx);
+    if (mainCtx->platformHandlesResizeWithCSD() || mainCtx->platformHandlesShadowsWithCSD())
+    {
+        assert(mainCtx->platformHandlesResizeWithCSD() && mainCtx->platformHandlesShadowsWithCSD());
+        m_CSDWindowEventHandler = new CSDWin32EventHandler(mainCtx, window, window);
+    }
+
     auto mainCtxWin32 = qobject_cast<MainCtxWin32*>(mainCtx);
     assert(mainCtxWin32);
     m_disableVolumeKeys = mainCtxWin32->getDisableVolumeKeys();
@@ -910,5 +993,8 @@ bool InterfaceWindowHandlerWin32::eventFilter(QObject* obj, QEvent* ev)
 
 void InterfaceWindowHandlerWin32::updateCSDWindowSettings()
 {
-    static_cast<CSDWin32EventHandler *>(m_CSDWindowEventHandler)->setUseClientSideDecoration(m_mainCtx->useClientSideDecoration());
+    if (m_CSDWindowEventHandler)
+        static_cast<CSDWin32EventHandler *>(m_CSDWindowEventHandler)->setUseClientSideDecoration(m_mainCtx->useClientSideDecoration());
+    else
+        InterfaceWindowHandler::updateCSDWindowSettings();
 }


=====================================
modules/gui/qt/maininterface/mainctx_win32.hpp
=====================================
@@ -28,6 +28,7 @@
 #include "player/player_controller.hpp"
 #include "interface_window_handler.hpp"
 #include <QAbstractNativeEventFilter>
+#include <QOperatingSystemVersion>
 #include <wrl/client.h>
 
 #include <objbase.h>
@@ -87,6 +88,9 @@ public:
 public:
     bool getDisableVolumeKeys() const;
 
+    Q_INVOKABLE bool platformHandlesShadowsWithCSD() const override { return (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8); };
+    Q_INVOKABLE bool platformHandlesResizeWithCSD() const override { return (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8); };
+
 public slots:
     void reloadPrefs() override;
 


=====================================
modules/gui/qt/maininterface/qml/BannerSources.qml
=====================================
@@ -202,7 +202,7 @@ T.ToolBar {
                         rightMargin: VLCStyle.applicationHorizontalMargin
                     }
                     height: VLCStyle.globalToolbar_height
-                    active: root._showCSD
+                    active: root._showCSD && !MainCtx.platformHandlesTitleBarButtonsWithCSD()
                     source: VLCStyle.palette.hasCSDImage
                               ? "qrc:///qt/qml/VLC/Widgets/CSDThemeButtonSet.qml"
                               : "qrc:///qt/qml/VLC/Widgets/CSDWindowButtonSet.qml"


=====================================
modules/gui/qt/maininterface/qml/MainInterface.qml
=====================================
@@ -310,7 +310,7 @@ Item {
         Loader {
             active: {
                 const windowVisibility = MainCtx.intfMainWindow.visibility
-                return MainCtx.clientSideDecoration
+                return MainCtx.clientSideDecoration && !MainCtx.platformHandlesResizeWithCSD()
                         && (windowVisibility !== Window.Maximized)
                         && (windowVisibility !== Window.FullScreen)
 
@@ -332,7 +332,7 @@ Item {
         z: -1
         hollow: Window.window && (Window.window.color.a < 1.0) // the interface may be translucent if the window has backdrop blur
         blending: false // stacked below everything, no need for blending even though it is not opaque
-        visible: _extendedFrameVisible
+        visible: _extendedFrameVisible && !MainCtx.platformHandlesShadowsWithCSD()
         anchors.fill: g_mainInterface
         spread: 0.0
         color: "black"



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/ad324b1a7a69bf939756067bd17609b5b3c76aab...1a408ca871366a9099445e007df7479b830a8ea1

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/ad324b1a7a69bf939756067bd17609b5b3c76aab...1a408ca871366a9099445e007df7479b830a8ea1
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list