[vlc-commits] [Git][videolan/vlc][master] 3 commits: qml: fix double layering in `FadingEdge`
Felix Paul Kühne (@fkuehne)
gitlab at videolan.org
Sat May 30 16:10:19 UTC 2026
Felix Paul Kühne pushed to branch master at VideoLAN / VLC
Commits:
31795ba1 by Fatih Uzunoglu at 2026-05-30T17:19:57+02:00
qml: fix double layering in `FadingEdge`
Even though the `ShaderEffectSource` is a dynamic
texture provider and represents a layer itself,
using `layer.enabled` makes Qt create another
layer on top of that layer.
- - - - -
4f1ee897 by Fatih Uzunoglu at 2026-05-30T17:19:57+02:00
qml: release the layer when applicable in `FadingEdge`
It covers these cases:
- If `useLayer` is always on, neither the layer nor the
associated items (`ShaderEffect` and `ShaderEffectSource`)
are never created.
- If `useLayer` becomes off at a later time, the layer
texture is released but the associated items are not
destroyed.
Regarding the items (`ShaderEffectSource` and `ShaderEffect`),
once `useLayer` becomes `true`, they are created and are not
destroyed if `useLayer` becomes `false` later on. This is
because `useLayer` can change many times depending on the
background color (such as backdrop blur is no longer available
because the interface window lost focus).
- - - - -
663e5b9a by Fatih Uzunoglu at 2026-05-30T17:19:57+02:00
qml: add property `enforceClipping` in `FadingEdge`
- - - - -
1 changed file:
- modules/gui/qt/widgets/qml/FadingEdge.qml
Changes:
=====================================
modules/gui/qt/widgets/qml/FadingEdge.qml
=====================================
@@ -32,7 +32,7 @@ Item {
// which results lower memory consumption:
property alias backgroundColor: backgroundRect.color
- property alias sourceItem: shaderEffectSource.sourceItem
+ property Item sourceItem
property real beginningMargin
property real endMargin
@@ -60,7 +60,40 @@ Item {
readonly property bool effectCompatible: (GraphicsInfo.shaderType === GraphicsInfo.RhiShader)
- readonly property bool implicitClipping: shaderEffectSource.visible
+ readonly property bool implicitClipping: !!_shaderEffect?.visible
+
+ property bool useLayering: effectCompatible && (enforceClipping || backgroundColor.a < 1.0)
+
+ // Sometimes `Item::clip` may not be suitable to use with item views,
+ // this includes, but not limited to, when display margins are set.
+ // In this case, `enforceClipping` may be set. Note that setting this
+ // property does not guarantee that clipping is done, it is advised
+ // to check `implicitClipping` to know if clipping is actually done.
+ // NOTE: `useLayering` must be set for this to be respected, by
+ // default it is set when `enforceClipping` is set.
+ // TODO: Get rid of this once we can adjust the clip rect directly
+ // in QML (currently requires overriding `QQuickItem::clipRect()`).
+ property bool enforceClipping: false
+
+ property Item _shaderEffectSource
+ property Item _shaderEffect
+
+ onUseLayeringChanged: {
+ if (useLayering) {
+ // We create the items needed for layer once it is needed, and keep it.
+ // Note that, while we keep the items, the layer texture should still
+ // be released if `useLayer` becomes `false` after that (since we set
+ // `sourceItem` to `null` while `live` is set).
+
+ if (!_shaderEffectSource) {
+ _shaderEffectSource = shaderEffectSourceComponent.createObject(positionerParent)
+ }
+
+ if (!_shaderEffect) {
+ _shaderEffect = shaderEffectComponent.createObject(positionerParent)
+ }
+ }
+ }
Rectangle {
id: backgroundRect
@@ -73,7 +106,7 @@ Item {
z: -100
- visible: shaderEffectSource.visible && (color.a > 0.0)
+ visible: !!root._shaderEffectSource?.visible && (color.a > 0.0)
}
// Fading edge effect provider when background is opaque:
@@ -85,7 +118,7 @@ Item {
implicitWidth: root.fadeSize
// NOTE: `OpacityAnimator` updates the opacity once the animation finishes.
- visible: (opacity > 0.0 || enabled) && (root.backgroundColor.a > (1.0 - Number.EPSILON))
+ visible: (opacity > 0.0 || enabled) && !root.useLayering
opacity: enabled ? 1.0 : 0.0
@@ -134,78 +167,94 @@ Item {
}
}
- FadingEdgeRectangleForOpaqueBackground {
- anchors {
- top: shaderEffectSource.top
- left: shaderEffectSource.left
+ Item {
+ id: positionerParent
- bottom: (root.orientation === Qt.Horizontal) ? shaderEffectSource.bottom : undefined
- right: (root.orientation === Qt.Vertical) ? shaderEffectSource.right : undefined
+ anchors {
+ fill: parent
+ leftMargin: (root.orientation === Qt.Horizontal ? -root.beginningMargin : 0)
+ rightMargin: (root.orientation === Qt.Horizontal ? -root.endMargin : 0)
+ topMargin: (root.orientation === Qt.Vertical ? -root.beginningMargin : 0)
+ bottomMargin: (root.orientation === Qt.Vertical ? -root.endMargin : 0)
}
- enabled: root.enableBeginningFade
- colorStop0: root.backgroundColor
- colorStop1: "transparent"
- }
+ FadingEdgeRectangleForOpaqueBackground {
+ anchors {
+ top: parent.top
+ left: parent.left
- FadingEdgeRectangleForOpaqueBackground {
- anchors {
- bottom: shaderEffectSource.bottom
- right: shaderEffectSource.right
+ bottom: (root.orientation === Qt.Horizontal) ? parent.bottom : undefined
+ right: (root.orientation === Qt.Vertical) ? parent.right : undefined
+ }
- top: (root.orientation === Qt.Horizontal) ? shaderEffectSource.top : undefined
- left: (root.orientation === Qt.Vertical) ? shaderEffectSource.left : undefined
+ enabled: root.enableBeginningFade
+ colorStop0: root.backgroundColor
+ colorStop1: "transparent"
}
- enabled: root.enableEndFade
- colorStop0: "transparent"
- colorStop1: root.backgroundColor
- }
+ FadingEdgeRectangleForOpaqueBackground {
+ anchors {
+ bottom: parent.bottom
+ right: parent.right
- // Fading edge effect provider when background is not opaque:
- ShaderEffectSource {
- id: shaderEffectSource
+ top: (root.orientation === Qt.Horizontal) ? parent.top : undefined
+ left: (root.orientation === Qt.Vertical) ? parent.left : undefined
+ }
- anchors {
- top: parent.top
- left: parent.left
- leftMargin: (root.orientation === Qt.Horizontal ? -root.beginningMargin : 0)
- topMargin: (root.orientation === Qt.Vertical ? -root.beginningMargin : 0)
+ enabled: root.enableEndFade
+ colorStop0: "transparent"
+ colorStop1: root.backgroundColor
}
+ }
+
+ Component {
+ id: shaderEffectSourceComponent
+
+ // Fading edge effect provider when background is not opaque:
+ ShaderEffectSource {
+ id: shaderEffectSource
+
+ anchors.top: parent.top
+ anchors.left: parent.left
- property real eDPR: MainCtx.effectiveDevicePixelRatio(Window.window) || 1.0
- readonly property int alignNumber: pixelAlignedForDPR ? Helpers.denominatorForFloat(eDPR) : 1
+ property real eDPR: MainCtx.effectiveDevicePixelRatio(Window.window) || 1.0
+ readonly property int alignNumber: pixelAlignedForDPR ? Helpers.denominatorForFloat(eDPR) : 1
- Connections {
- target: MainCtx
+ Connections {
+ target: MainCtx
- function onIntfDevicePixelRatioChanged() {
- shaderEffectSource.eDPR = MainCtx.effectiveDevicePixelRatio(shaderEffectSource.Window.window) || 1.0
+ function onIntfDevicePixelRatioChanged() {
+ shaderEffectSource.eDPR = MainCtx.effectiveDevicePixelRatio(shaderEffectSource.Window.window) || 1.0
+ }
}
- }
- implicitWidth: Helpers.alignUp((parent.width + (root.orientation === Qt.Horizontal ? (root.beginningMargin + root.endMargin) : 0)), alignNumber)
- implicitHeight: Helpers.alignUp((parent.height + (root.orientation === Qt.Vertical ? (root.beginningMargin + root.endMargin) : 0)), alignNumber)
+ implicitWidth: Helpers.alignUp((parent.width + (root.orientation === Qt.Horizontal ? (root.beginningMargin + root.endMargin) : 0)), alignNumber)
+ implicitHeight: Helpers.alignUp((parent.height + (root.orientation === Qt.Vertical ? (root.beginningMargin + root.endMargin) : 0)), alignNumber)
- sourceRect: Qt.rect(root.sourceX - (root.orientation === Qt.Horizontal ? root.beginningMargin : 0),
- root.sourceY - (root.orientation === Qt.Vertical ? root.beginningMargin : 0),
- width, // Texture width is (width * dpr)
- height) // Texture height is (height * dpr)
+ // When `live` is set, Qt releases the layer when `sourceItem` becomes `null`. For that reason
+ // we do not need to set `parent` to null. The important thing is to get rid of the layer
+ // texture, we don't need to care about getting rid of the texture provider. See
+ // `QQuickShaderEffectSource::updatePaintNode()` and `QSGRhiLayer::setItem()`.
+ sourceItem: root.useLayering ? root.sourceItem : null
- // Make sure sourceItem is not rendered twice:
- hideSource: visible
+ sourceRect: Qt.rect(root.sourceX - (root.orientation === Qt.Horizontal ? root.beginningMargin : 0),
+ root.sourceY - (root.orientation === Qt.Vertical ? root.beginningMargin : 0),
+ width, // Texture width is (width * dpr)
+ height) // Texture height is (height * dpr)
- smooth: false
+ // Make sure sourceItem is not rendered twice:
+ hideSource: root._shaderEffect && (root._shaderEffect.visible && root._shaderEffect.source === this)
- visible: effectCompatible &&
- (root.backgroundColor.a < 1.0) &&
- ((root.enableBeginningFade || root.enableEndFade) ||
- ((shaderEffect) && (shaderEffect.beginningFadeSize > 0 || shaderEffect.endFadeSize > 0)))
+ visible: false
+
+ smooth: false
+ }
+ }
- property ShaderEffect shaderEffect
+ Component {
+ id: shaderEffectComponent
- layer.enabled: true
- layer.effect: ShaderEffect {
+ ShaderEffect {
// It makes sense to use the effect for only in the fading part.
// However, it would complicate things in the QML side. As it
// would require two additional items, as well as two more texture
@@ -215,6 +264,10 @@ Item {
id: effect
+ anchors.fill: root._shaderEffectSource
+
+ readonly property Item source: root._shaderEffectSource
+
readonly property bool vertical: (root.orientation === Qt.Vertical)
readonly property real normalFadeSize: root.fadeSize / (vertical ? height : width)
@@ -222,6 +275,11 @@ Item {
property real endFadeSize: root.enableEndFade ? normalFadeSize : 0
readonly property real endFadePos: 1.0 - endFadeSize
+ visible: root.useLayering &&
+ (root.enforceClipping ||
+ ((root.enableBeginningFade || root.enableEndFade) ||
+ (beginningFadeSize > 0 || endFadeSize > 0)))
+
onBeginningFadeSizeChanged: {
if (!beginningFadeBehavior.enabled) {
Qt.callLater(effect._enableBeginningFadeBehavior)
@@ -242,16 +300,6 @@ Item {
endFadeBehavior.enabled = true
}
- Component.onCompleted: {
- console.assert(shaderEffectSource.shaderEffect === null)
- shaderEffectSource.shaderEffect = this
- }
-
- Component.onDestruction: {
- console.assert(shaderEffectSource.shaderEffect === this)
- shaderEffectSource.shaderEffect = null
- }
-
component FadeBehavior : Behavior {
enabled: false
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1ee9c4139eacf5ac562795f089ffc5aec1c500ad...663e5b9a0e0520323cedd82499b802a8e2645fce
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1ee9c4139eacf5ac562795f089ffc5aec1c500ad...663e5b9a0e0520323cedd82499b802a8e2645fce
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help
More information about the vlc-commits
mailing list