Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/gui/qt/maininterface/qml/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 40 kB image not shown  

Quelle  MainDisplay.qml  Sprache: unbekannt

 
Spracherkennung für: .qml vermutete Sprache: Delphi {Delphi[48] SPS[48] ABAP[44]} [Methode: maximale Elemente, drei Dimensionen]

/*****************************************************************************
 * Copyright (C) 2019 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
import QtQuick.Layouts

import VLC.Style
import VLC.MainInterface
import VLC.Widgets as Widgets
import VLC.Playlist
import VLC.Player

import VLC.Util
import VLC.Dialogs

FocusScope {
    id: g_mainDisplay

    Accessible.role: Accessible.Pane
    Accessible.name: qsTr("Main display")
    AccessibleCompat.id: "mainDisplay"

    // Properties

    property bool hasMiniPlayer: miniPlayer.visible

    // NOTE: The main view must be above the indexing bar and the mini player.
    property real displayMargin: (height - miniPlayer.y) +
                                 (loaderUpdatePane.active ? loaderUpdatePane.height : 0)

    //MainDisplay behave as a PageLoader
    property alias pagePrefix: stackView.pagePrefix

    readonly property int positionSliderY: {
        var size = miniPlayer.y + miniPlayer.sliderY

        if (MainCtx.pinVideoControls)
            return size - VLCStyle.margin_xxxsmall
        else
            return size
    }

    property bool _showMiniPlayer: false

    property bool _showCSD: MainCtx.clientSideDecoration
        && !(MainCtx.intfMainWindow.visibility === Window.FullScreen)

    // functions

    //MainDisplay behave as a PageLoader
    function loadView(path, properties, focusReason) {
        const found = stackView.loadView(path, properties, focusReason)
        if (!found)
            return

        if (Player.hasVideoOutput && MainCtx.hasEmbededVideo)
            _showMiniPlayer = true
    }

    function loadCurrentHistoryView(focusReason) {
        loadView(History.viewPath, History.viewProp, focusReason)
        contextSaver.restore(History.viewPath)
    }

    Component.onCompleted: {
        if (MainCtx.canShowVideoPIP) {
            pipPlayerComponent.createObject(this)
        } else {
            if (MainCtx.hasEmbededVideo)
                MainPlaylistController.stop()
        }

        if (History.previousEmpty) {
            History.update(["home"])
        }
        loadCurrentHistoryView(Qt.OtherFocusReason)
    }

    Navigation.cancelAction: function() {
        History.previous(Qt.BacktabFocusReason)
    }

    Keys.onPressed: (event) => {
        if (KeyHelper.matchSearch(event)) {
            MainCtx.search.askShow()
            event.accepted = true
        }
        //unhandled keys are forwarded as hotkeys
        if (!event.accepted)
            MainCtx.sendHotkey(event.key, event.modifiers);
    }

    readonly property var pageModel: [
        {
            name: "home",
            url: MainCtx.mediaLibraryAvailable ?
                 "qrc:///qt/qml/VLC/MediaLibrary/HomeDisplay.qml" :
                 "qrc:///qt/qml/VLC/MainInterface/NoMedialibHome.qml"
        }, {
            name: "video",
            url: "qrc:///qt/qml/VLC/MediaLibrary/VideoDisplay.qml"
        }, {
            name: "music",
            url: "qrc:///qt/qml/VLC/MediaLibrary/MusicDisplay.qml"
        }, {
            name: "network",
            url: "qrc:///qt/qml/VLC/Network/BrowseDisplay.qml"
        }, {
            name: "discover",
            url: "qrc:///qt/qml/VLC/Network/DiscoverDisplay.qml"
        }, {
            name: "mlsettings",
            url: "qrc:///qt/qml/VLC/MediaLibrary/MLFoldersSettings.qml"
        }
    ]

    PinchHandler {
        id: interfaceScalePinchHandler

        acceptedDevices: PointerDevice.TouchScreen | PointerDevice.TouchPad

        minimumPointCount: 2
        maximumPointCount: 2

        target: null

        // Qt version check in disguise, only available starting with 6.5:
        enabled: !!scaleAxis?.activeValueChanged

        // Default in QPlatformTheme::defaultThemeHint is 10
        dragThreshold: ((Application.styleHints?.startDragDistance ?? 10) * 4)

        property double accumulatedDelta: 0.0

        function adjustScaleFactor() {
            // We don't want too fine change, since changing it requires reloading
            // all images from disk:
            if (accumulatedDelta >= 0.5) {
                MainCtx.setIntfUserScaleFactor(+((MainCtx.getIntfUserScaleFactor() * accumulatedDelta).toFixed(2)))
                accumulatedDelta = 0.0
            }
        }

        Component.onCompleted: {
            if (interfaceScalePinchHandler?.scaleAxis?.activeValueChanged) {
                scaleAxis.activeValueChanged.connect(interfaceScalePinchHandler, onScaleAxisActiveValueChanged)
            }
        }

        function onScaleAxisActiveValueChanged(delta) {
            if (delta < 0.25)
                return

            accumulatedDelta += delta

            // Naive compression:
            Qt.callLater(interfaceScalePinchHandler.adjustScaleFactor)
        }

        onActiveChanged: {
            if (!active) {
                accumulatedDelta = 0.0
            }
        }
    }

    ModelSortSettingHandler {
        id: contextSaver
    }

    Connections {
        target: MainCtx.sort

        function onCriteriaChanged(criteria) {
            contextSaver.save(History.viewPath)
        }

        function onOrderChanged(order) {
            contextSaver.save(History.viewPath)
        }
    }

    Connections {
        target: History
        function onNavigate(focusReason) {
            loadCurrentHistoryView(focusReason)
        }
    }

    ColorContext {
        id: theme
        palette: VLCStyle.palette
        colorSet: ColorContext.View
    }

    Loader {
        id: voronoiSnowLoader

        z: 3.5
        source: "qrc:///qt/qml/VLC/Widgets/VoronoiSnow.qml"
        anchors.fill: parent
        active: false

        function toggleActive() {
            voronoiSnowLoader.active = !voronoiSnowLoader.active
        }

        Component.onCompleted: {
            if (MainCtx.useXmasCone()) {
                MainCtx.kc_pressed.connect(voronoiSnowLoader.toggleActive)
            }
        }
    }

    MenuTopbar {
        id: menuTopbar
        z: 6

        visible: MainCtx.hasToolbarMenu
        enabled: visible

        anchors {
            top: parent.top
            right: parent.right
            left: parent.left
        }

        plListView: {
            if (playlistLoader.active)
                return playlistLoader.item
            else if (playlistWindowLoader.status === Loader.Ready)
                return playlistWindowLoader.item.playlistView
            else
                return null
        }
    }

    LocalTopbar {
        id: localTopbar
        z: 5

        anchors {
            top: menuTopbar.visible ? menuTopbar.bottom : parent.top
            left: parent.left
            right: parent.right
        }

        leftPadding: VLCStyle.applicationHorizontalMargin
        rightPadding: VLCStyle.applicationHorizontalMargin
        topPadding: menuTopbar.visible ? 0 : VLCStyle.applicationVerticalMargin

        plListView: {
            if (playlistLoader.active)
                return playlistLoader.item
            else if (playlistWindowLoader.status === Loader.Ready)
                return playlistWindowLoader.item.playlistView
            else
                return null
        }

        navigationVisible: pannelVisiblity.showNavigation
        playqueueVisible: pannelVisiblity.showPlayqueue

        Navigation.parentItem: g_mainDisplay
        Navigation.upItem: menuTopbar
        Navigation.downItem: mainRow

        onToggleNavigationVisibility: pannelVisiblity.toggleNavigationVisibility()
        onTogglePlayqueueVisibility:  pannelVisiblity.togglePlayqueueVisibility()
    }

    FocusScope {
        id: mainRow

        anchors {
            top: localTopbar.bottom
            right: parent.right
            left: parent.left
            bottom: parent.bottom
        }

        z: 0

        Navigation.parentItem: g_mainDisplay

        Rectangle {
            id: stackViewParent

            // This rectangle is used to display the effect in
            // the area of miniplayer background.
            // We can not directly apply the effect on the
            // view because its size is limited and the effect
            // should exceed the size. Also, it is beneficial
            // to have a rectangle here because if the background
            // is transparent we would lose subpixel font rendering
            // support.

            z: 1

            anchors.fill: parent

            implicitWidth: stackView.implicitWidth
            implicitHeight: stackView.implicitHeight

            color: theme.bg.primary

            layer.enabled: MainCtx.backdropBlurRequested() &&
                           (GraphicsInfo.shaderType === GraphicsInfo.RhiShader) &&
                           (miniPlayer.visible || !!loaderUpdatePane.item?.visible)

            // Blurring requires to access neighbour pixels, thus the source texture should be bigger than
            // the effect so that the effect have access to the neighbor pixels for the pixels near the
            // border, where the extra size would depend on the blur configuration. When the source is
            // static, this problem is harder to notice, but when the source is not static, such as
            // during scrolling, not considering this causes glitches in the bottom side. `PartialEffect`,
            // since 03b0de26, already provides the effect the whole source texture with a proper sub-rect,
            // so the effect here can sample the top edge neighbour pixels, but for the bottom edge we
            // need to configure the layer:
            readonly property int edgeExtension: 16
            // The layer width is smaller than the item width, this is intentional because we do not want
            // to include the area that playqueue occupies in the layer since it is empty. Note that we
            // still want to do layering here, because even though the layer is smaller than the item
            // size, what we display is covered by the item size. The effect, which needs to cover the
            // item width is going to respect the empty area due to clamp to edge behavior, so we don't
            // need to use background coloring. It is currently a todo to further reduce video memory
            // consumption by covering the effect for only the area of interest, currently the blur
            // effect does not support having an extension area for postprocessing.
            layer.sourceRect: Qt.rect(stackView.x - edgeExtension,
                                      0,
                                      Helpers.alignUp(stackView.width + (2 * edgeExtension), alignNumber),
                                      Helpers.alignUp(height + edgeExtension, alignNumber))

            property real eDPR: MainCtx.effectiveDevicePixelRatio(Window.window) || 1.0
            readonly property int alignNumber: Helpers.denominatorForFloat(eDPR)

            Connections {
                target: MainCtx

                function onIntfDevicePixelRatioChanged() {
                    stackViewParent.eDPR = MainCtx.effectiveDevicePixelRatio(stackViewParent.Window.window) || 1.0
                }
            }

            Rectangle {
                // Extension of parent rectangle for edge and fractional scale alignment extension.
                anchors.fill: parent
                anchors.margins: -(stackViewParent.edgeExtension + fractionalScaleExtensionSize)

                // With fractional scale, if we align up the layer size to make sure the size is
                // an integer, we also need to extend here because the background must cover the
                // extension area. 8 should be enough for the fractions that we care (.25, .5, .75).
                // Note that the background extension here does not consume additional video memory.
                readonly property real fractionalScaleExtensionSize: (stackViewParent.alignNumber > 1 ? 8.0 : 0.0)

                visible: stackViewParent.layer.enabled && (height > 0 && width > 0)
                // We can't simply adjust the color because the color might not be opaque,
                // so we use border instead. Note that border is always placed inside the
                // rectangle:
                border.width: -anchors.margins
                border.color: parent.color
                color: "transparent"
            }

            layer.effect: Widgets.PartialEffect {
                id: stackViewParentLayerEffect

                // Setting `height` does not seem to work here. Anchoring the effect is not very nice, but it works:
                anchors.fill: stackViewParent // WARNING: layered item is not necessarily the visual parent of its layer effect.
                anchors.bottomMargin: (stackViewParent.height - stackViewParent.layer.sourceRect.height)
                // Layer width is limited to `stackView` width to save memory, in `PartialEffect` the source visual uses the
                // size of the `PartialEffect` unless `sourceVisualRect` is used, so we define the boundary in `PartialEffect`
                // for the source visual here. The effect rect exceeds the boundaries of `PartialEffect` due to this (see
                // `effectRect`), which is not particularly nice, but there is not much we can do about that here without
                // using `sourceVisualRect`, and saving video memory is considered more important:
                anchors.rightMargin: (stackViewParent.width - stackViewParent.layer.sourceRect.width - stackViewParent.layer.sourceRect.x)
                anchors.leftMargin: stackViewParent.layer.sourceRect.x

                blending: stackViewParent.color.a < (1.0 - Number.EPSILON)

                // Each pass of the blur effect also suffers from the border neighbour pixel issue mentioned
                // above, making all the borders problematic, to a less considerable extent. For that reason,
                // we extend both the top and the bottom edges and use viewport to prevent overdraw:
                effectRect: Qt.rect(-stackView.x,
                                    stackView.height - stackViewParent.edgeExtension,
                                    stackViewParent.width + 2 * stackViewParent.edgeExtension,
                                    loaderUpdatePane.height + miniPlayer.height + 2 * stackViewParent.edgeExtension)

                // WARNING: We are not using `sourceVisualRect` because it is not trivial to guarantee that
                //          the visual (`ShaderEffect`) scene graph sizing and sub-texturing are synchronized.
                //          This can cause the visual to deviate from 1:1 representation of the texture
                //          momentarily, leading to sizing glitches during initialization and animations.

                effect: frostedGlassEffect

                Widgets.FrostedGlassEffect {
                    id: frostedGlassEffect

                    ColorContext {
                        id: frostedTheme
                        palette: VLCStyle.palette
                        colorSet: ColorContext.Window
                    }

                    backgroundColor: (ready ? "transparent" : stackViewParent.color)
                    tint: frostedTheme.bg.secondary

                    // Prevent overdraw (the extension margin should not be painted).
                    // This also saves video memory compared to solely using visual rect.
                    // Note that viewport rect does not cover edge extension in y-axis
                    // but covers edge extension in x-axis because we don't want to
                    // have artifacts with regard to clamp-to-edge texture extension
                    // in x-axis (relevant with delegate background coloring in list
                    // view mode). Since we don't do texture extension in y-axis, we
                    // don't need to cover the edge extension in y-axis, which allows
                    // us to save some video memory (as opposed to `visualRect`).
                    viewportRect: Qt.rect(stackView.x,
                                          stackViewParent.edgeExtension,
                                          stackView.width + (2 * stackViewParent.edgeExtension),
                                          height - (2 * stackViewParent.edgeExtension))

                    visualRect: (stackView.width < stackViewParent.width) ? Qt.rect(stackViewParent.edgeExtension,
                                                                                    viewportRect.y,
                                                                                    width - (2 * stackViewParent.edgeExtension),
                                                                                    viewportRect.height)
                                                                          : Qt.rect(0, 0, 0, 0)
                }
            }

            Widgets.PageLoader {
                id: stackView

                focus: true

                anchors.fill: parent
                anchors.leftMargin: (sidebar.visible && (VLCStyle.isScreenSmall ? (sidebar.state === "") : true)) ? sidebar.width : 0.0
                anchors.rightMargin: (playlistLoader.shown && !VLCStyle.isScreenSmall)
                                     ? playlistLoader.width
                                     : 0
                anchors.bottomMargin: g_mainDisplay.displayMargin

                // Prevent animating x on collapse with overlayed side navigation bar:
                Binding on anchors.leftMargin {
                    when: (VLCStyle.isScreenSmall && sidebar.state === "" && sidebarTransition.running)
                    value: 0.0
                }

                pageModel: g_mainDisplay.pageModel

                leftPadding: sidebar.visible  ? 0 : VLCStyle.applicationHorizontalMargin

                rightPadding: playlistLoader.shown ? 0 : VLCStyle.applicationHorizontalMargin

                // Top-left corner rounding:
                Widgets.AcrylicBackground {
                    id: topLeftCornerBackground

                    z: 99
                    color: sidebar.background.color
                    visible: sidebar.visible && (stackViewTopLeftRadiusRectangle.topLeftRadius !== undefined)

                    anchors.left: stackView.left
                    anchors.leftMargin: -1
                    anchors.top: stackView.top

                    // (-1) is to prevent seams
                    width: stackViewTopLeftRadiusRectangle.width - 1
                    height: stackViewTopLeftRadiusRectangle.height - 1

                    Rectangle {
                        id: stackViewTopLeftRadiusRectangle

                        width: (stackViewTopLeftRadiusRectangle.topLeftRadius * 2)
                        height: (stackViewTopLeftRadiusRectangle.topLeftRadius * 2)

                        anchors.top: parent.top
                        anchors.left: parent.left
                        anchors.leftMargin: 1

                        color: stackViewParent.color

                        property real implicitTopLeftRadius: VLCStyle.mainView_topLeftRadius

                        Component.onCompleted: {
                            if (stackViewTopLeftRadiusRectangle.topLeftRadius !== undefined) // TODO: Directly set when minimum Qt is 6.7
                                stackViewTopLeftRadiusRectangle.topLeftRadius = Qt.binding(() => stackViewTopLeftRadiusRectangle.implicitTopLeftRadius)
                        }
                    }
                }

                onCurrentItemChanged: {
                    if (currentItem) {
                        {
                            // Main pages need to compensate for the mini player:

                            if (currentItem.displayMarginEnd !== undefined)
                                currentItem.displayMarginEnd = Qt.binding(() => { return g_mainDisplay.displayMargin })

                            if (currentItem.enableEndFade !== undefined)
                                currentItem.enableEndFade = Qt.binding(() => { return (g_mainDisplay.hasMiniPlayer === false) })
                        }
                    }
                }

                Navigation.parentItem: g_mainDisplay
                Navigation.upItem: localTopbar
                Navigation.rightItem: playlistLoader
                Navigation.leftItem: sidebar
                Navigation.downItem: loaderUpdatePane
            }
        }

        Rectangle {
            id: smallScreenOverlay

            // overlay for smallscreens
            z: 2

            anchors.top: parent.top
            anchors.left: parent.left
            anchors.right: parent.right

            height: parent.height - g_mainDisplay.displayMargin

            visible: VLCStyle.isScreenSmall && (playlistLoader.shown || (sidebar.visible && (sidebar.state === "expanded")))
            color: "black"
            opacity: 0.4

            MouseArea {
                anchors.fill: parent
                hoverEnabled: true
                onClicked:  pannelVisiblity.hideVisiblePanels()

                // Capture WheelEvents before they reach stackView
                onWheel: (wheel) => {
                    wheel.accepted = true
                }
            }
        }

        SideNavigationPane {
            id: sidebar

            z: 3

            anchors {
                top : parent.top
                left: parent.left
            }

            width: VLCStyle.isScreenReallySmall ? 0.0 : collapsedWidth

            property real collapsedWidth: VLCStyle.icon_normal + VLCStyle.margin_small + VLCStyle.margin_xsmall + VLCStyle.applicationHorizontalMargin

            delegateShowText: (state === "expanded")

            visible: (width > 0.0)

            height: parent.height - g_mainDisplay.displayMargin

            property int maximumWidth: Math.max(minimumWidth, (g_mainDisplay.width + sidebarResizeHandle.width) / 3)

            topPadding: 0
            leftPadding: 0
            rightPadding: sidebarResizeHandle.visible ? sidebarResizeHandle.visualBorder.width : 0.0
            bottomPadding: VLCStyle.applicationVerticalMargin + VLCStyle.margin_small

            safeAreaLeftMargin: VLCStyle.applicationHorizontalMargin

            useAcrylic: (VLCStyle.isScreenSmall ? (sidebar.state === "") : true) // See `694d51a7` for the reasoning.

            NavigationBarContextMenu {
                id: navigationBarContextMenu

                ctx: MainCtx

                model: sidebar.model

                onPageRequested: (uri) => {
                    if (stackView.isDefaulLoadedForPath([...uri]) ||
                        History.exactMatch(History.viewPath, uri)) {
                        stackView.positionContentAtBeginning()
                        return
                    }

                    History.push(uri)
                }
            }

            onItemClicked: (modelUri, item) => {
                console.assert(modelUri)

                console.assert(item)
                if ((sidebar.state !== 'expanded') && item.expandable) {
                    // TODO: Investigate opening the menu on hover, if
                    //       we switch to `Popup.Item` type of menus.
                    const mappedPos = item.mapToGlobal(item.width, 0)
                    navigationBarContextMenu.popup(item.index, mappedPos, !item.hovered)

                    return
                }

                if (stackView.isDefaulLoadedForPath([...modelUri]) ||
                    !!modelUri.length && History.exactMatch(History.viewPath, modelUri)) {
                    stackView.positionContentAtBeginning()
                    return
                }

                History.push(modelUri)
            }

            implicitWidth: Math.round(Helpers.clamp(MainCtx.navigationPanel.width, minimumWidth, maximumWidth))

            Navigation.parentItem: g_mainDisplay
            Navigation.upItem: localTopbar
            Navigation.downItem: loaderUpdatePane
            Navigation.rightItem: stackView.currentItem

            state: pannelVisiblity.showNavigation ? "expanded" : ""

            onVisibleChanged: {
                if (!visible) {
                    stackView.focus = true
                }
            }

            Component.onCompleted: {
                Qt.callLater(() => { sidebarTransition.enabled = true; })
            }

            states: State {
                name: "expanded"
                PropertyChanges {
                    target: sidebar
                    width: sidebar.implicitWidth
                }
            }

            transitions: Transition {
                id: sidebarTransition
                enabled: false

                from: ""; to: "expanded";
                reversible: true

                NumberAnimation {
                    property: "width"
                    duration: VLCStyle.duration_short
                    easing.type: Easing.InOutSine
                }
            }

            PaneResizeHandle {
                id: sidebarResizeHandle

                parent: sidebar
                target: sidebar

                panelObject: MainCtx.navigationPanel
                atRight: true

                resizeHandle.visible: (sidebar.state === "expanded") && !VLCStyle.isScreenSmall
                visualBorder.visible: !topLeftCornerBackground.visible

                minimumWidth: sidebar.minimumWidth
                maximumWidth: sidebar.maximumWidth

                anchors {
                    top: parent.top
                    bottom: parent.bottom
                    right: parent.right
                }

            }
        }

        Loader {
            id: playlistLoader

            z: 3
            anchors {
                top: parent.top
                right: parent.right
            }

            width: 0

            visible: false
            height: parent.height - g_mainDisplay.displayMargin

            active: MainCtx.playqueuePanel.docked

            state: ((status === Loader.Ready) && pannelVisiblity.showPlayqueue) ? "expanded" : ""

            readonly property bool shown: (status === Loader.Ready) && item.visible

            onVisibleChanged: {
                if (!visible) {
                    stackView.focus = true
                }
            }

            Component.onCompleted: {
                Qt.callLater(() => { playlistTransition.enabled = true; })
            }

            states: State {
                name: "expanded"
                PropertyChanges {
                    target: playlistLoader
                    width: playlistLoader.implicitWidth
                    visible: true
                }
            }

            transitions: Transition {
                id: playlistTransition
                enabled: false

                from: ""; to: "expanded";
                reversible: true

                SequentialAnimation {
                    PropertyAction { property: "visible" }

                    NumberAnimation {
                        property: "width"
                        duration: VLCStyle.duration_short
                        easing.type: Easing.InOutSine
                    }
                }
            }

            sourceComponent: PlaylistPane {
                id: playlist

                property int maximumWidth: Math.max(minimumWidth, (g_mainDisplay.width + playqueueResizeHandle.width)
                                                    / (pannelVisiblity.showNavigation ? 3 : 2))

                implicitWidth: Math.round(Helpers.clamp(MainCtx.playqueuePanel.width, minimumWidth, maximumWidth))

                focus: true

                leftPadding: playqueueResizeHandle.visualBorder.width
                rightPadding: VLCStyle.applicationHorizontalMargin
                bottomPadding: Math.max(VLCStyle.applicationVerticalMargin - g_mainDisplay.displayMargin, 0)
                topPadding: VLCStyle.layoutTitle_top_padding

                useAcrylic: !VLCStyle.isScreenSmall

                view.fadingEdge.pixelAlignedForDPR: !miniPlayer.visible

                Navigation.parentItem: g_mainDisplay
                Navigation.upItem: localTopbar
                Navigation.downItem: loaderUpdatePane
                Navigation.leftItem: stackView.currentItem

                Navigation.cancelAction: function() {
                    MainCtx.playqueuePanel.visible = false
                    stackView.forceActiveFocus()
                }

                PaneResizeHandle {
                    id: playqueueResizeHandle

                    parent: playlist
                    target: playlist

                    minimumWidth: playlist.minimumWidth
                    maximumWidth: playlist.maximumWidth

                    panelObject: MainCtx.playqueuePanel
                    atRight: false

                    anchors {
                        top: parent.top
                        bottom: parent.bottom
                        left: parent.left
                    }
                }
            }
        }
    }

    Loader {
        id: loaderUpdatePane

        z: 2

        // WARNING: Object name is used from C++ side to acknowledge the modern update pane is loaded,
        //          if C++ side does not get this acknowledgement, the old update dialog is shown as
        //          fallback.
        objectName: "updatePaneLoader"

        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: miniPlayer.top

        readonly property bool updateModelIsAvailable: (typeof UpdateModel !== 'undefined')

        active: updateModelIsAvailable && (shouldShow || height > 0.0)

        focus: !!item

        property int _updateStatusOnDismissal: -1

        readonly property bool shouldShow: {
            if (!updateModelIsAvailable)
                return false

            if (UpdateModel.explicitCheck) {
                return (UpdateModel.updateStatus !== UpdateModel.Unchecked)
            } else {
                if (UpdateModel.updateStatus === _updateStatusOnDismissal)
                    return false // already dismissed with the same status

                switch (UpdateModel.updateStatus) {
                    case UpdateModel.Unchecked:
                    case UpdateModel.Checking:
                    case UpdateModel.UpToDate:
                        return false
                    default:
                        return true
                }
            }
        }

        // This property can be used to enable/disable the animation:
        property alias toggleAnimation: heightBehavior.enabled

        function dismiss() {
            // This could be also be an assertion:
            if (updateModelIsAvailable)
                _updateStatusOnDismissal = UpdateModel.updateStatus

            height = 0.0
        }

        onShouldShowChanged: {
            if (shouldShow)
                height = Qt.binding(() => { return implicitHeight })
        }

        onActiveChanged: {
            if (updateModelIsAvailable && !active) {
                UpdateModel.explicitCheck = undefined // Resets the property
            }
        }

        clip: (height < implicitHeight)

        Behavior on height {
            id: heightBehavior

            NumberAnimation {
                easing.type: Easing.InOutSine
                duration: VLCStyle.duration_long
            }
        }

        source: "qrc:///qt/qml/VLC/MainInterface/UpdatePane.qml"

        Navigation.parentItem: g_mainDisplay
        Navigation.upItem: mainRow
        Navigation.downItem: miniPlayer
        Navigation.navigable: (active && height > 0.0)

        onLoaded: {
            item.background.visible = Qt.binding(function() { return !stackViewParent.layer.enabled })

            item.leftPadding = Qt.binding(function() { return VLCStyle.margin_large + VLCStyle.applicationHorizontalMargin })
            item.rightPadding = Qt.binding(function() { return VLCStyle.margin_large + VLCStyle.applicationHorizontalMargin })
            item.bottomPadding = Qt.binding(function() { return VLCStyle.margin_small + (miniPlayer.visible ? 0
                                                                                                            : VLCStyle.applicationVerticalMargin) })

            item.dismissRequested.connect(loaderUpdatePane, loaderUpdatePane.dismiss)

            // We have height animation here, we don't want animation inside the item in addition to that:
            item.animations = Qt.binding(function() { return !loaderUpdatePane.toggleAnimation })

            item.Navigation.parentItem = loaderUpdatePane

            item.focus = true // Loader itself is a focus scope, so we need this
        }
    }

    //track the visiblity state of the side panels
    //FIXME do we want proper state machine?
    Item {
        id: pannelVisiblity
        property bool showNavigation: false
        property bool showPlayqueue: false

        Component.onCompleted: {
            onIsScreenSmallChanged()
        }

        onShowNavigationChanged: {
            if (VLCStyle.isScreenSmall && pannelVisiblity.showPlayqueue && MainCtx.playqueuePanel.docked && pannelVisiblity.showNavigation) {
                pannelVisiblity.showPlayqueue = false
            }
        }

        onShowPlayqueueChanged: {
            if (VLCStyle.isScreenSmall && pannelVisiblity.showPlayqueue && MainCtx.playqueuePanel.docked && pannelVisiblity.showNavigation) {
                pannelVisiblity.showNavigation = false
            }
        }

        function hideVisiblePanels() {
            pannelVisiblity.showNavigation = false
            MainCtx.navigationPanel.visible = false
            if (MainCtx.playqueuePanel.docked) {
                pannelVisiblity.showPlayqueue = false
                MainCtx.playqueuePanel.visible = false
            }
        }

        function toggleNavigationVisibility() {
            showNavigation = !showNavigation
            MainCtx.navigationPanel.visible = showNavigation
        }

        function togglePlayqueueVisibility() {
            showPlayqueue = !showPlayqueue
            MainCtx.playqueuePanel.visible = showPlayqueue
        }

        function onIsScreenSmallChanged() {
            //when screen becomes small hide side panels
            if (VLCStyle.isScreenSmall) {
                pannelVisiblity.showNavigation = false
                pannelVisiblity.showPlayqueue = MainCtx.playqueuePanel.visible
            } else {
                //reshow the navigation panels to original state
                pannelVisiblity.showNavigation = MainCtx.navigationPanel.visible
                pannelVisiblity.showPlayqueue =  MainCtx.playqueuePanel.visible
            }
        }

        Connections {
            target: VLCStyle

            function onIsScreenSmallChanged() {
                pannelVisiblity.onIsScreenSmallChanged()
            }
        }

        Connections {
            target: MainCtx.playqueuePanel

            function onVisibleChanged() {
                pannelVisiblity.showPlayqueue = MainCtx.playqueuePanel.visible
            }
        }

        Connections {
            target: MainCtx.navigationPanel

            function onVisibleChanged() {
                pannelVisiblity.showNavigation = MainCtx.navigationPanel.visible
            }
        }
    }

    Component {
        id: pipPlayerComponent

        PIPPlayer {
            id: playerPip
            anchors {
                bottom: loaderUpdatePane.top
                left: parent.left
                bottomMargin: VLCStyle.margin_normal
                leftMargin: VLCStyle.margin_normal + VLCStyle.applicationHorizontalMargin
            }

            width: VLCStyle.dp(320, VLCStyle.scale)
            height: VLCStyle.dp(180, VLCStyle.scale)
            z: 4
            visible: g_mainDisplay._showMiniPlayer && MainCtx.hasEmbededVideo
            enabled: g_mainDisplay._showMiniPlayer && MainCtx.hasEmbededVideo

            Binding on visible {
                id: visibleBinding
                when: false
                delayed: true
                value: g_mainDisplay._showMiniPlayer && MainCtx.hasEmbededVideo
            }

            Component.onCompleted: {
                Qt.callLater(() => { visibleBinding.when = true })
            }

            dragXMin: VLCStyle.applicationHorizontalMargin
            dragXMax: g_mainDisplay.width - playerPip.width - VLCStyle.applicationHorizontalMargin
            dragYMin: localTopbar.y + localTopbar.height
            dragYMax: loaderUpdatePane.y - playerPip.height

            //keep the player visible on resize
            Connections {
                target: g_mainDisplay
                function onWidthChanged() {
                    if (playerPip.x > playerPip.dragXMax)
                        playerPip.x = playerPip.dragXMax
                }
                function onHeightChanged() {
                    if (playerPip.y > playerPip.dragYMax)
                        playerPip.y = playerPip.dragYMax
                }
            }
        }
    }

    Dialogs {
        z: 10
        bgContent: g_mainDisplay

        anchors {
            bottom: miniPlayer.visible ? miniPlayer.top : parent.bottom
            left: parent.left
            right: parent.right
        }
    }

    Widgets.FloatingNotification {
        id: notif
        z: 11

        anchors {
            bottom: miniPlayer.top
            left: parent.left
            right: parent.right
            margins: VLCStyle.margin_large
        }
    }

    MiniPlayer {
        id: miniPlayer

        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom

        z: 3

        horizontalPadding: VLCStyle.applicationHorizontalMargin
        bottomPadding: VLCStyle.applicationVerticalMargin + VLCStyle.margin_xsmall

        background.visible: !stackViewParent.layer.enabled

        Navigation.parentItem: g_mainDisplay
        Navigation.upItem: loaderUpdatePane

        onVisibleChanged: {
            if (!visible && miniPlayer.activeFocus)
                stackView.forceActiveFocus()
        }
    }

    Connections {
        target: Player
        function onHasVideoOutputChanged() {
            if (Player.hasVideoOutput && MainCtx.hasEmbededVideo) {
                MainCtx.playerView = true
            } else {
                _showMiniPlayer = false;
            }
        }
    }

    component PaneResizeHandle: Item {
        id: paneResizeHandle
        required property Item target
        required property QtObject panelObject
        property alias atRight: resizeHandle.atRight

        property alias visualBorder: visualBorder
        property alias resizeHandle: resizeHandle

        property alias minimumWidth: resizeHandle.minimumWidth
        property alias maximumWidth: resizeHandle.maximumWidth

        implicitWidth: resizeHandle.width

        Rectangle {
            id: visualBorder

            anchors {
                top: parent.top
                bottom: parent.bottom
                left: resizeHandle.atRight ? undefined : parent.left
                right: resizeHandle.atRight ? parent.right: undefined
            }

            width: VLCStyle.border
            color: theme.separator
        }

        Widgets.HorizontalResizeHandle {
            id: resizeHandle

            property bool _inhibitMainInterfaceUpdate: false

            anchors {
                top: parent.top
                bottom: parent.bottom
                left: resizeHandle.atRight ? undefined : parent.left
                right: resizeHandle.atRight ? parent.right: undefined
            }

            atRight: false
            currentWidth: target.width

            visible: !VLCStyle.isScreenSmall

            onRequestedWidthChanged: {
                if (!_inhibitMainInterfaceUpdate)
                    paneResizeHandle.panelObject.width = requestedWidth
            }

            Component.onCompleted:  _updateFromMainInterface()

            function _updateFromMainInterface() {
                if (requestedWidth === paneResizeHandle.panelObject.width)
                    return

                _inhibitMainInterfaceUpdate = true
                requestedWidth = paneResizeHandle.panelObject.width
                _inhibitMainInterfaceUpdate = false
            }

            Connections {
                target: paneResizeHandle.panelObject

                function onWidthChanged() {
                    resizeHandle._updateFromMainInterface()
                }
            }
        }
    }

    MouseArea {
        /// handles mouse navigation buttons
        z:9
        anchors.fill: parent
        acceptedButtons: Qt.BackButton
        cursorShape: undefined
        onClicked: History.previous()
    }
}

[Dauer der Verarbeitung: 0.12 Sekunden, vorverarbeitet 2026-10-24]