[vlc-commits] [Git][videolan/vlc][master] 10 commits: Revert "qml/PlayerControlLayout: Update the 'layoutSpacing' property"

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Sun Nov 26 16:08:50 UTC 2023



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


Commits:
70732cf3 by Fatih Uzunoglu at 2023-11-26T15:53:44+00:00
Revert "qml/PlayerControlLayout: Update the 'layoutSpacing' property"

This reverts commit 72c34ac178d835a549920ece08a0e4edefe12351.

- - - - -
848b6e21 by Fatih Uzunoglu at 2023-11-26T15:53:44+00:00
Revert "qml/PlayerControlLayout: Update resize and collpasing behavior"

This reverts commit a1f2bda77b7446d6a71f79f1a82311f3db18bd3f.

- - - - -
2bcc6e63 by Fatih Uzunoglu at 2023-11-26T15:53:44+00:00
Revert "qml/ArtworkInfoWidget: Add the 'preferredWidth' property"

This reverts commit aec938f083e269bcc69de2b8f9b4307a0336584b.

- - - - -
979681c2 by Fatih Uzunoglu at 2023-11-26T15:53:44+00:00
Revert "qml/ControlLayout: Add the 'preferredWidth' support"

This reverts commit 3efc5ce68fd39b6d0aebd80a6fa626473e2098ad.

- - - - -
4af07aea by Fatih Uzunoglu at 2023-11-26T15:53:44+00:00
Revert "qml/ControlLayout: Update the 'implicitWidth' property"

This reverts commit 44bc4d84b507b9d4e817fb97a844fb8ae6ce1c54.

- - - - -
92ffca9b by Fatih Uzunoglu at 2023-11-26T15:53:44+00:00
Revert "qml/ControlLayout: Add the 'count' alias"

This reverts commit 96e3f57302d95f08dad13068389e1f38804e6e9b.

- - - - -
55099e40 by Fatih Uzunoglu at 2023-11-26T15:53:44+00:00
Revert "qml/ControlLayout: Add the 'alignment' property"

This reverts commit a421045fd7d6a31bc85887bf797327a3f8ba9753.

- - - - -
c2803be3 by Fatih Uzunoglu at 2023-11-26T15:53:44+00:00
Revert "qml/ControlLayout: Add the 'contentWidth' property"

This reverts commit 9bea3945b16ad336e323543065667054274f615a.

- - - - -
3be2ea83 by Fatih Uzunoglu at 2023-11-26T15:53:44+00:00
qml: major overhaul for player control layout

- Support dynamic width when center is not
  available.
- Proper support for height adjustment
- Declarative approach

- - - - -
d4d2c619 by Fatih Uzunoglu at 2023-11-26T15:53:44+00:00
qml: enable delayed bindings when the component is completed

- - - - -


11 changed files:

- modules/gui/qt/Makefile.am
- modules/gui/qt/dialogs/toolbar/qml/EditorDNDDelegate.qml
- modules/gui/qt/dialogs/toolbar/qml/EditorDNDView.qml
- modules/gui/qt/player/qml/ControlBar.qml
- modules/gui/qt/player/qml/ControlLayout.qml
- + modules/gui/qt/player/qml/ControlRepeater.qml
- modules/gui/qt/player/qml/PlayerControlLayout.qml
- modules/gui/qt/player/qml/controlbarcontrols/ArtworkInfoWidget.qml
- modules/gui/qt/player/qml/controlbarcontrols/ExpandingSpacerWidget.qml
- modules/gui/qt/player/qml/controlbarcontrols/PlayButton.qml
- modules/gui/qt/vlc.qrc


Changes:

=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -930,6 +930,7 @@ libqt_plugin_la_QML = \
 	gui/qt/player/qml/TracksPageSubtitle.qml \
 	gui/qt/player/qml/TracksListPage.qml \
 	gui/qt/player/qml/ControlLayout.qml \
+	gui/qt/player/qml/ControlRepeater.qml \
 	gui/qt/player/qml/controlbarcontrols/HighResolutionTimeWidget.qml \
 	gui/qt/player/qml/controlbarcontrols/ArtworkInfoWidget.qml \
 	gui/qt/player/qml/controlbarcontrols/AspectRatioWidget.qml \


=====================================
modules/gui/qt/dialogs/toolbar/qml/EditorDNDDelegate.qml
=====================================
@@ -18,6 +18,7 @@
 
 import QtQuick 2.12
 import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
 import QtQml.Models 2.12
 
 import org.videolan.vlc 0.1
@@ -35,7 +36,7 @@ Control {
     readonly property int controlId: model.id
     property ListView dndView: null
 
-    readonly property bool dragActive: loader.Drag.active
+    readonly property bool dragActive: contentItem.target.Drag.active
     property alias dropArea: dropArea
 
     property alias containsMouse: mouseArea.containsMouse
@@ -78,9 +79,9 @@ Control {
         }
 
         onPressed: {
-            const pos = mapToItem(loader.parent, mouseX, mouseY)
-            loader.y = pos.y + VLCStyle.dragDelta
-            loader.x = pos.x + VLCStyle.dragDelta
+            const pos = mapToItem(control.contentItem.target.parent, mouseX, mouseY)
+            control.contentItem.target.y = pos.y + VLCStyle.dragDelta
+            control.contentItem.target.x = pos.x + VLCStyle.dragDelta
         }
     }
 
@@ -132,8 +133,6 @@ Control {
     }
 
     background: Rectangle {
-        opacity: Drag.active ? 0.75 : 1.0
-
         color: "transparent"
 
         border.width: VLCStyle.dp(1, VLCStyle.scale)
@@ -145,13 +144,14 @@ Control {
         implicitHeight: loader.implicitHeight
         implicitWidth: loader.implicitWidth
 
+        readonly property Item target: loader
+
         Loader {
             id: loader
 
             parent: Drag.active ? root : control.contentItem
 
-            anchors.horizontalCenter: Drag.active ? undefined : parent.horizontalCenter
-            anchors.verticalCenter:  Drag.active ? undefined : parent.verticalCenter
+            anchors.fill: (parent === control.contentItem) ? parent : undefined
 
             source: PlayerControlbarControls.control(model.id).source
 
@@ -160,10 +160,6 @@ Control {
             onLoaded: {
                 item.paintOnly = true
                 item.enabled = false
-
-                if (!extraWidthAvailable && item.minimumWidth !== undefined) {
-                    item.width = item.minimumWidth
-                }
             }
         }
     }


=====================================
modules/gui/qt/dialogs/toolbar/qml/EditorDNDView.qml
=====================================
@@ -161,8 +161,9 @@ ListView {
     }
 
     delegate: EditorDNDDelegate {
-        anchors.verticalCenter: (!!parent) ? parent.verticalCenter : undefined
+        height: Math.min((contentItem.implicitHeight > 0) ? contentItem.implicitHeight : Number.MAX_VALUE, VLCStyle.controlLayoutHeight)
 
+        anchors.verticalCenter: parent ? parent.verticalCenter : undefined
         dndView: playerBtnDND
 
         BindingCompat {


=====================================
modules/gui/qt/player/qml/ControlBar.qml
=====================================
@@ -243,6 +243,9 @@ T.Pane {
             Layout.rightMargin: VLCStyle.margin_large
 
             identifier: root.identifier
+            
+            implicitHeight: MainCtx.pinVideoControls ? VLCStyle.controlLayoutHeightPinned
+                                                     : VLCStyle.controlLayoutHeight
 
             Navigation.upItem: trackPositionSlider.enabled ? trackPositionSlider : root.Navigation.upItem
 


=====================================
modules/gui/qt/player/qml/ControlLayout.qml
=====================================
@@ -32,9 +32,55 @@ FocusScope {
 
     // Properties
 
-    property int contentWidth: 0
+    // These delayed bindings are necessary
+    // because the size of the items
+    // may not be ready immediately.
+    // The wise thing to do would be to not
+    // delay if the sizes are ready.
+
+    BindingCompat on Layout.minimumWidth {
+        delayed: true
+        when: controlLayout._componentCompleted
+        value: {
+            const count = repeater.count
+
+            if (count === 0)
+                return 0
+
+            let size = 0
+
+            for (let i = 0; i < count; ++i) {
+                const item = repeater.itemAt(i)
+
+                if (item.Layout.minimumWidth < 0)
+                    size += item.implicitWidth
+                else
+                    size += item.Layout.minimumWidth
+            }
+
+            return size + ((count - 1 + ((controlLayout.alignment & (Qt.AlignLeft | Qt.AlignRight)) ? 1 : 0)) * playerControlLayout.spacing)
+        }
+    }
+
+    BindingCompat on Layout.maximumWidth {
+        delayed: true
+        when: controlLayout._componentCompleted
+        value: {
+            let maximumWidth = 0
+            const count = repeater.count
 
-    property int alignment: 0
+            for (let i = 0; i < count; ++i) {
+                const item = repeater.itemAt(i)
+                maximumWidth += item.implicitWidth
+            }
+
+            maximumWidth += ((count - 1 + ((alignment & (Qt.AlignLeft | Qt.AlignRight)) ? 1 : 0)) * playerControlLayout.spacing)
+
+            return maximumWidth
+        }
+    }
+
+    property alias alignment: repeater.alignment
 
     property var altFocusAction: Navigation.defaultNavigationUp
 
@@ -43,9 +89,9 @@ FocusScope {
         colorSet: ColorContext.Window
     }
 
-    // Aliases
+    property bool _componentCompleted: false
 
-    property alias count: repeater.count
+    // Aliases
 
     property alias model: repeater.model
 
@@ -57,23 +103,7 @@ FocusScope {
 
     // Settings
 
-    implicitWidth: {
-        if (count === 0)
-            return 0
-
-        let size = 0
-
-        for (let i = 0; i < count; ++i) {
-            size += repeater.itemAt(i).preferredWidth
-        }
-
-        if (alignment)
-            // NOTE: We provision the spacing induced by the alignment item.
-            return size + count * rowLayout.spacing
-        else
-            return size + (count - 1) * rowLayout.spacing
-    }
-
+    implicitWidth: Layout.maximumWidth
     implicitHeight: rowLayout.implicitHeight
 
     Navigation.navigable: {
@@ -92,6 +122,8 @@ FocusScope {
     Component.onCompleted: {
         visibleChanged.connect(_handleFocus)
         activeFocusChanged.connect(_handleFocus)
+
+        _componentCompleted = true
     }
 
     // Functions
@@ -104,28 +136,6 @@ FocusScope {
             altFocusAction()
     }
 
-    function _updateContentWidth() {
-        let size = 0
-
-        for (let i = 0; i < count; i++) {
-
-            const item = repeater.itemAt(i)
-
-            if (item === null || item.isActive === false)
-                continue
-
-            const width = item.width
-
-            if (width)
-                size += width + spacing
-        }
-
-        if (size)
-            contentWidth = size - spacing
-        else
-            contentWidth = size
-    }
-
     // Children
 
     RowLayout {
@@ -136,249 +146,25 @@ FocusScope {
         spacing: playerControlLayout.spacing
 
         Item {
-            Layout.fillWidth: (controlLayout.alignment === Qt.AlignRight)
+            Layout.fillWidth: visible
+            visible: (controlLayout.alignment & Qt.AlignRight)
         }
 
-        Repeater {
+        ControlRepeater {
             id: repeater
 
-            // NOTE: We apply the 'navigation chain' after adding the item.
-            onItemAdded: {
-                item.applyNavigation()
-
-                controlLayout._updateContentWidth()
-            }
-
-            onItemRemoved: {
-                // NOTE: We update the 'navigation chain' after removing the item.
-                item.removeNavigation()
-
-                item.recoverFocus(index)
-
-                controlLayout._updateContentWidth()
-            }
-
-            delegate: Loader {
-                id: loader
-
-                // Properties
-
-                // NOTE: This is required for contentWidth because the visible property is delayed.
-                property bool isActive: (x + minimumWidth <= rowLayout.width)
+            Navigation.parentItem: controlLayout
 
-                property int minimumWidth: {
-                    if (expandable)
-                        return item.minimumWidth
-                    else if (item)
-                        return item.implicitWidth
-                    else
-                        return 0
-                }
+            availableWidth: rowLayout.width
+            availableHeight: rowLayout.height
 
-                property int preferredWidth: (item && item.preferredWidth) ? item.preferredWidth
-                                                                           : minimumWidth
-
-                readonly property bool expandable: (item && item.minimumWidth !== undefined)
-
-                // Settings
-
-                source: PlayerControlbarControls.control(model.id).source
-
-                focus: (index === 0)
-
-                Layout.fillWidth: expandable
-
-                Layout.minimumWidth: minimumWidth
-
-                Layout.preferredWidth: preferredWidth
-
-                Layout.maximumWidth: preferredWidth
-
-                Layout.alignment: (Qt.AlignVCenter | controlLayout.alignment)
-
-                BindingCompat {
-                    delayed: true // this is important
-                    target: loader
-                    property: "visible"
-                    value: isActive
-                }
-
-                // Events
-
-                Component.onCompleted: repeater.countChanged.connect(controlLayout._handleFocus)
-
-                onIsActiveChanged: controlLayout._updateContentWidth()
-
-                onWidthChanged: controlLayout._updateContentWidth()
-
-                onActiveFocusChanged: {
-                    if (activeFocus && (!!item && !item.focus)) {
-                        recoverFocus()
-                    }
-                }
-
-                onLoaded: {
-                    // control should not request focus if they are not enabled:
-                    item.focus = Qt.binding(function() { return item.enabled && item.visible })
-
-                    // navigation parent of control is always controlLayout
-                    // so it can be set here unlike leftItem and rightItem:
-                    item.Navigation.parentItem = controlLayout
-
-                    if (item instanceof Control || item instanceof T.Control)
-                        item.activeFocusOnTab = true
-
-                    // FIXME: Do we really need to enforce a defaultSize ?
-                    if (item.size !== undefined)
-                        item.size = Qt.binding(function() { return defaultSize; })
-
-                    item.width = Qt.binding(function() { return loader.width } )
-
-                    if (item.maximumHeight !== undefined)
-                        item.maximumHeight = Qt.binding(function() { return rowLayout.height })
-
-                    item.visible = Qt.binding(function() { return loader.visible })
-
-                    if (item.requestLockUnlockAutoHide) {
-                        item.requestLockUnlockAutoHide.connect(function(lock) {
-                            controlLayout.requestLockUnlockAutoHide(lock)
-                        })
-                    }
-
-                    if (item.menuOpened) {
-                        item.menuOpened.connect(function(menu) {
-                            controlLayout.menuOpened(menu)
-                        })
-                    }
-                }
-
-                // Connections
-
-                Connections {
-                    target: item
-
-                    enabled: loader.status === Loader.Ready
-
-                    onEnabledChanged: {
-                        if (activeFocus && !item.enabled) // Loader has focus but item is not enabled
-                            recoverFocus()
-                    }
-
-                    onVisibleChanged: {
-                        if (activeFocus && !item.visible)
-                            recoverFocus()
-                    }
-                }
-
-                // Functions
-
-                function applyNavigation() {
-                    if (item == null) return
-
-                    const itemLeft  = repeater.itemAt(index - 1)
-                    const itemRight = repeater.itemAt(index + 1)
-
-                    if (itemLeft) {
-                        const componentLeft = itemLeft.item
-
-                        if (componentLeft)
-                        {
-                            item.Navigation.leftItem = componentLeft
-
-                            componentLeft.Navigation.rightItem = item
-                        }
-                    }
-
-                    if (itemRight) {
-                        const componentRight = itemRight.item
-
-                        if (componentRight)
-                        {
-                            item.Navigation.rightItem = componentRight
-
-                            componentRight.Navigation.leftItem = item
-                        }
-                    }
-                }
-
-                function removeNavigation() {
-                    if (item == null) return
-
-                    const itemLeft = repeater.itemAt(index - 1)
-
-                    // NOTE: The current item was removed from the repeater so we test against the
-                    //       same index.
-                    const itemRight = repeater.itemAt(index)
-
-                    if (itemLeft) {
-                        if (itemRight) {
-                            itemLeft.item.Navigation.rightItem = itemRight.item
-                            itemRight.item.Navigation.leftItem = itemLeft.item
-                        }
-                        else
-                            itemLeft.item.Navigation.rightItem = null
-                    }
-                    else if (itemRight) {
-                        itemRight.item.Navigation.leftItem = null
-                    }
-                }
-
-                function recoverFocus(_index) {
-                    if (item == null) return
-
-                    if (!controlLayout.visible)
-                        return
-
-                    if (_index === undefined)
-                        _index = index
-
-                    for (let i = 1; i <= Math.max(_index, repeater.count - (_index + 1)); ++i) {
-                         if (i <= _index) {
-                             const leftItem = repeater.itemAt(_index - i)
-
-                             if (_focusIfFocusable(leftItem))
-                                 return
-                         }
-
-                         if (_index + i <= repeater.count - 1) {
-                             const rightItem = repeater.itemAt(_index + i)
-
-                             if (_focusIfFocusable(rightItem))
-                                 return
-                         }
-                    }
-
-                    // focus to other alignment if focusable control
-                    // in the same alignment is not found:
-                    if (!!controlLayout.Navigation.rightItem) {
-                        controlLayout.Navigation.defaultNavigationRight()
-                    } else if (!!controlLayout.Navigation.leftItem) {
-                        controlLayout.Navigation.defaultNavigationLeft()
-                    } else {
-                        controlLayout.altFocusAction()
-                    }
-                }
-
-                // Private
-
-                function _focusIfFocusable(_loader) {
-                    if (!!_loader && !!_loader.item && _loader.item.focus) {
-                        if (item.focusReason !== undefined)
-                            _loader.item.forceActiveFocus(item.focusReason)
-                        else {
-                            console.warn("focusReason is not available in %1!".arg(item))
-                            _loader.item.forceActiveFocus()
-                        }
-                        return true
-                    } else {
-                        return false
-                    }
-                }
-            }
+            requestLockUnlockAutoHide: controlLayout.requestLockUnlockAutoHide
+            menuOpened: controlLayout.menuOpened
         }
 
         Item {
-            Layout.fillWidth: (controlLayout.alignment === Qt.AlignLeft)
+            Layout.fillWidth: visible
+            visible: (controlLayout.alignment & Qt.AlignLeft)
         }
     }
 }


=====================================
modules/gui/qt/player/qml/ControlRepeater.qml
=====================================
@@ -0,0 +1,227 @@
+/*****************************************************************************
+ * Copyright (C) 2023 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+import QtQuick 2.12
+import QtQuick.Layouts 1.12
+import QtQuick.Controls 2.12
+
+import org.videolan.vlc 0.1
+import org.videolan.compat 0.1
+
+Repeater {
+    id: repeater
+
+    property int alignment: Qt.AlignVCenter
+
+    property real availableWidth: Number.MAX_VALUE
+    property real availableHeight: Number.MAX_VALUE
+
+    property var menuOpened
+    property var requestLockUnlockAutoHide
+
+    // NOTE: We apply the 'navigation chain' after adding the item.
+    onItemAdded: item.applyNavigation()
+
+    onItemRemoved: {
+        // NOTE: We update the 'navigation chain' after removing the item.
+        item.removeNavigation()
+
+        item.recoverFocus(index)
+    }
+
+    delegate: Loader {
+        id: loader
+
+        // Settings
+
+        source: PlayerControlbarControls.control(model.id).source
+
+        focus: (index === 0)
+
+        Layout.fillWidth: (item && item.Layout.minimumWidth > 0)
+        Layout.minimumWidth: Layout.fillWidth ? item.Layout.minimumWidth : (item ? item.implicitWidth : -1)
+        Layout.maximumWidth: Layout.fillWidth ? (item ? item.implicitWidth : -1) : -1
+
+        Layout.minimumHeight: 0
+        Layout.maximumHeight: Math.min((item && item.implicitHeight <= 0) ? Number.MAX_VALUE : item.implicitHeight, repeater.availableHeight)
+        Layout.fillHeight: true
+
+        Layout.alignment: repeater.alignment
+
+        BindingCompat on visible {
+            delayed: true // this is important
+            value: (loader.x + loader.Layout.minimumWidth <= repeater.availableWidth)
+        }
+
+        // Events
+
+        onActiveFocusChanged: {
+            if (activeFocus && (!!item && !item.focus)) {
+                recoverFocus()
+            }
+        }
+
+        onLoaded: {
+            // control should not request focus if they are not enabled:
+            item.focus = Qt.binding(function() { return item.enabled && item.visible })
+
+            // navigation parent of control is always controlLayout
+            // so it can be set here unlike leftItem and rightItem:
+            item.Navigation.parentItem = repeater
+
+            if (typeof item.activeFocusOnTab === "boolean")
+                item.activeFocusOnTab = true
+
+            // FIXME: Do we really need to enforce a defaultSize ?
+            if (item.size !== undefined)
+                item.size = Qt.binding(function() { return defaultSize; })
+
+            item.visible = Qt.binding(function() { return loader.visible })
+
+            if (item.requestLockUnlockAutoHide)
+                item.requestLockUnlockAutoHide.connect(repeater.requestLockUnlockAutoHide)
+
+            if (item.menuOpened)
+                item.menuOpened.connect(repeater.menuOpened)
+        }
+
+        // Connections
+
+        Connections {
+            target: item
+
+            enabled: loader.status === Loader.Ready
+
+            onEnabledChanged: {
+                if (activeFocus && !item.enabled) // Loader has focus but item is not enabled
+                    recoverFocus()
+            }
+
+            onVisibleChanged: {
+                if (activeFocus && !item.visible)
+                    recoverFocus()
+            }
+        }
+
+        // Functions
+
+        function applyNavigation() {
+            if (item === null) return
+
+            const itemLeft  = repeater.itemAt(index - 1)
+            const itemRight = repeater.itemAt(index + 1)
+
+            if (itemLeft) {
+                const componentLeft = itemLeft.item
+
+                if (componentLeft)
+                {
+                    item.Navigation.leftItem = componentLeft
+
+                    componentLeft.Navigation.rightItem = item
+                }
+            }
+
+            if (itemRight) {
+                const componentRight = itemRight.item
+
+                if (componentRight)
+                {
+                    item.Navigation.rightItem = componentRight
+
+                    componentRight.Navigation.leftItem = item
+                }
+            }
+        }
+
+        function removeNavigation() {
+            if (item === null) return
+
+            const itemLeft = repeater.itemAt(index - 1)
+
+            // NOTE: The current item was removed from the repeater so we test against the
+            //       same index.
+            const itemRight = repeater.itemAt(index)
+
+            if (itemLeft) {
+                if (itemRight) {
+                    itemLeft.item.Navigation.rightItem = itemRight.item
+                    itemRight.item.Navigation.leftItem = itemLeft.item
+                }
+                else
+                    itemLeft.item.Navigation.rightItem = null
+            }
+            else if (itemRight) {
+                itemRight.item.Navigation.leftItem = null
+            }
+        }
+
+        function recoverFocus(_index) {
+            if (item === null) return
+
+            const controlLayout = repeater.Navigation.parentItem
+
+            if (!controlLayout || !controlLayout.visible)
+                return
+
+            if (_index === undefined)
+                _index = index
+
+            for (let i = 1; i <= Math.max(_index, repeater.count - (_index + 1)); ++i) {
+                 if (i <= _index) {
+                     const leftItem = repeater.itemAt(_index - i)
+
+                     if (_focusIfFocusable(leftItem))
+                         return
+                 }
+
+                 if (_index + i <= repeater.count - 1) {
+                     const rightItem = repeater.itemAt(_index + i)
+
+                     if (_focusIfFocusable(rightItem))
+                         return
+                 }
+            }
+
+            // focus to other alignment if focusable control
+            // in the same alignment is not found:
+            if (!!controlLayout.Navigation.rightItem) {
+                controlLayout.Navigation.defaultNavigationRight()
+            } else if (!!controlLayout.Navigation.leftItem) {
+                controlLayout.Navigation.defaultNavigationLeft()
+            } else if (controlLayout.altFocusAction) {
+                controlLayout.altFocusAction()
+            }
+        }
+
+        // Private
+
+        function _focusIfFocusable(_loader) {
+            if (!!_loader && !!_loader.item && _loader.item.focus) {
+                if (item.focusReason !== undefined)
+                    _loader.item.forceActiveFocus(item.focusReason)
+                else {
+                    console.warn("focusReason is not available in %1!".arg(item))
+                    _loader.item.forceActiveFocus()
+                }
+                return true
+            } else {
+                return false
+            }
+        }
+    }
+}


=====================================
modules/gui/qt/player/qml/PlayerControlLayout.qml
=====================================
@@ -17,8 +17,10 @@
  *****************************************************************************/
 
 import QtQuick 2.12
+import QtQuick.Layouts 1.12
 
 import org.videolan.vlc 0.1
+import org.videolan.compat 0.1
 
 import "qrc:///style/"
 import "qrc:///widgets/" as Widgets
@@ -33,7 +35,7 @@ FocusScope {
 
     property real spacing: VLCStyle.margin_normal // spacing between controls
 
-    property real layoutSpacing: VLCStyle.margin_xlarge // spacing between layouts (left, center, and right)
+    property real layoutSpacing: VLCStyle.margin_xxlarge // spacing between layouts (left, center, and right)
 
     property int identifier: -1
 
@@ -49,10 +51,6 @@ FocusScope {
         colorSet: ColorContext.Window
     }
 
-    // Private
-
-    property int _minimumSpacing: layoutSpacing - spacing
-
     // Signals
 
     signal requestLockUnlockAutoHide(bool lock)
@@ -61,102 +59,120 @@ FocusScope {
 
     // Settings
 
-    // NOTE: We want a smaller ControlBar when the controls are pinned.
-    implicitHeight: (MainCtx.pinVideoControls) ? VLCStyle.controlLayoutHeightPinned
-                                               : VLCStyle.controlLayoutHeight
-
-    // Events
-
-    Component.onCompleted: {
-        console.assert(identifier >= 0)
+    implicitWidth: loaderLeftRight.active ? loaderLeftRight.implicitWidth
+                                          : (loaderLeft.implicitWidth + loaderCenter.implicitWidth + loaderRight.implicitWidth)
 
-        _updateLayout()
-    }
+    implicitHeight: Math.max(loaderLeft.implicitHeight, loaderCenter.implicitHeight, loaderRight.implicitHeight)
 
-    onWidthChanged: _updateLayout()
-
-    // Functions
+    // Events
 
-    function _updateLayout() {
-        let item = loaderCenter.item
+    Component.onCompleted: console.assert(identifier >= 0)
 
-        // NOTE: Sometimes this gets called before the item is loaded.
-        if (item === null)
-            return
+    // Children
 
-        if (item.count) {
+    Loader {
+        id: loaderLeftRight
 
-            loaderCenter.width = Math.min(loaderCenter.implicitWidth, width)
+        anchors.fill: parent
 
-            loaderLeft.width = loaderCenter.x - _minimumSpacing
+        active: !loaderCenter.active &&
+                playerControlLayout.model &&
+                ((playerControlLayout.model.left && (playerControlLayout.model.left.count > 0)) ||
+                (playerControlLayout.model.right && (playerControlLayout.model.right.count > 0)))
 
-            loaderRight.width = width - loaderCenter.x - loaderCenter.width - _minimumSpacing
+        focus: active
 
-        } else if (loaderRight.item.count) {
+        sourceComponent: RowLayout {
+            spacing: playerControlLayout.spacing
 
-            const implicitLeft = loaderLeft.implicitWidth
-            const implicitRight = loaderRight.implicitWidth
+            focus: true
 
-            const total = implicitLeft + implicitRight
+            // TODO: Qt >= 5.13 Use QConcatenateTablesProxyModel
+            //       instead of multiple repeaters
 
-            let size = total + _minimumSpacing
+            ControlRepeater {
+                id: leftRepeater
+                model: ControlListFilter {
+                    sourceModel: playerControlLayout.model.left
 
-            if (size > width) {
-                size = width - _minimumSpacing
+                    player: Player
+                    ctx: MainCtx
+                }
 
-                // NOTE: When both sizes are equals we expand on the left.
-                if (implicitLeft >= implicitRight) {
+                Navigation.parentItem: playerControlLayout
+                Navigation.rightAction: function() {
+                    const item = rightRepeater.itemAt(0)
+                    if (item)
+                        item.forceActiveFocus(Qt.TabFocusReason)
+                    else
+                        return false
+                }
 
-                    loaderRight.width = Math.round(size * (implicitRight / total))
+                availableWidth: loaderLeftRight.width
+                availableHeight: loaderLeftRight.height
+            }
 
-                    item = loaderRight.item
+            Item {
+                function containsVisibleItem(repeater) {
+                    for (let i = 0; i < repeater.count; ++i) {
+                        const item = repeater.itemAt(i)
 
-                    if (item === null)
-                        return
+                        if (item && item.visible)
+                            return true
+                    }
 
-                    const contentWidth = item.contentWidth
+                    return false
+                }
 
-                    // NOTE: We assign the remaining width based on the contentWidth.
-                    if (contentWidth)
-                        loaderLeft.width = width - contentWidth - _minimumSpacing
-                    else
-                        loaderLeft.width = width
-                } else {
-                    loaderLeft.width = Math.round(size * (implicitLeft / total))
+                Layout.minimumWidth: (containsVisibleItem(leftRepeater) && containsVisibleItem(rightRepeater)) ? playerControlLayout.layoutSpacing
+                                                                                                               : 0
 
-                    item = loaderLeft.item
+                Layout.fillWidth: true
+                visible: true
+            }
 
-                    if (item === null)
-                        return
+            ControlRepeater {
+                id: rightRepeater
+                model: ControlListFilter {
+                    sourceModel: playerControlLayout.model.right
 
-                    const contentWidth = item.contentWidth
+                    player: Player
+                    ctx: MainCtx
+                }
 
-                    // NOTE: We assign the remaining width based on the contentWidth.
-                    if (contentWidth)
-                        loaderRight.width = width - contentWidth - _minimumSpacing
+                Navigation.parentItem: playerControlLayout
+                Navigation.leftAction: function() {
+                    const item = leftRepeater.itemAt(leftRepeater.count - 1)
+                    if (item)
+                        item.forceActiveFocus(Qt.BacktabFocusReason)
                     else
-                        loaderRight.width = width
+                        return false
                 }
-            } else {
-                loaderLeft.width = implicitLeft
-                loaderRight.width = implicitRight
+
+                availableWidth: loaderLeftRight.width
+                availableHeight: loaderLeftRight.height
             }
-        } else
-            loaderLeft.width = width
+        }
     }
 
-    // Children
 
     Loader {
         id: loaderLeft
 
-        anchors.left: parent.left
-        anchors.top: parent.top
-        anchors.bottom: parent.bottom
+        anchors {
+            right: loaderCenter.left
+            left: parent.left
+            top: parent.top
+            bottom: parent.bottom
+
+            // Spacing for the filler item acts as padding
+            rightMargin: layoutSpacing - spacing
+        }
 
-        active: !!playerControlLayout.model && !!playerControlLayout.model.left
+        active: !!playerControlLayout.model && !!playerControlLayout.model.left && (playerControlLayout.model.left.count > 0) &&
+                !loaderLeftRight.active
 
-        focus: true
+        focus: active
 
         sourceComponent: ControlLayout {
             model: ControlListFilter {
@@ -166,7 +182,7 @@ FocusScope {
                 ctx: MainCtx
             }
 
-            alignment: Qt.AlignLeft
+            alignment: (Qt.AlignVCenter | Qt.AlignLeft)
 
             focus: true
 
@@ -175,10 +191,6 @@ FocusScope {
             Navigation.parentItem: playerControlLayout
             Navigation.rightItem: loaderCenter.item
 
-            onImplicitWidthChanged: playerControlLayout._updateLayout()
-
-            onCountChanged: playerControlLayout._updateLayout()
-
             onRequestLockUnlockAutoHide: playerControlLayout.requestLockUnlockAutoHide(lock)
 
             onMenuOpened: playerControlLayout.menuOpened(menu)
@@ -188,12 +200,37 @@ FocusScope {
     Loader {
         id: loaderCenter
 
-        anchors.top: parent.top
-        anchors.bottom: parent.bottom
+        anchors {
+            horizontalCenter: parent.horizontalCenter
+            top: parent.top
+            bottom: parent.bottom
+        }
 
-        anchors.horizontalCenter: parent.horizontalCenter
+        // TODO: "ControlListFilter"'s count......
+        active: !!playerControlLayout.model && !!playerControlLayout.model.center && (playerControlLayout.model.center.count > 0)
 
-        active: !!playerControlLayout.model && !!playerControlLayout.model.center
+        BindingCompat on width {
+            delayed: true
+            when: loaderCenter._componentCompleted
+            value: {
+                const item = loaderCenter.item
+
+                const minimumWidth = (item && item.Layout.minimumWidth > 0) ? item.Layout.minimumWidth : implicitWidth
+                const maximumWidth = (item && item.Layout.maximumWidth > 0) ? item.Layout.maximumWidth : implicitWidth
+
+                if ((loaderLeft.active && (loaderLeft.width > 0)) || (loaderRight.active && (loaderRight.width > 0))) {
+                    return minimumWidth
+                } else {
+                    return Math.min(loaderCenter.parent.width, maximumWidth)
+                }
+            }
+        }
+
+        property bool _componentCompleted: false
+
+        Component.onCompleted: {
+            _componentCompleted = true
+        }
 
         sourceComponent: ControlLayout {
             model: ControlListFilter {
@@ -211,10 +248,6 @@ FocusScope {
             Navigation.leftItem: loaderLeft.item
             Navigation.rightItem: loaderRight.item
 
-            onImplicitWidthChanged: playerControlLayout._updateLayout()
-
-            onCountChanged: playerControlLayout._updateLayout()
-
             onRequestLockUnlockAutoHide: playerControlLayout.requestLockUnlockAutoHide(lock)
 
             onMenuOpened: playerControlLayout.menuOpened(menu)
@@ -224,11 +257,18 @@ FocusScope {
     Loader {
         id: loaderRight
 
-        anchors.right: parent.right
-        anchors.top: parent.top
-        anchors.bottom: parent.bottom
+        anchors {
+            left: loaderCenter.right
+            right: parent.right
+            top: parent.top
+            bottom: parent.bottom
+
+            // Spacing for the filler item acts as padding
+            leftMargin: layoutSpacing - spacing
+        }
 
-        active: !!playerControlLayout.model && !!playerControlLayout.model.right
+        active: !!playerControlLayout.model && !!playerControlLayout.model.right && (playerControlLayout.model.right.count > 0) &&
+                !loaderLeftRight.active
 
         sourceComponent: ControlLayout {
             model: ControlListFilter {
@@ -238,7 +278,7 @@ FocusScope {
                 ctx: MainCtx
             }
 
-            alignment: Qt.AlignRight
+            alignment: (Qt.AlignVCenter | Qt.AlignRight)
 
             focus: true
 
@@ -247,10 +287,6 @@ FocusScope {
             Navigation.parentItem: playerControlLayout
             Navigation.leftItem: loaderCenter.item
 
-            onImplicitWidthChanged: playerControlLayout._updateLayout()
-
-            onCountChanged: playerControlLayout._updateLayout()
-
             onRequestLockUnlockAutoHide: playerControlLayout.requestLockUnlockAutoHide(lock)
 
             onMenuOpened: playerControlLayout.menuOpened(menu)


=====================================
modules/gui/qt/player/qml/controlbarcontrols/ArtworkInfoWidget.qml
=====================================
@@ -21,6 +21,7 @@ import QtQuick.Controls 2.12
 import QtQuick.Layouts 1.12
 
 import org.videolan.vlc 0.1
+import org.videolan.compat 0.1
 
 import "qrc:///widgets/" as Widgets
 import "qrc:///style/"
@@ -32,16 +33,11 @@ AbstractButton {
 
     property bool paintOnly: false
 
-    readonly property real minimumWidth: coverRect.implicitWidth +
-                                         + (leftPadding + rightPadding)
+    Layout.minimumWidth: height
 
-    property int maximumHeight: _preferredHeight
+    implicitHeight: 0
 
-    readonly property int preferredWidth: minimumWidth + contentItem.spacing * 2
-                                          +
-                                          Math.max(titleLabel.implicitWidth,
-                                                   artistLabel.implicitWidth,
-                                                   progressIndicator.implicitWidth)
+    property bool _keyPressed: false
 
     readonly property ColorContext colorContext: ColorContext {
         id: theme
@@ -52,10 +48,6 @@ AbstractButton {
         hovered: root.hovered
     }
 
-    property int _preferredHeight: VLCStyle.dp(60, VLCStyle.scale)
-
-    property bool _keyPressed: false
-
     // Settings
 
     text: I18n.qtr("Open player")
@@ -102,57 +94,51 @@ AbstractButton {
     // Children
 
     contentItem: RowLayout {
-        spacing: infoColumn.visible ? VLCStyle.margin_xsmall : 0
+        spacing: VLCStyle.margin_xsmall
 
-        Rectangle {
-            id: coverRect
+        Image {
+            id: coverImage
 
-            implicitWidth: implicitHeight
+            Layout.fillHeight: true
+            Layout.preferredWidth: root.height
 
-            implicitHeight: Math.min(root._preferredHeight, root.maximumHeight)
+            sourceSize.width: root.height
 
-            color: theme.bg.primary
-
-            Widgets.DoubleShadow {
-                anchors.centerIn: cover
-                width: cover.paintedWidth
-                height: cover.paintedHeight
-                z: -1
+            source: {
+                if (!paintOnly && Player.artwork && Player.artwork.toString())
+                    return Player.artwork
+                else
+                    return VLCStyle.noArtAlbumCover
+            }
 
-                primaryBlurRadius: VLCStyle.dp(3, VLCStyle.scale)
-                primaryVerticalOffset: VLCStyle.dp(1, VLCStyle.scale)
+            fillMode: Image.PreserveAspectFit
 
-                secondaryBlurRadius: VLCStyle.dp(14, VLCStyle.scale)
-                secondaryVerticalOffset: VLCStyle.dp(6, VLCStyle.scale)
-            }
+            asynchronous: true
 
-            Widgets.ScaledImage {
-                id: cover
+            Accessible.role: Accessible.Graphic
+            Accessible.name: I18n.qtr("Cover")
 
-                anchors.fill: parent
+            ToolTip.visible: infoColumn.width < infoColumn.implicitWidth
+                             && (root.hovered || root.visualFocus)
+            ToolTip.delay: VLCStyle.delayToolTipAppear
+            ToolTip.text: I18n.qtr("%1\n%2\n%3").arg(titleLabel.text)
+                                                .arg(artistLabel.text)
+                                                .arg(progressIndicator.text)
 
-                source: {
-                    if (!paintOnly
-                        && Player.artwork
-                        && Player.artwork.toString())
-                        Player.artwork
-                    else
-                        VLCStyle.noArtAlbumCover
-                }
+            Widgets.DoubleShadow {
+                anchors.centerIn: coverImage
+                anchors.alignWhenCentered: false
 
-                fillMode: Image.PreserveAspectFit
+                implicitWidth: coverImage.paintedWidth
+                implicitHeight: coverImage.paintedHeight
 
-                asynchronous: true
+                z: -1
 
-                Accessible.role: Accessible.Graphic
-                Accessible.name: I18n.qtr("Cover")
+                primaryBlurRadius: VLCStyle.dp(3, VLCStyle.scale)
+                primaryVerticalOffset: VLCStyle.dp(1, VLCStyle.scale)
 
-                ToolTip.visible: infoColumn.width < infoColumn.implicitWidth
-                                 && (root.hovered || root.visualFocus)
-                ToolTip.delay: VLCStyle.delayToolTipAppear
-                ToolTip.text: I18n.qtr("%1\n%2\n%3").arg(titleLabel.text)
-                                                    .arg(artistLabel.text)
-                                                    .arg(progressIndicator.text)
+                secondaryBlurRadius: VLCStyle.dp(14, VLCStyle.scale)
+                secondaryVerticalOffset: VLCStyle.dp(6, VLCStyle.scale)
             }
         }
 
@@ -160,15 +146,19 @@ AbstractButton {
             id: infoColumn
 
             Layout.fillWidth: true
-            Layout.preferredHeight: coverRect.height
+            Layout.fillHeight: true
             Layout.minimumWidth: 0.1 // FIXME: Qt layout bug
 
+            spacing: 0
+
             Widgets.MenuLabel {
                 id: titleLabel
 
                 Layout.fillWidth: true
                 Layout.fillHeight: true
 
+                visible: text.length > 0
+
                 text: {
                     if (paintOnly)
                         I18n.qtr("Title")
@@ -184,12 +174,18 @@ AbstractButton {
                 Layout.fillWidth: true
                 Layout.fillHeight: true
 
+                BindingCompat on visible {
+                    delayed: (MainCtx.qtVersion() < MainCtx.qtVersionCheck(5, 15, 8))
+                    value: (infoColumn.height > infoColumn.implicitHeight) && (artistLabel.text.length > 0)
+                }
+
                 text: {
                     if (paintOnly)
                         I18n.qtr("Artist")
                     else
                         Player.artist
                 }
+
                 color: theme.fg.secondary
             }
 
@@ -199,7 +195,7 @@ AbstractButton {
                 Layout.fillWidth: true
                 Layout.fillHeight: true
 
-                visible: (infoColumn.height >= root._preferredHeight)
+                visible: text.length > 0
 
                 text: {
                     if (paintOnly)


=====================================
modules/gui/qt/player/qml/controlbarcontrols/ExpandingSpacerWidget.qml
=====================================
@@ -17,6 +17,7 @@
  *****************************************************************************/
 import QtQuick 2.12
 import QtQuick.Templates 2.12 as T
+import QtQuick.Layouts 1.12
 
 import org.videolan.vlc 0.1
 
@@ -27,13 +28,13 @@ import "qrc:///style/"
 Item {
     enabled: false
 
-    implicitWidth: paintOnly ? VLCStyle.widthExtendedSpacer : Number.MAX_VALUE
+    implicitWidth: VLCStyle.widthExtendedSpacer
     implicitHeight: VLCStyle.icon_toolbar
 
     property bool paintOnly: false
     property alias spacetextExt: spacetext
 
-    readonly property real minimumWidth: 0
+    Layout.minimumWidth: 1
 
     readonly property ColorContext colorContext: ColorContext {
         id: theme


=====================================
modules/gui/qt/player/qml/controlbarcontrols/PlayButton.qml
=====================================
@@ -30,8 +30,6 @@ T.Control {
 
     // Properties
 
-    property int maximumHeight: VLCStyle.icon_medium
-
     property bool paintOnly: false
 
     readonly property ColorContext colorContext: ColorContext {
@@ -53,9 +51,8 @@ T.Control {
 
     // Settings
 
-    implicitWidth: implicitHeight
-
-    implicitHeight: Math.min(VLCStyle.icon_medium, maximumHeight)
+    implicitWidth: implicitBackgroundWidth + leftInset + rightInset
+    implicitHeight: implicitBackgroundHeight + topInset + bottomInset
 
     scale: (_keyOkPressed || (mouseArea.pressed && cursorInside)) ? 0.95
                                                                   : 1.00
@@ -230,6 +227,9 @@ T.Control {
     }
 
     background: Item {
+        implicitWidth: height
+        implicitHeight: VLCStyle.icon_medium
+
         // TODO: Qt >= 5.15 use inline component for the drop shadows
         Widgets.DropShadowImage {
             id: hoverShadow


=====================================
modules/gui/qt/vlc.qrc
=====================================
@@ -318,6 +318,7 @@
         <file alias="PIPPlayer.qml">player/qml/PIPPlayer.qml</file>
         <file alias="PlayerControlLayout.qml">player/qml/PlayerControlLayout.qml</file>
         <file alias="ControlLayout.qml">player/qml/ControlLayout.qml</file>
+        <file alias="ControlRepeater.qml">player/qml/ControlRepeater.qml</file>
         <file alias="PlaybackSpeed.qml">player/qml/PlaybackSpeed.qml</file>
         <file alias="PlayerPlaylistVisibilityFSM.qml">player/qml/PlayerPlaylistVisibilityFSM.qml</file>
         <file alias="PlayerBlurredBackground.qml">player/qml/PlayerBlurredBackground.qml</file>



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/10a4528c1ef62d3703172c94415eff760acd7584...d4d2c619c0da284fb272b3fc5a57ab3c965b25a4

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/10a4528c1ef62d3703172c94415eff760acd7584...d4d2c619c0da284fb272b3fc5a57ab3c965b25a4
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