diff --git a/modules/gui/qt/maininterface/qml/MainDisplay.qml b/modules/gui/qt/maininterface/qml/MainDisplay.qml index 91eae3d689..c34bcdef1a 100644 --- a/modules/gui/qt/maininterface/qml/MainDisplay.qml +++ b/modules/gui/qt/maininterface/qml/MainDisplay.qml @@ -213,16 +213,36 @@ FocusScope { onContentModelChanged: modelSortSettingHandler.set(sourcesBanner.contentModel, History.viewPath) } - Rectangle { - color: VLCStyle.colors.bg + FocusScope { + focus: true + id: medialibId anchors.fill: parent - FocusScope { - focus: true - id: medialibId + Navigation.parentItem: root + + Rectangle { + id: parentRectangle anchors.fill: parent - Navigation.parentItem: root + color: VLCStyle.colors.bg + + layer.enabled: (((GraphicsInfo.shaderType === GraphicsInfo.GLSL)) && + ((GraphicsInfo.shaderSourceType & GraphicsInfo.ShaderSourceString))) && + (miniPlayer.visible || (loaderProgress.active && loaderProgress.item.visible)) + + layer.effect: Widgets.FrostedGlassEffect { + tint: VLCStyle.colors.lowerBanner + + effectRect: { + var _height = 0 + if (loaderProgress.active && loaderProgress.item.visible) + _height += loaderProgress.item.height + if (miniPlayer.visible) + _height += miniPlayer.height + + return Qt.rect(0, height - _height, width, _height) + } + } ColumnLayout { id: mainColumn @@ -281,27 +301,6 @@ FocusScope { : VLCStyle.applicationHorizontalMargin leftMargin: VLCStyle.applicationHorizontalMargin } - - // This item is the root of a large hierarchy - // which requires many batches to be rendered. - // When the miniPlayer effect is active, this - // item (source item) gets rendered in an offscreen - // surface. If we don't enable layer here, - // it (along with children) gets rendered again - // in the assigned window. - // If layer is enabled, instead of rendering one - // more time with many batches, a dynamic texture - // from the offscreen surface is used. This behavior - // reduces the amount of batches from 2x to x+1. - // A side effect is having to draw a large texture - // with blending on, but this must be cheaper redrawing - // all the batches. - // TODO: Reconsider this behavior when batching is optimized. - layer.enabled: miniPlayer.visible && miniPlayer.effectAvailable - - // Enable clipping so that the effect does not sit - // on top of the source. - clip: miniPlayer.visible && miniPlayer.effectAvailable } FocusScope { @@ -430,114 +429,110 @@ FocusScope { } } } + } - Loader { - id: loaderProgress + Loader { + id: loaderProgress - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: miniPlayer.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: miniPlayer.top - active: (MainCtx.mediaLibraryAvailable && MainCtx.mediaLibrary.idle === false) + active: (MainCtx.mediaLibraryAvailable && MainCtx.mediaLibrary.idle === false) - source: "qrc:///widgets/ScanProgressBar.qml" + source: "qrc:///widgets/ScanProgressBar.qml" - onItemChanged: { - if (item === null) return + onLoaded: { + item.background.visible = Qt.binding(function() { return !parentRectangle.layer.enabled }) - // NOTE: These are required for the FrostedGlassEffect. + 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.source = Qt.binding(function() { return stackView }) - - item.sourceRect = Qt.binding(function() { - return stackView.mapFromItem(parent, x, y, width, height) - }) - } + P.PIPPlayer { + id: playerPip + anchors { + bottom: miniPlayer.top + left: parent.left + bottomMargin: VLCStyle.margin_normal + leftMargin: VLCStyle.margin_normal + VLCStyle.applicationHorizontalMargin } - P.PIPPlayer { - id: playerPip - anchors { - bottom: miniPlayer.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: 2 + visible: !root._inhibitMiniPlayer && root._showMiniPlayer && MainCtx.hasEmbededVideo + enabled: !root._inhibitMiniPlayer && root._showMiniPlayer && MainCtx.hasEmbededVideo - width: VLCStyle.dp(320, VLCStyle.scale) - height: VLCStyle.dp(180, VLCStyle.scale) - z: 2 - visible: !root._inhibitMiniPlayer && root._showMiniPlayer && MainCtx.hasEmbededVideo - enabled: !root._inhibitMiniPlayer && root._showMiniPlayer && MainCtx.hasEmbededVideo - - dragXMin: 0 - dragXMax: root.width - playerPip.width - dragYMin: sourcesBanner.y + sourcesBanner.height - dragYMax: miniPlayer.y - playerPip.height - - //keep the player visible on resize - Connections { - target: root - onWidthChanged: { - if (playerPip.x > playerPip.dragXMax) - playerPip.x = playerPip.dragXMax - } - onHeightChanged: { - if (playerPip.y > playerPip.dragYMax) - playerPip.y = playerPip.dragYMax - } - } - } - - DG.Dialogs { - z: 10 - bgContent: root - - anchors { - bottom: miniPlayer.visible ? miniPlayer.top : parent.bottom - left: parent.left - right: parent.right - } - } - - P.MiniPlayer { - id: miniPlayer - - BindingCompat on state { - when: root._inhibitMiniPlayer && !miniPlayer.visible - value: "" - } - - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - - z: 3 - Navigation.parentItem: medialibId - Navigation.upItem: stackView - Navigation.cancelItem:sourcesBanner - onVisibleChanged: { - if (!visible && miniPlayer.activeFocus) - stackView.forceActiveFocus() - } - - effectSource: stackView - effectSourceRect: effectSource.mapFromItem(parent, - x, - y, - width, - height) - } + dragXMin: 0 + dragXMax: root.width - playerPip.width + dragYMin: sourcesBanner.y + sourcesBanner.height + dragYMax: miniPlayer.y - playerPip.height + //keep the player visible on resize Connections { - target: Player - onHasVideoOutputChanged: { - if (Player.hasVideoOutput && MainCtx.hasEmbededVideo) { - if (History.current.view !== "player") - g_mainDisplay.showPlayer() - } else { - _showMiniPlayer = false; - } + target: root + onWidthChanged: { + if (playerPip.x > playerPip.dragXMax) + playerPip.x = playerPip.dragXMax + } + onHeightChanged: { + if (playerPip.y > playerPip.dragYMax) + playerPip.y = playerPip.dragYMax + } + } + } + + DG.Dialogs { + z: 10 + bgContent: root + + anchors { + bottom: miniPlayer.visible ? miniPlayer.top : parent.bottom + left: parent.left + right: parent.right + } + } + + P.MiniPlayer { + id: miniPlayer + + BindingCompat on state { + when: root._inhibitMiniPlayer && !miniPlayer.visible + value: "" + } + + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + z: 3 + + rightPadding: VLCStyle.applicationHorizontalMargin + leftPadding: VLCStyle.applicationHorizontalMargin + bottomPadding: VLCStyle.applicationVerticalMargin + + background.visible: !parentRectangle.layer.enabled + + Navigation.parentItem: medialibId + Navigation.upItem: stackView + Navigation.cancelItem:sourcesBanner + onVisibleChanged: { + if (!visible && miniPlayer.activeFocus) + stackView.forceActiveFocus() + } + } + + Connections { + target: Player + onHasVideoOutputChanged: { + if (Player.hasVideoOutput && MainCtx.hasEmbededVideo) { + if (History.current.view !== "player") + g_mainDisplay.showPlayer() + } else { + _showMiniPlayer = false; } } } diff --git a/modules/gui/qt/player/qml/MiniPlayer.qml b/modules/gui/qt/player/qml/MiniPlayer.qml index 448294ebec..20a06416c5 100644 --- a/modules/gui/qt/player/qml/MiniPlayer.qml +++ b/modules/gui/qt/player/qml/MiniPlayer.qml @@ -17,6 +17,7 @@ *****************************************************************************/ import QtQuick 2.11 import QtQuick.Controls 2.4 +import QtQuick.Templates 2.4 as T import QtQuick.Layouts 1.11 import QtGraphicalEffects 1.0 @@ -25,17 +26,15 @@ import org.videolan.vlc 0.1 import "qrc:///widgets/" as Widgets import "qrc:///style/" -FocusScope { +T.Pane { id: root - implicitHeight: controlBar.implicitHeight height: 0 - visible: false + implicitWidth: Math.max(background ? background.implicitWidth : 0, contentItem.implicitWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, contentItem.implicitHeight + topPadding + bottomPadding) - property alias effectSource: effect.source - property alias effectSourceRect: effect.sourceRect - property alias effectAvailable: effect.effectAvailable + visible: false state: (Player.playingState === Player.PLAYING_STATE_STOPPED) ? "" : "expanded" @@ -68,22 +67,11 @@ FocusScope { acceptedButtons: Qt.AllButtons } - Widgets.FrostedGlassEffect { - id: effect - anchors.fill: parent - - tint: VLCStyle.colors.lowerBanner + background: Rectangle { + color: VLCStyle.colors.bg } - ControlBar { - id: controlBar - - anchors.fill: parent - - rightPadding: VLCStyle.applicationHorizontalMargin - leftPadding: rightPadding - bottomPadding: VLCStyle.applicationVerticalMargin - + contentItem: ControlBar { focus: true colors: VLCStyle.colors textPosition: ControlBar.TimeTextPosition.Hide @@ -94,7 +82,7 @@ FocusScope { Navigation.parentItem: root Keys.onPressed: { - controlBar.Navigation.defaultKeyAction(event) + Navigation.defaultKeyAction(event) if (!event.accepted) { MainCtx.sendHotkey(event.key, event.modifiers) diff --git a/modules/gui/qt/widgets/qml/FrostedGlassEffect.qml b/modules/gui/qt/widgets/qml/FrostedGlassEffect.qml index 1ecab3cf3d..dbb94eafc4 100644 --- a/modules/gui/qt/widgets/qml/FrostedGlassEffect.qml +++ b/modules/gui/qt/widgets/qml/FrostedGlassEffect.qml @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (C) 2020 VLC authors and VideoLAN + * Copyright (C) 2022 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 @@ -21,101 +21,114 @@ import QtGraphicalEffects 1.0 import "qrc:///style/" +// This item can be used as a layer effect. +// Right now, it has the following limitations: +// * The blur effect is processed for the whole source, +// even though it is only shown for the area denoted +// by effectRect. This is caused by FastBlur not +// accepting a source rectangle. +// * The source is sampled and displayed as a whole, +// however it is stacked below of the blur effect +// so it is only partly seen as intended. +// * As a corollary of the previous limitation, you should +// always have a solid background for the source item. +// otherwise, the effect can not work properly. Item { id: effect - property Item source - property rect sourceRect: mapToItem(source, x, y, width, height) + property var source - property bool recursive: false - property real blurRadius: 64 - property color tint + // Rectangular area where the effect should be applied: + property alias effectRect: blurProxy.sourceRect - property real tintStrength: 0.7 + property alias blurRadius: blurEffect.radius + + property color tint: "transparent" + property real tintStrength: Qt.colorEqual(tint, "transparent") ? 0.0 : 0.7 property real noiseStrength: 0.02 property real exclusionStrength: 0.09 - readonly property bool effectAvailable: (GraphicsInfo.shaderType === GraphicsInfo.GLSL) && - (GraphicsInfo.shaderSourceType & GraphicsInfo.ShaderSourceString) + ShaderEffect { + anchors.fill: parent - opacity: source.opacity + property alias source: effect.source - Loader { - id: loader + cullMode: ShaderEffect.BackFaceCulling + } + + FastBlur { + id: blurEffect anchors.fill: parent - sourceComponent: effect.effectAvailable ? effectComponent : rectComponent + source: effect.source - Component { - id: rectComponent + radius: 64 - Rectangle { - color: effect.tint - } - } + visible: false + } - Component { - id: effectComponent + ShaderEffectSource { + id: blurProxy - FastBlur { - id: blurEffect + x: Math.floor(sourceRect.x) + y: Math.floor(sourceRect.y) + width: sourceRect.width > 0 ? Math.ceil(sourceRect.width) + : implicitWidth + height: sourceRect.height > 0 ? Math.ceil(sourceRect.height) + : implicitHeight - source: ShaderEffectSource { - id: effectSource - smooth: false - sourceItem: effect.source - sourceRect: effect.sourceRect - visible: false - samples: 0 + implicitWidth: Math.ceil(parent.width) + implicitHeight: Math.ceil(parent.height) + + sourceItem: blurEffect + recursive: false + samples: 0 + smooth: false + + mipmap: false + + layer.enabled: true + layer.effect: ShaderEffect { + readonly property color tint: effect.tint + readonly property real tintStrength: effect.tintStrength + readonly property real noiseStrength: effect.noiseStrength + readonly property real exclusionStrength: effect.exclusionStrength + + cullMode: ShaderEffect.BackFaceCulling + + fragmentShader: " + uniform lowp sampler2D source; // this item + varying highp vec2 qt_TexCoord0; + + uniform lowp float qt_Opacity; + + uniform lowp vec4 tint; + + uniform lowp float exclusionStrength; + uniform lowp float noiseStrength; + uniform lowp float tintStrength; + + mediump float rand(highp vec2 co){ + return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); } - radius: effect.blurRadius - - layer.enabled: true - layer.effect: ShaderEffect { - readonly property color tint: effect.tint - readonly property real tintStrength: effect.tintStrength - readonly property real noiseStrength: effect.noiseStrength - readonly property real exclusionStrength: effect.exclusionStrength - - cullMode: ShaderEffect.BackFaceCulling - - fragmentShader: " - uniform lowp sampler2D source; // this item - varying highp vec2 qt_TexCoord0; - - uniform lowp float qt_Opacity; - - uniform lowp vec4 tint; - - uniform lowp float exclusionStrength; - uniform lowp float noiseStrength; - uniform lowp float tintStrength; - - mediump float rand(highp vec2 co){ - return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); - } - - mediump vec4 exclude(mediump vec4 src, mediump vec4 dst) - { - return src + dst - 2.0 * src * dst; - } - - void main() { - mediump float r = rand(qt_TexCoord0) - 0.5; - mediump vec4 noise = vec4(r,r,r,1.0) * noiseStrength; - mediump vec4 blurred = texture2D(source, qt_TexCoord0); - - mediump vec4 exclColor = vec4(exclusionStrength, exclusionStrength, exclusionStrength, 0.0); - - blurred = exclude(blurred, exclColor); - - gl_FragColor = (mix(blurred, tint, tintStrength) + noise) * qt_Opacity; - }" + mediump vec4 exclude(mediump vec4 src, mediump vec4 dst) + { + return src + dst - 2.0 * src * dst; } - } - } + void main() { + mediump float r = rand(qt_TexCoord0) - 0.5; + mediump vec4 noise = vec4(r,r,r,1.0) * noiseStrength; + mediump vec4 blurred = texture2D(source, qt_TexCoord0); + + mediump vec4 exclColor = vec4(exclusionStrength, exclusionStrength, exclusionStrength, 0.0); + + blurred = exclude(blurred, exclColor); + + gl_FragColor = (mix(blurred, tint, tintStrength) + noise) * qt_Opacity; + }" + } } } diff --git a/modules/gui/qt/widgets/qml/ScanProgressBar.qml b/modules/gui/qt/widgets/qml/ScanProgressBar.qml index 709acd7840..f4690601ed 100644 --- a/modules/gui/qt/widgets/qml/ScanProgressBar.qml +++ b/modules/gui/qt/widgets/qml/ScanProgressBar.qml @@ -19,138 +19,115 @@ import QtQuick 2.11 import QtQuick.Controls 2.4 import QtQuick.Templates 2.4 as T -import QtQuick.Layouts 1.11 import org.videolan.vlc 0.1 import org.videolan.medialib 0.1 import "qrc:///style/" -FrostedGlassEffect { - // Settings +T.ProgressBar { + id: control - height: _getHeight() + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentItem.implicitWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + contentItem.implicitHeight + topPadding + bottomPadding) - tint: VLCStyle.colors.lowerBanner + rightPadding: VLCStyle.margin_large + leftPadding: VLCStyle.margin_large + bottomPadding: VLCStyle.margin_small + topPadding: VLCStyle.margin_small - // Functions + from: 0 + to: 100 - // Private + value: MediaLib.parsingProgress - function _getHeight() { - var height = column.implicitHeight + VLCStyle.margin_small * 2 + indeterminate: MediaLib.discoveryPending - // NOTE: We don't need to take the vertical safe area into consideration when the - // miniPlayer is visible. - if (g_mainDisplay.hasMiniPlayer) - return height - else - return height + VLCStyle.applicationVerticalMargin + background: Rectangle { + color: VLCStyle.colors.bg } - // Children - - ColumnLayout { - id: column - - anchors.fill: parent - - anchors.leftMargin: VLCStyle.margin_large + VLCStyle.applicationHorizontalMargin - anchors.rightMargin: anchors.leftMargin - - anchors.topMargin: VLCStyle.margin_small - - anchors.bottomMargin: (g_mainDisplay.hasMiniPlayer) ? VLCStyle.margin_small - : VLCStyle.margin_small - + VLCStyle.applicationVerticalMargin - + contentItem: Column { spacing: VLCStyle.margin_small - T.ProgressBar { - id: control + Item { + anchors.left: parent.left + anchors.right: parent.right - Layout.fillWidth: true + implicitHeight: VLCStyle.heightBar_xxsmall + implicitWidth: 200 - height: VLCStyle.heightBar_xxsmall + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter - from: 0 - to: 100 + implicitHeight: VLCStyle.heightBar_xxxsmall - value: MediaLib.parsingProgress + color: VLCStyle.colors.sliderBarMiniplayerBgColor + } - indeterminate: MediaLib.discoveryPending + Rectangle { + anchors.verticalCenter: parent.verticalCenter - contentItem: Item { - Rectangle { - anchors.left: parent.left - anchors.right: parent.right + implicitWidth: parent.width * control.visualPosition + implicitHeight: VLCStyle.heightBar_xxsmall - anchors.verticalCenter: parent.verticalCenter + // NOTE: We want round corners. + radius: height - height: VLCStyle.heightBar_xxxsmall + visible: !control.indeterminate - color: VLCStyle.colors.sliderBarMiniplayerBgColor - } + color: VLCStyle.colors.accent + } - Rectangle { - anchors.verticalCenter: parent.verticalCenter + Rectangle { + property real position: 0 - width: parent.width * control.visualPosition - height: VLCStyle.heightBar_xxsmall + anchors.verticalCenter: parent.verticalCenter - // NOTE: We want round corners. - radius: height + // NOTE: Why 0.24 though ? + implicitWidth: parent.width * 0.24 + implicitHeight: VLCStyle.heightBar_xxsmall - visible: (control.indeterminate === false) + x: Math.round((parent.width - width) * position) - color: VLCStyle.colors.accent - } + // NOTE: We want round corners. + radius: height - Rectangle { - property real position: 0 + visible: control.indeterminate - anchors.verticalCenter: parent.verticalCenter + color: VLCStyle.colors.accent - // NOTE: Why 0.24 though ? - width: parent.width * 0.24 - height: VLCStyle.heightBar_xxsmall + SequentialAnimation on position { + loops: Animation.Infinite - x: Math.round((parent.width - width) * position) + running: visible - // NOTE: We want round corners. - radius: height + NumberAnimation { + from: 0 + to: 1.0 - visible: control.indeterminate + duration: VLCStyle.durationSliderBouncing + easing.type: Easing.OutBounce + } - color: VLCStyle.colors.accent + NumberAnimation { + from: 1.0 + to: 0 - SequentialAnimation on position { - loops: Animation.Infinite - - running: visible - - NumberAnimation { - from: 0 - to: 1.0 - - duration: VLCStyle.durationSliderBouncing - easing.type: Easing.OutBounce - } - - NumberAnimation { - from: 1.0 - to: 0 - - duration: VLCStyle.durationSliderBouncing - easing.type: Easing.OutBounce - } + duration: VLCStyle.durationSliderBouncing + easing.type: Easing.OutBounce } } } } SubtitleLabel { - Layout.fillWidth: true + anchors.left: parent.left + anchors.right: parent.right text: (MediaLib.discoveryPending) ? I18n.qtr("Scanning %1") .arg(MediaLib.discoveryEntryPoint)