[vlc-commits] [Git][videolan/vlc][master] 8 commits: qt: add extended frame capability to compositor

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Sat Jul 22 11:03:16 UTC 2023



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
fbcb6c31 by Pierre Lamot at 2023-07-22T10:48:01+00:00
qt: add extended frame capability to compositor

this allows to define a non-client area to draw things like window shadows when
using CSD

- - - - -
0451411b by Pierre Lamot at 2023-07-22T10:48:01+00:00
qt: set extended frame when available on X11

- - - - -
72e63e36 by Pierre Lamot at 2023-07-22T10:48:01+00:00
qt: account for extended frame in minimal window size

- - - - -
c861c002 by Pierre Lamot at 2023-07-22T10:48:01+00:00
qml: move g_mainInterface a an inner item in the window

no functional change, this is preliminary work for the following patch

- - - - -
11258b08 by Pierre Lamot at 2023-07-22T10:48:01+00:00
qml: draw CSD window shadow, when extended frames is supported

- - - - -
747116e3 by Pierre Lamot at 2023-07-22T10:48:01+00:00
qml: avoid popup overlay background to bleed into the extended area

Overlay.overlay is a root node, it isn't aware of our extended area

- - - - -
db1a1e67 by Pierre Lamot at 2023-07-22T10:48:01+00:00
qml: specify the target explicitly in CSDMouseStealer

don't rely on global id

- - - - -
e3dea257 by Pierre Lamot at 2023-07-22T10:48:01+00:00
qml: allow CSDMouseStealer areas to be placed outside the target item

- - - - -


13 changed files:

- modules/gui/qt/dialogs/dialogs/qml/ModalDialog.qml
- modules/gui/qt/maininterface/compositor.cpp
- modules/gui/qt/maininterface/compositor.hpp
- 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/compositor_x11_utils.cpp
- modules/gui/qt/maininterface/compositor_x11_utils.hpp
- modules/gui/qt/maininterface/interface_window_handler.cpp
- modules/gui/qt/maininterface/mainctx.cpp
- modules/gui/qt/maininterface/mainctx.hpp
- modules/gui/qt/maininterface/qml/MainInterface.qml
- modules/gui/qt/widgets/qml/CSDMouseStealer.qml


Changes:

=====================================
modules/gui/qt/dialogs/dialogs/qml/ModalDialog.qml
=====================================
@@ -50,13 +50,21 @@ Dialog {
         colorSet: ColorContext.Window
     }
 
-    Overlay.modal: GaussianBlur {
-        source: ShaderEffectSource {
-            sourceItem: control.rootWindow
-            live: true
+    Overlay.modal: Item {
+        GaussianBlur {
+            anchors.fill: parent
+            anchors.topMargin: MainCtx.windowExtendedMargin
+            anchors.leftMargin: MainCtx.windowExtendedMargin
+            anchors.rightMargin: MainCtx.windowExtendedMargin
+            anchors.bottomMargin: MainCtx.windowExtendedMargin
+
+            source: ShaderEffectSource {
+                sourceItem: control.rootWindow
+                live: true
+            }
+            radius: 12
+            samples: 16
         }
-        radius: 12
-        samples: 16
     }
 
     background: Rectangle {


=====================================
modules/gui/qt/maininterface/compositor.cpp
=====================================
@@ -260,6 +260,7 @@ bool CompositorVideo::commonGUICreateImpl(QWindow* window, QWidget* rootWidget,
     m_interfaceWindowHandler = std::make_unique<InterfaceWindowHandler>(m_intf, m_mainCtx,  window, rootWidget);
 #endif
     m_mainCtx->setHasAcrylicSurface(flags & CompositorVideo::HAS_ACRYLIC);
+    m_mainCtx->setWindowSuportExtendedFrame(flags & CompositorVideo::HAS_EXTENDED_FRAME);
 
 #ifdef _WIN32
     m_taskbarWidget = std::make_unique<WinTaskbarWidget>(m_intf, window);


=====================================
modules/gui/qt/maininterface/compositor.hpp
=====================================
@@ -92,7 +92,8 @@ public:
     enum Flag : unsigned
     {
         CAN_SHOW_PIP = 1,
-        HAS_ACRYLIC = 2
+        HAS_ACRYLIC = 2,
+        HAS_EXTENDED_FRAME = 4,
     };
     Q_DECLARE_FLAGS(Flags, Flag)
 


=====================================
modules/gui/qt/maininterface/compositor_x11.cpp
=====================================
@@ -194,6 +194,8 @@ bool CompositorX11::makeMainInterface(MainCtx* mainCtx)
     CompositorVideo::Flags flags = CompositorVideo::CAN_SHOW_PIP;
     if (m_renderWindow->hasAcrylic())
         flags |= CompositorVideo::HAS_ACRYLIC;
+    if (m_renderWindow->supportExtendedFrame())
+        flags |= CompositorVideo::HAS_EXTENDED_FRAME;
     commonGUICreate(m_renderWindow.get(), nullptr, m_qmlView.get(), flags);
 
     m_renderWindow->setInterfaceWindow(m_qmlView.get());


=====================================
modules/gui/qt/maininterface/compositor_x11_renderwindow.cpp
=====================================
@@ -37,9 +37,11 @@
 #include <vlc_cxx_helpers.hpp>
 
 #include "qt.hpp"
+#include "mainctx.hpp"
 
 //blur behind for KDE
 #define _KDE_NET_WM_BLUR_BEHIND_REGION_NAME "_KDE_NET_WM_BLUR_BEHIND_REGION"
+#define _GTK_FRAME_EXTENTS "_GTK_FRAME_EXTENTS"
 
 using namespace vlc;
 
@@ -74,7 +76,7 @@ void RenderTask::render(unsigned int requestId)
     xcb_flush(m_conn);
     xcb_render_picture_t drawingarea = getBackTexture();
 
-    if (m_hasAcrylic)
+    if (m_hasAcrylic || m_hasExtendedFrame)
     {
         //clear screen
         xcb_render_color_t clear = { 0x0000, 0x0000, 0x0000, 0x0000 };
@@ -180,6 +182,12 @@ void RenderTask::onAcrylicChanged(bool enabled)
     m_hasAcrylic = enabled;
 }
 
+void RenderTask::onExtendedFrameChanged(bool enabled)
+{
+    m_hasExtendedFrame = enabled;
+}
+
+
 xcb_render_picture_t RenderTask::getBackTexture()
 {
     if (m_drawingarea && !m_resizeRequested)
@@ -352,6 +360,14 @@ bool CompositorX11RenderWindow::init()
     }
 
     xcb_connection_t* qtConn = QX11Info::connection();
+    xcb_window_t rootWindow = QX11Info::appRootWindow();
+    int appScreen = QX11Info::appScreen();
+
+    //if we don't have compositor, transparency effects won't work.
+    //Note that composite extention may be available and functionnal without a compositor,
+    //so having video compositing doesn't imply that window transparency is available
+    if (!wmScreenHasCompositor(qtConn, appScreen))
+        return true;
 
     //check if KDE "acrylic" effect is available
     xcb_atom_t blurBehindAtom = getInternAtom(qtConn, _KDE_NET_WM_BLUR_BEHIND_REGION_NAME);
@@ -362,6 +378,15 @@ bool CompositorX11RenderWindow::init()
                             blurBehindAtom, XCB_ATOM_CARDINAL, 32, 1, &val);
         m_hasAcrylic = true;
     }
+
+    //_GTK_FRAME_EXTENTS should be available at least on Gnome/KDE/FXCE/Enlightement
+    xcb_atom_t gtkExtendFrame = getInternAtom(qtConn, _GTK_FRAME_EXTENTS);
+    if (gtkExtendFrame != XCB_ATOM_NONE && wmNetSupport(qtConn, rootWindow, gtkExtendFrame))
+    {
+        m_supportExtendedFrame = true;
+        connect(m_intf->p_mi, &MainCtx::windowExtendedMarginChanged,
+               this, &CompositorX11RenderWindow::onWindowExtendedMarginChanged);
+    }
     return true;
 }
 
@@ -385,12 +410,13 @@ bool CompositorX11RenderWindow::startRendering()
     connect(this, &CompositorX11RenderWindow::videoPositionChanged, m_renderTask, &RenderTask::onVideoPositionChanged);
     connect(this, &CompositorX11RenderWindow::registerVideoWindow, m_renderTask, &RenderTask::onRegisterVideoWindow);
     connect(this, &CompositorX11RenderWindow::videoSurfaceChanged, m_renderTask, &RenderTask::onVideoSurfaceChanged, Qt::BlockingQueuedConnection);
-
+    connect(this, &CompositorX11RenderWindow::hasExtendedFrameChanged, m_renderTask, &RenderTask::onExtendedFrameChanged, Qt::BlockingQueuedConnection);
     //pass initial values
     m_renderTask->onInterfaceSurfaceChanged(m_interfaceClient.get());
     m_renderTask->onVideoSurfaceChanged(m_videoClient.get());
     m_renderTask->onWindowSizeChanged(size() * devicePixelRatio());
     m_renderTask->onAcrylicChanged(m_hasAcrylic);
+    m_renderTask->onExtendedFrameChanged(m_hasExtendedFrame);
 
     //use the same thread as the rendering thread, neither tasks are blocking.
     m_damageObserver->moveToThread(m_renderThread);
@@ -525,5 +551,26 @@ void CompositorX11RenderWindow::setInterfaceWindow(CompositorX11UISurface* windo
     xcb_flush(QX11Info::connection());
     m_interfaceClient = std::make_unique<CompositorX11RenderClient>(m_intf, m_conn, window);
     m_interfaceWindow = window;
+}
 
+
+void CompositorX11RenderWindow::setHasExtendedFrame(bool hasExtendedFrame)
+{
+    if (hasExtendedFrame == m_hasExtendedFrame)
+        return;
+    m_hasExtendedFrame = hasExtendedFrame;
+    emit hasExtendedFrameChanged(m_hasExtendedFrame);
 }
+
+void CompositorX11RenderWindow::onWindowExtendedMarginChanged(unsigned margin)
+{
+    xcb_atom_t gtkExtendFrame = getInternAtom(m_conn, _GTK_FRAME_EXTENTS);
+
+    margin *= m_intf->p_mi->intfMainWindow()->devicePixelRatio();
+
+    uint32_t val[4] = {margin, margin, margin, margin};
+    xcb_change_property(m_conn, XCB_PROP_MODE_REPLACE, m_wid,
+                        gtkExtendFrame, XCB_ATOM_CARDINAL, 32, 4, &val);
+    setHasExtendedFrame(margin != 0);
+}
+


=====================================
modules/gui/qt/maininterface/compositor_x11_renderwindow.hpp
=====================================
@@ -83,6 +83,7 @@ public slots:
     void onVisibilityChanged(bool visible);
 
     void onAcrylicChanged(bool enabled);
+    void onExtendedFrameChanged(bool enabled);
 
 signals:
     void requestRefreshInternal(unsigned int requestId, QPrivateSignal priv);
@@ -109,6 +110,7 @@ private:
     CompositorX11RenderClient* m_interfaceClient = nullptr;
 
     bool m_hasAcrylic = false;
+    bool m_hasExtendedFrame = false;
     bool m_visible = true;
 };
 
@@ -163,6 +165,7 @@ public:
     void setVideoSize(const QSize& size);
 
     inline bool hasAcrylic() const { return m_hasAcrylic; }
+    inline bool supportExtendedFrame() const { return m_supportExtendedFrame; }
 
     void setVideoWindow(QWindow* window);
     void setInterfaceWindow(CompositorX11UISurface* window);
@@ -179,6 +182,7 @@ signals:
     void videoSurfaceChanged(CompositorX11RenderClient*);
     void visiblityChanged(bool visible);
     void registerVideoWindow(unsigned int xid);
+    bool hasExtendedFrameChanged(bool hasExtendedFrame);
 
 protected:
     //override from QWindow
@@ -189,7 +193,8 @@ protected:
 
 private:
     void resetClientPixmaps();
-
+    void onWindowExtendedMarginChanged(unsigned margin);
+    void setHasExtendedFrame(bool hasExtendedFrame);
 
     qt_intf_t* m_intf = nullptr;
     xcb_connection_t* m_conn = nullptr;
@@ -202,6 +207,10 @@ private:
     xcb_window_t m_wid = 0;
 
     bool m_hasAcrylic = false;
+    //does the compositor support extended frames
+    bool m_supportExtendedFrame = false;
+    //is extended frame enabled
+    bool m_hasExtendedFrame = false;
 
     QWindow* m_videoWindow = nullptr;
     std::unique_ptr<CompositorX11RenderClient> m_videoClient;


=====================================
modules/gui/qt/maininterface/compositor_x11_utils.cpp
=====================================
@@ -130,4 +130,88 @@ void setTransparentForMouseEvent(xcb_connection_t* conn, xcb_window_t window)
      xcb_xfixes_destroy_region(conn, region);
 }
 
+//see https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm45894597940912
+bool wmScreenHasCompositor(xcb_connection_t* conn, int screen)
+{
+     QByteArray propName("_NET_WM_CM_S");
+     propName += QByteArray::number(screen);
+
+     xcb_atom_t atom = getInternAtom(conn, propName.constData());
+     if (atom == 0)
+        return false;
+
+     xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(conn, atom);
+     auto reply = wrap_cptr(xcb_get_selection_owner_reply(conn, cookie, nullptr));
+     if (!reply)
+        return false;
+
+     return reply->owner != 0;
+}
+
+//see https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm45894598144416
+static std::vector<xcb_atom_t> getNetSupportedList(xcb_connection_t* conn, xcb_window_t rootWindow)
+{
+    std::vector<xcb_atom_t> netSupportedList;
+
+    xcb_atom_t netSupportedAtom = getInternAtom(conn, "_NET_SUPPORTED");
+    if (netSupportedAtom == 0)
+       return netSupportedList;
+
+    int offset = 0;
+    int remaining = 0;
+    do {
+       xcb_get_property_cookie_t cookie = xcb_get_property(conn,
+            false, rootWindow, netSupportedAtom, XCB_ATOM_ATOM, offset, 1024);
+       auto reply = wrap_cptr(xcb_get_property_reply(conn, cookie, NULL));
+       if (!reply)
+            break;
+       if (reply->type == XCB_ATOM_ATOM && reply->format == 32)
+       {
+            int length = xcb_get_property_value_length(reply.get())/sizeof(xcb_atom_t);
+            xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get());
+
+            //&atoms[length] -> pointer past the last item
+            std::copy(&atoms[0], &atoms[length], std::back_inserter(netSupportedList));
+            remaining = reply->bytes_after;
+            offset += length;
+       }
+    } while (remaining > 0);
+
+    return netSupportedList;
+}
+
+static xcb_window_t getWindowProperty(xcb_connection_t* conn, xcb_window_t window, xcb_atom_t atom)
+{
+    xcb_get_property_cookie_t cookie = xcb_get_property(conn,
+        false, window, atom, 0, 0, 4096);
+    auto reply = wrap_cptr(xcb_get_property_reply(conn, cookie, NULL));
+    if (!reply)
+       return 0;
+
+    return ((xcb_window_t *)xcb_get_property_value(reply.get()))[0];
+}
+
+//see https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm45894598113264
+static bool supportWMCheck(xcb_connection_t* conn, xcb_window_t rootWindow)
+{
+    xcb_atom_t atom = getInternAtom(conn, "_NET_SUPPORTING_WM_CHECK");
+    xcb_window_t wmWindow = getWindowProperty(conn, rootWindow, atom);
+    if (wmWindow == 0)
+        return false;
+
+    xcb_window_t wmWindowProp = getWindowProperty(conn, wmWindow, atom);
+    return (wmWindow == wmWindowProp);
+}
+
+bool wmNetSupport(xcb_connection_t* conn, xcb_window_t rootWindow, xcb_atom_t atom)
+{
+    if (!supportWMCheck(conn, rootWindow))
+        return false;
+
+    std::vector<xcb_atom_t> netSupported = getNetSupportedList(conn, rootWindow);
+
+    auto it = std::find(netSupported.cbegin(), netSupported.cend(), atom);
+    return it != netSupported.cend();
+}
+
 }


=====================================
modules/gui/qt/maininterface/compositor_x11_utils.hpp
=====================================
@@ -110,6 +110,11 @@ bool findVisualFormat(xcb_connection_t* conn, xcb_visualid_t visual, xcb_render_
 xcb_atom_t getInternAtom(xcb_connection_t* conn, const char* atomName);
 
 void setTransparentForMouseEvent(xcb_connection_t* conn, xcb_window_t window);
+
+bool wmScreenHasCompositor(xcb_connection_t* conn, int screen);
+
+bool wmNetSupport(xcb_connection_t* conn, xcb_window_t rootWindow, xcb_atom_t atom);
+
 }
 
 


=====================================
modules/gui/qt/maininterface/interface_window_handler.cpp
=====================================
@@ -66,8 +66,9 @@ InterfaceWindowHandler::InterfaceWindowHandler(qt_intf_t *_p_intf, MainCtx* main
 
     const auto updateMinimumSize = [this]()
     {
-        int width = 320;
-        int height = 300;
+        int margin = m_mainCtx->windowExtendedMargin() * 2;
+        int width = 320 + margin;
+        int height = 300 + margin;
 
         double intfScaleFactor = m_mainCtx->getIntfScaleFactor();
         int scaledWidth = std::ceil( intfScaleFactor * width );
@@ -76,6 +77,7 @@ InterfaceWindowHandler::InterfaceWindowHandler(qt_intf_t *_p_intf, MainCtx* main
         m_window->setMinimumSize( QSize(scaledWidth, scaledHeight) );
     };
     connect( m_mainCtx, &MainCtx::intfScaleFactorChanged, this, updateMinimumSize );
+    connect( m_mainCtx, &MainCtx::windowExtendedMarginChanged, this, updateMinimumSize );
     m_mainCtx->updateIntfScaleFactor();
 
     m_mainCtx->onWindowVisibilityChanged(m_window->visibility());


=====================================
modules/gui/qt/maininterface/mainctx.cpp
=====================================
@@ -271,6 +271,20 @@ void MainCtx::setUseGlobalShortcuts( bool useShortcuts )
     emit useGlobalShortcutsChanged(m_useGlobalShortcuts);
 }
 
+void MainCtx::setWindowSuportExtendedFrame(bool support) {
+    if (m_windowSuportExtendedFrame == support)
+        return;
+    m_windowSuportExtendedFrame = support;
+    emit windowSuportExtendedFrameChanged();
+}
+
+void MainCtx::setWindowExtendedMargin(unsigned int margin) {
+    if (margin == m_windowExtendedMargin)
+        return;
+    m_windowExtendedMargin = margin;
+    emit windowExtendedMarginChanged(margin);
+}
+
 /*****************************
  *   Main UI handling        *
  *****************************/


=====================================
modules/gui/qt/maininterface/mainctx.hpp
=====================================
@@ -187,6 +187,9 @@ class MainCtx : public QObject
     // NOTE: This is useful when we want to prioritize player hotkeys over QML keyboard navigation.
     Q_PROPERTY(bool preferHotkeys READ preferHotkeys WRITE setPreferHotkeys NOTIFY preferHotkeysChanged FINAL)
 
+    Q_PROPERTY(bool windowSuportExtendedFrame READ windowSuportExtendedFrame NOTIFY windowSuportExtendedFrameChanged)
+    Q_PROPERTY(unsigned windowExtendedMargin READ windowExtendedMargin WRITE setWindowExtendedMargin NOTIFY windowExtendedMarginChanged)
+
 public:
     /* tors */
     MainCtx(qt_intf_t *);
@@ -267,6 +270,11 @@ public:
 
     inline float safeArea() const { return m_safeArea; }
 
+    inline bool windowSuportExtendedFrame() const { return m_windowSuportExtendedFrame; }
+    inline unsigned windowExtendedMargin() const { return m_windowExtendedMargin; }
+    void setWindowSuportExtendedFrame(bool support);
+    void setWindowExtendedMargin(unsigned margin);
+
     bool hasEmbededVideo() const;
     VideoSurfaceProvider* getVideoSurfaceProvider() const;
     void setVideoSurfaceProvider(VideoSurfaceProvider* videoSurfaceProvider);
@@ -381,6 +389,9 @@ protected:
 
     float m_safeArea = 0.0;
 
+    bool m_windowSuportExtendedFrame = false;
+    unsigned m_windowExtendedMargin = 0;
+
     std::unique_ptr<CSDButtonModel> m_csdButtonModel;
 
 public slots:
@@ -477,6 +488,9 @@ signals:
 
     void safeAreaChanged();
 
+    void windowSuportExtendedFrameChanged();
+    void windowExtendedMarginChanged(unsigned margin);
+
 private:
     void loadPrefs(bool callSignals);
     void loadFromSettingsImpl(bool callSignals);


=====================================
modules/gui/qt/maininterface/qml/MainInterface.qml
=====================================
@@ -20,236 +20,293 @@ import QtQuick 2.12
 import QtQuick.Layouts 1.12
 import QtQuick.Controls 2.12
 import QtQuick.Window 2.12
+import QtGraphicalEffects 1.12
 
 import org.videolan.vlc 0.1
 import org.videolan.compat 0.1
 
 import "qrc:///widgets/" as Widgets
 import "qrc:///style/"
-
 import "qrc:///playlist/" as PL
 
 Item {
-    id: g_mainInterface
 
     property bool _interfaceReady: false
     property bool _playlistReady: false
+    property bool _extendedFrameVisible: MainCtx.windowSuportExtendedFrame
+                                      && MainCtx.clientSideDecoration
+                                      && (MainCtx.intfMainWindow.visibility === Window.Windowed)
 
-    BindingCompat {
-        target: VLCStyle
-        property: "appWidth"
-        value: g_mainInterface.width
-    }
-
-    BindingCompat {
-        target: VLCStyle
-        property: "appHeight"
-        value: g_mainInterface.height
-    }
-
-    Window.onWindowChanged: {
-        if (Window.window && !Qt.colorEqual(Window.window.color, "transparent")) {
-            Window.window.color = Qt.binding(function() { return theme.bg.primary })
-        }
-    }
-
-    ColorContext {
-        id: theme
-        palette: VLCStyle.palette
-        colorSet: ColorContext.View
-    }
-
-    Widgets.ToolTipExt {
-        id: attachedToolTip
+    readonly property var _pageModel: [
+        { name: "mc", url: "qrc:///main/MainDisplay.qml" },
+        { name: "player", url:"qrc:///player/Player.qml" },
+    ]
 
-        parent: null
-        z: 99
-        colorContext.palette: parent && parent.colorContext ? parent.colorContext.palette
-                                                            : VLCStyle.palette
+    function setInitialView() {
+        //set the initial view
+        const loadPlayer = !MainPlaylistController.empty;
 
-        Component.onCompleted: {
-            MainCtx.setAttachedToolTip(this)
-        }
-    }
+        if (MainCtx.mediaLibraryAvailable)
+            History.push(["mc", "video"],
+                         Qt.OtherFocusReason, loadPlayer ? History.Stay : History.Go)
+        else
+            History.push(["mc", "home"],
+                         Qt.OtherFocusReason, loadPlayer ? History.Stay : History.Go)
 
-    Loader {
-        id: playlistWindowLoader
-        asynchronous: true
-        active: !MainCtx.playlistDocked && MainCtx.playlistVisible
-        source: "qrc:///playlist/PlaylistDetachedWindow.qml"
-    }
-    Connections {
-        target: playlistWindowLoader.item
-        onClosing: MainCtx.playlistVisible = false
+        if (loadPlayer)
+            History.push(["player"])
     }
 
-    Connections {
-        target: MainPlaylistController
-
-        onPlaylistInitialized: {
-            g_mainInterface._playlistReady = true
-            if (g_mainInterface._interfaceReady)
-                setInitialView()
-        }
+    function _pushHome() {
+        if (MainCtx.mediaLibraryAvailable)
+            History.push(["mc", "video"])
+        else
+            History.push(["mc", "home"])
     }
 
-    readonly property var pageModel: [
-        { name: "mc", url: "qrc:///main/MainDisplay.qml" },
-        { name: "player", url:"qrc:///player/Player.qml" },
-    ]
-
     function loadCurrentHistoryView() {
         const current = History.current
         if ( !current || !current.name  || !current.properties ) {
             console.warn("unable to load requested view, undefined")
             return
         }
-        stackView.loadView(g_mainInterface.pageModel, current.name, current.properties)
+        stackView.loadView(_pageModel, current.name, current.properties)
 
         MainCtx.mediaLibraryVisible = (current.name !== "player")
     }
 
-    Connections {
-        target: History
-        onCurrentChanged: loadCurrentHistoryView()
-    }
-
-    Connections {
-        target: MainCtx
+    Item {
+        id: g_mainInterface
 
-        onMediaLibraryVisibleChanged: {
-            if (MainCtx.mediaLibraryVisible) {
-                // NOTE: Useful when we started the application on the 'player' view.
-                if (History.previousEmpty) {
-                    if (MainCtx.hasEmbededVideo && MainCtx.canShowVideoPIP === false)
-                        MainPlaylistController.stop()
+        anchors.fill: parent
+        anchors.topMargin: MainCtx.windowExtendedMargin
+        anchors.leftMargin: MainCtx.windowExtendedMargin
+        anchors.rightMargin: MainCtx.windowExtendedMargin
+        anchors.bottomMargin: MainCtx.windowExtendedMargin
+
+        BindingCompat {
+            target: VLCStyle
+            property: "appWidth"
+            value: g_mainInterface.width
+        }
 
-                    _pushHome()
+        BindingCompat {
+            target: VLCStyle
+            property: "appHeight"
+            value: g_mainInterface.height
+        }
 
-                    return
-                }
+        BindingCompat {
+            target: MainCtx
+            property: "windowExtendedMargin"
+            value: _extendedFrameVisible ? VLCStyle.dp(20, VLCStyle.scale) : 0
+        }
 
-                if (History.current.name !== "player")
-                    return
+        Window.onWindowChanged: {
+            if (Window.window && !Qt.colorEqual(Window.window.color, "transparent")) {
+                Window.window.color = Qt.binding(function() { return theme.bg.primary })
+            }
+        }
 
-                if (MainCtx.hasEmbededVideo && MainCtx.canShowVideoPIP === false)
-                    MainPlaylistController.stop()
+        ColorContext {
+            id: theme
+            palette: VLCStyle.palette
+            colorSet: ColorContext.View
+        }
 
-                History.previous()
-            } else {
-                if (History.current.name === "player")
-                    return
+        Widgets.ToolTipExt {
+            id: attachedToolTip
 
-                stackView.currentItem._inhibitMiniPlayer = true
+            parent: null
+            z: 99
+            colorContext.palette: parent && parent.colorContext ? parent.colorContext.palette
+                                                                : VLCStyle.palette
 
-                History.push(["player"])
+            Component.onCompleted: {
+                MainCtx.setAttachedToolTip(this)
             }
         }
-    }
 
-    function setInitialView() {
-        //set the initial view
-        const loadPlayer = !MainPlaylistController.empty;
+        Loader {
+            id: playlistWindowLoader
+            asynchronous: true
+            active: !MainCtx.playlistDocked && MainCtx.playlistVisible
+            source: "qrc:///playlist/PlaylistDetachedWindow.qml"
+        }
+        Connections {
+            target: playlistWindowLoader.item
+            onClosing: MainCtx.playlistVisible = false
+        }
 
-        if (MainCtx.mediaLibraryAvailable)
-            History.push(["mc", "video"],
-                         Qt.OtherFocusReason, loadPlayer ? History.Stay : History.Go)
-        else
-            History.push(["mc", "home"],
-                         Qt.OtherFocusReason, loadPlayer ? History.Stay : History.Go)
+        Connections {
+            target: MainPlaylistController
 
-        if (loadPlayer)
-            History.push(["player"])
-    }
+            onPlaylistInitialized: {
+                _playlistReady = true
+                if (_interfaceReady)
+                    setInitialView()
+            }
+        }
 
-    function _pushHome() {
-        if (MainCtx.mediaLibraryAvailable)
-            History.push(["mc", "video"])
-        else
-            History.push(["mc", "home"])
-    }
+        Connections {
+            target: History
+            onCurrentChanged: loadCurrentHistoryView()
+        }
 
-    Component.onCompleted: {
-        g_mainInterface._interfaceReady = true;
-        if (g_mainInterface._playlistReady)
-            setInitialView()
-    }
+        Connections {
+            target: MainCtx
 
+            onMediaLibraryVisibleChanged: {
+                if (MainCtx.mediaLibraryVisible) {
+                    // NOTE: Useful when we started the application on the 'player' view.
+                    if (History.previousEmpty) {
+                        if (MainCtx.hasEmbededVideo && MainCtx.canShowVideoPIP === false)
+                            MainPlaylistController.stop()
 
-    DropArea {
-        anchors.fill: parent
-        onDropped: {
-            let urls = []
-            if (drop.hasUrls) {
+                        _pushHome()
 
-                for (let i = 0; i < drop.urls.length; i++)
-                    urls.push(drop.urls[i])
+                        return
+                    }
 
-            } else if (drop.hasText) {
-                /* Browsers give content as text if you dnd the addressbar,
-                   so check if mimedata has valid url in text and use it
-                   if we didn't get any normal Urls()*/
+                    if (History.current.name !== "player")
+                        return
 
-                urls.push(drop.text)
-            }
+                    if (MainCtx.hasEmbededVideo && MainCtx.canShowVideoPIP === false)
+                        MainPlaylistController.stop()
 
-            if (urls.length > 0) {
-                /* D&D of a subtitles file, add it on the fly */
-                if (Player.isPlaying && urls.length == 1) {
-                    if (Player.associateSubtitleFile(urls[0])) {
-                        drop.accept()
+                    History.previous()
+                } else {
+                    if (History.current.name === "player")
                         return
-                    }
-                }
 
-                MainPlaylistController.append(urls, true)
-                drop.accept()
+                    stackView.currentItem._inhibitMiniPlayer = true
+
+                    History.push(["player"])
+                }
             }
         }
 
-        Widgets.StackViewExt {
-            id: stackView
+        Component.onCompleted: {
+            _interfaceReady = true;
+            if (_playlistReady)
+                setInitialView()
+        }
+
+
+        DropArea {
             anchors.fill: parent
-            focus: true
-
-            Connections {
-                target: Player
-                onPlayingStateChanged: {
-                    if (Player.playingState === Player.PLAYING_STATE_STOPPED
-                            && History.current.name === "player") {
-                        if (History.previousEmpty)
-                            _pushHome()
-                        else
-                            History.previous()
+            onDropped: {
+                let urls = []
+                if (drop.hasUrls) {
+
+                    for (let i = 0; i < drop.urls.length; i++)
+                        urls.push(drop.urls[i])
+
+                } else if (drop.hasText) {
+                    /* Browsers give content as text if you dnd the addressbar,
+                       so check if mimedata has valid url in text and use it
+                       if we didn't get any normal Urls()*/
+
+                    urls.push(drop.text)
+                }
+
+                if (urls.length > 0) {
+                    /* D&D of a subtitles file, add it on the fly */
+                    if (Player.isPlaying && urls.length == 1) {
+                        if (Player.associateSubtitleFile(urls[0])) {
+                            drop.accept()
+                            return
+                        }
+                    }
+
+                    MainPlaylistController.append(urls, true)
+                    drop.accept()
+                }
+            }
+
+            Widgets.StackViewExt {
+                id: stackView
+                anchors.fill: parent
+                focus: true
+                clip: _extendedFrameVisible
+
+                Connections {
+                    target: Player
+                    onPlayingStateChanged: {
+                        if (Player.playingState === Player.PLAYING_STATE_STOPPED
+                                && History.current.name === "player") {
+                            if (History.previousEmpty)
+                                _pushHome()
+                            else
+                                History.previous()
+                        }
                     }
                 }
             }
         }
-    }
 
-    Loader {
-        asynchronous: true
-        source: "qrc:///menus/GlobalShortcuts.qml"
-    }
+        Loader {
+            asynchronous: true
+            source: "qrc:///menus/GlobalShortcuts.qml"
+        }
 
-    MouseArea {
-        /// handles mouse navigation buttons
-        anchors.fill: parent
-        acceptedButtons: Qt.BackButton
-        cursorShape: undefined
-        onClicked: History.previous()
-    }
+        MouseArea {
+            /// handles mouse navigation buttons
+            anchors.fill: parent
+            acceptedButtons: Qt.BackButton
+            cursorShape: undefined
+            onClicked: History.previous()
+        }
 
-    Loader {
-        active: {
-            const windowVisibility = MainCtx.intfMainWindow.visibility
-            return MainCtx.clientSideDecoration
-                    && (windowVisibility !== Window.Maximized)
-                    && (windowVisibility !== Window.FullScreen)
+        Loader {
+            active: {
+                const windowVisibility = MainCtx.intfMainWindow.visibility
+                return MainCtx.clientSideDecoration
+                        && (windowVisibility !== Window.Maximized)
+                        && (windowVisibility !== Window.FullScreen)
 
-        }
+            }
 
-        source: "qrc:///widgets/CSDMouseStealer.qml"
+            source: "qrc:///widgets/CSDMouseStealer.qml"
+
+            onLoaded: {
+                item.target = g_mainInterface
+                item.anchorInside = Qt.binding(() => !_extendedFrameVisible)
+            }
+        }
     }
 
+    //draw the window drop shadow ourselve when the windowing system doesn't
+    //provide them but support extended frame
+    RectangularGlow {
+        id: effect
+        z: -1
+        visible: _extendedFrameVisible
+        anchors.fill: g_mainInterface
+        spread: 0.0
+        color: "black"
+        opacity:  0.5
+        cornerRadius: glowRadius
+        states: [
+            State {
+                when: MainCtx.intfMainWindow.active
+                PropertyChanges {
+                    target: effect
+                    glowRadius: MainCtx.windowExtendedMargin * 0.7
+                }
+            },
+            State {
+                when: !MainCtx.intfMainWindow.active
+                PropertyChanges {
+                    target: effect
+                    glowRadius: MainCtx.windowExtendedMargin * 0.5
+                }
+            }
+        ]
+        Behavior on  glowRadius {
+            NumberAnimation {
+                duration: VLCStyle.duration_veryShort
+            }
+        }
+    }
 }


=====================================
modules/gui/qt/widgets/qml/CSDMouseStealer.qml
=====================================
@@ -25,80 +25,165 @@ Item {
     id: root
 
     property int csdSize: MainCtx.csdBorderSize
+    /* required */ property Item target: null
+    property bool anchorInside: true
 
     //private
-    readonly property int _edgeVtHeight: g_mainInterface.height - root.csdSize * 2
-    readonly property int _edgeHzWidth: g_mainInterface.width - root.csdSize * 2
+    readonly property int _targetHeight: target ? target.height : 0
+    readonly property int _targetWidth: target ? target.width : 0
+
+    readonly property int _edgeVtHeight: target ? (target.height - root.csdSize * 2) : 0
+    readonly property int _edgeHzWidth:  target ? (target.width  - root.csdSize * 2) : 0
+
+    //areas placed inside the target
+    readonly property var _innerModel: [
+        //Edges
+        {
+            edge: Qt.TopEdge,
+            x: root.csdSize,
+            y: 0,
+            width: root._edgeHzWidth,
+            height: root.csdSize,
+            cursor: Qt.SizeVerCursor,
+        },
+        {
+            edge: Qt.LeftEdge,
+            x: 0,
+            y: root.csdSize,
+            width: root.csdSize,
+            height: root._edgeVtHeight,
+            cursor: Qt.SizeHorCursor,
+        },
+        {
+            edge: Qt.RightEdge,
+            x: _targetWidth - root.csdSize,
+            y: root.csdSize,
+            width: root.csdSize,
+            height: root._edgeVtHeight,
+            cursor: Qt.SizeHorCursor,
+        },
+        {
+            edge: Qt.BottomEdge,
+            x: root.csdSize,
+            y: _targetHeight - root.csdSize,
+            width: root._edgeHzWidth,
+            height: root.csdSize,
+            cursor: Qt.SizeVerCursor,
+        },
+        //Corners
+        {
+            edge: Qt.TopEdge | Qt.LeftEdge,
+            x: 0,
+            y: 0,
+            width: root.csdSize,
+            height: root.csdSize,
+            cursor: Qt.SizeFDiagCursor,
+        },
+        {
+            edge: Qt.BottomEdge | Qt.LeftEdge,
+            x: 0,
+            y: _targetHeight - root.csdSize,
+            width: root.csdSize,
+            height: root.csdSize,
+            cursor: Qt.SizeBDiagCursor,
+        },
+        {
+            edge: Qt.TopEdge | Qt.RightEdge,
+            x: _targetWidth - root.csdSize,
+            y: 0,
+            width: root.csdSize,
+            height: root.csdSize,
+            cursor: Qt.SizeBDiagCursor,
+        },
+        {
+            edge: Qt.BottomEdge | Qt.RightEdge,
+            x: _targetWidth - root.csdSize,
+            y: _targetHeight - root.csdSize,
+            width: root.csdSize,
+            height: root.csdSize,
+            cursor: Qt.SizeFDiagCursor,
+        },
+    ]
+
+    //areas placed ouside the target
+    readonly property var _outterModel: [
+        //Edges
+        {
+            edge: Qt.TopEdge,
+            x: 0,
+            y: -root.csdSize,
+            width: _targetWidth,
+            height: root.csdSize,
+            cursor: Qt.SizeVerCursor,
+        },
+        {
+            edge: Qt.LeftEdge,
+            x: -root.csdSize,
+            y: 0,
+            width: root.csdSize,
+            height: _targetHeight,
+            cursor: Qt.SizeHorCursor,
+        },
+        {
+            edge: Qt.RightEdge,
+            x: _targetWidth,
+            y: 0,
+            width: root.csdSize,
+            height: _targetHeight,
+            cursor: Qt.SizeHorCursor,
+        },
+        {
+            edge: Qt.BottomEdge,
+            x: 0,
+            y: _targetHeight,
+            width: _targetWidth,
+            height: root.csdSize,
+            cursor: Qt.SizeVerCursor,
+        },
+        //Corners
+        {
+            edge: Qt.TopEdge | Qt.LeftEdge,
+            x: -root.csdSize,
+            y: -root.csdSize,
+            width: root.csdSize,
+            height: root.csdSize,
+            cursor: Qt.SizeFDiagCursor,
+        },
+        {
+            edge: Qt.BottomEdge | Qt.LeftEdge,
+            x: -root.csdSize,
+            y: _targetHeight,
+            width: root.csdSize,
+            height: root.csdSize,
+            cursor: Qt.SizeBDiagCursor,
+        },
+        {
+            edge: Qt.TopEdge | Qt.RightEdge,
+            x: _targetWidth,
+            y: -root.csdSize,
+            width: root.csdSize,
+            height: root.csdSize,
+            cursor: Qt.SizeBDiagCursor,
+        },
+        {
+            edge: Qt.BottomEdge | Qt.RightEdge,
+            x: _targetWidth,
+            y: _targetHeight,
+            width: root.csdSize,
+            height: root.csdSize,
+            cursor: Qt.SizeFDiagCursor,
+        },
+    ]
 
     Repeater {
-        model: [
-            //Edges
-            {
-                edge: Qt.TopEdge,
-                x: root.csdSize,
-                y: 0,
-                width: root._edgeHzWidth,
-                height: root.csdSize,
-                cursor: Qt.SizeVerCursor,
-            },
-            {
-                edge: Qt.LeftEdge,
-                x: 0,
-                y: root.csdSize,
-                width: root.csdSize,
-                height: root._edgeVtHeight,
-                cursor: Qt.SizeHorCursor,
-            },
-            {
-                edge: Qt.RightEdge,
-                x: g_mainInterface.width - root.csdSize,
-                y: root.csdSize,
-                width: root.csdSize,
-                height: root._edgeVtHeight,
-                cursor: Qt.SizeHorCursor,
-            },
-            {
-                edge: Qt.BottomEdge,
-                x: root.csdSize,
-                y: g_mainInterface.height - root.csdSize,
-                width: root._edgeHzWidth,
-                height: root.csdSize,
-                cursor: Qt.SizeVerCursor,
-            },
-            //Corners
-            {
-                edge: Qt.TopEdge | Qt.LeftEdge,
-                x: 0,
-                y: 0,
-                width: root.csdSize,
-                height: root.csdSize,
-                cursor: Qt.SizeFDiagCursor,
-            },
-            {
-                edge: Qt.BottomEdge | Qt.LeftEdge,
-                x: 0,
-                y: g_mainInterface.height - root.csdSize,
-                width: root.csdSize,
-                height: root.csdSize,
-                cursor: Qt.SizeBDiagCursor,
-            },
-            {
-                edge: Qt.TopEdge | Qt.RightEdge,
-                x: g_mainInterface.width - root.csdSize,
-                y: 0,
-                width: root.csdSize,
-                height: root.csdSize,
-                cursor: Qt.SizeBDiagCursor,
-            },
-            {
-                edge: Qt.BottomEdge | Qt.RightEdge,
-                x: g_mainInterface.width - root.csdSize,
-                y: g_mainInterface.height - root.csdSize,
-                width: root.csdSize,
-                height: root.csdSize,
-                cursor: Qt.SizeFDiagCursor,
-            },
-        ]
+        model: {
+            if (!target)
+                return 0
+            else if (anchorInside)
+                return _innerModel
+            else
+                return _outterModel
+        }
 
         delegate: MouseArea {
             x: modelData.x



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/8c569859a0b36b438bd4979c3f96e91bed55a88b...e3dea2575851338e8df78d1886d450f1a698e378

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/8c569859a0b36b438bd4979c3f96e91bed55a88b...e3dea2575851338e8df78d1886d450f1a698e378
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