qml: make FrostedGlassEffect a layer effect

... so that we can set background on it.

Also,
- Make MiniPlayer a control.
- Refactor ScanProgressBar and make it a control.
This commit is contained in:
Fatih Uzunoglu 2022-12-21 21:19:52 +03:00 committed by Jean-Baptiste Kempf
parent d28f235b93
commit e3b28612d3
4 changed files with 280 additions and 307 deletions

View File

@ -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;
}
}
}

View File

@ -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)

View File

@ -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;
}"
}
}
}

View File

@ -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)