QML layer element "reveals" element above it when they overlap - qt

Is it possible in QML, maybe using shader effects with layers, to create an item that makes another item (with a higher z index) visible only when the two layers overlap? I've been messing aroud with OpacityMask and ThresholdMask but have been unable to figure it out. The effect I'm looking for in the context of the example below would be if the the black circle was only visible when the two red squares are under it:
current:
desired:
Some key points are that the bottom layer (red squares) must be moveable (OpacityMask doesn't seem to let you drag the maskSource) and the bottom layer needs to also be able to contain other elements within it that the black circle responds to. Any guidance towards the right things to learn in order to achieve this would be appreciated. Here is the QML for the red squares and black circle thing. The red squares are draggable as one element:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
Window {
id: main_window
visible: true
width: 1500
height: 1000
title: qsTr("Hello World")
Item {
width: main_window.width
height: main_window.height
LinearGradient {
anchors.fill: parent
start: Qt.point(0, 0)
end: Qt.point(main_window.width, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "#003cff" }
GradientStop { position: 1.0; color: "#9afff9" }
}
}
}
Rectangle {
id: sfg
width: 175
height: 75
color: 'transparent'
RowLayout {
width: parent.width
height: parent.height
spacing: 25
Rectangle {
Layout.preferredWidth: 75
Layout.fillWidth: false
Layout.fillHeight: true
color: 'red'
}
Rectangle {
Layout.preferredWidth: 75
Layout.fillWidth: false
Layout.fillHeight: true
color: 'red'
}
}
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
drag {
target: sfg
}
}
}
Rectangle {
id: mask
color: 'black'
x: 400
y: 200
width: 100
height: 100
visible: true
opacity: 1
radius: 50
}
}

Like this?
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
Window {
id: main_window
visible: true
width: 1500
height: 1000
title: qsTr("Hello World")
Item {
width: main_window.width
height: main_window.height
LinearGradient {
anchors.fill: parent
start: Qt.point(0, 0)
end: Qt.point(main_window.width, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "#003cff" }
GradientStop { position: 1.0; color: "#9afff9" }
}
}
}
Rectangle {
id: sgfBox
anchors.fill: parent
color: "transparent"
Rectangle {
id: sfg
width: 175
height: 75
color: 'transparent'
RowLayout {
width: parent.width
height: parent.height
spacing: 25
Rectangle {
Layout.preferredWidth: 75
Layout.fillWidth: false
Layout.fillHeight: true
color: 'red'
}
Rectangle {
Layout.preferredWidth: 75
Layout.fillWidth: false
Layout.fillHeight: true
color: 'red'
}
}
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
drag {
target: sfg
}
}
}
}
Rectangle {
id: mask
anchors.fill: parent
color: "transparent"
Rectangle {
color: 'black'
x: 400
y: 200
width: 100
height: 100
opacity: 1
radius: 50
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: sgfBox
}
}
}

Related

LinearGradient inside Rectangle is displaying blurred white borders

I am using LinearGradient as background for rectangle, but the left and right borders of the rectangle are a little bit white and blurred. How can I avoid this situation?
I have tried to set below properties on the Rectangle but it didn't work.
clip: true
smooth: true
antialiasing: true
Here is my code:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtGraphicalEffects 1.15
Window {
width: 640
height: 480
visible: true
Rectangle {
anchors.fill: parent
color: "#4f4444"
}
Rectangle {
id: root
anchors.centerIn: parent
width: 355
height: 90
radius: 50
LinearGradient {
anchors.fill: parent
source: ShaderEffectSource {
sourceItem: root
recursive: true
}
start: Qt.point(0, 0)
end: Qt.point(parent.width, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "#2a3254" }
GradientStop { position: 1.0; color: "#0e1c57" }
}
}
Text {
anchors.centerIn: parent
text: "Click me!"
color: "white"
}
}
}
The problem is that it is smoothing the edges of the shape to blend with the Rectangle that contains the gradient (root). If you change that Rectangle's color to match what is drawn behind it, you won't see those edges anymore.
Rectangle {
id: bground
anchors.fill: parent
color: "#4f4444"
}
Rectangle {
id: root
color: bground.color // Match the background's color
LinearGradient { ... }
}

QML Scroll Bar doesn't update when it is at the bottom and window height is resized

I have a custom scrollbar QML type that I am working on. The problem I'm having is that if the scroll bar is all the way at the bottom of the page and the height of the main application window is increased, the translated contents stay in place and the size of the scroll bar is not updated. After this window resize occurs, clicking on the scroll bar causes the content to snap to its proper place and the scroll bar to snap to its proper size. What changes might could be made to the code below so the position of the contents (red blocks) and scroll bar size update while the window height is changing? Not afterwards when the scroll bar has been clicked again. To see the issue just open the code below, scroll the blue scroll bar all the way to the bottom, increase the height of the main window (observing the scroll bar size and the content position), and then click on the scroll bar after the resize. Here is my code:
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12
import QtQuick.Shapes 1.15
Window {
id: main_window
width: 640
height: 480
visible: true
title: qsTr("Hello World")
color: 'light blue'
// container
ColumnLayout {
id: my_column
anchors.centerIn: parent
width: main_window.width / 3
height: main_window.height / 3
spacing: 0
// contents
ColumnLayout {
id: repeater_element
Layout.fillWidth: true
Layout.fillHeight: false
spacing: 4
Repeater {
model: 7
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: false
Layout.preferredHeight: 75
Layout.alignment: Qt.AlignTop
color: 'red'
}
}
transform: Translate {
id: rect_translate
y: 0
}
}
}
// scroll bar type
Scroll_Bar {
x: 0
y: 0
height: parent.height
width: 20
container_element: my_column
content_element: repeater_element
translate_element: rect_translate
orientation: Qt.Vertical
}
// just a border for the container element
Shape {
ShapePath {
strokeWidth: 4
strokeColor: "black"
fillColor: Qt.rgba(.09, .05, .86, 0)
joinStyle: ShapePath.MiterJoin
startX: my_column.x
startY: my_column.y
PathLine {
relativeX: my_column.width
relativeY: 0
}
PathLine {
relativeX: 0
relativeY: my_column.height
}
PathLine {
relativeX: -my_column.width
relativeY: 0
}
PathLine {
relativeX: 0
relativeY: -my_column.height
}
}
}
}
Scroll_Bar.qml
import QtQuick 2.0
import QtQuick.Controls 2.5
ScrollBar {
property var container_element
property var content_element
property var translate_element
QtObject {
id: internal
property real vertical_size: container_element.height / content_element.height
property real horizontal_size: container_element.width / content_element.width
property real off_the_bottom: (content_element.height - container_element.height) + translate_element.y
}
id: scroll_bar_element
hoverEnabled: true
active: size
orientation: orientation
size: orientation === Qt.Vertical ? internal.vertical_size : internal.horizontal_size
padding: 0
contentItem: Rectangle {
id: ci
radius: 0
color: 'blue'
}
onSizeChanged: {
if(size > 1){
visible = false
}
else{
visible = true
}
}
onPositionChanged: {
if (orientation === Qt.Horizontal) {
translate_element.x = -scroll_bar_element.position * content_element.width
} else {
translate_element.y = -scroll_bar_element.position * content_element.height
}
}
Component.onCompleted: {
scroll_bar_element.onPositionChanged()
}
}
You can hardly write better scrollbar than the existing one, so I made the following code which does the same thing I saw in your example. ScrollBar can be the sibling of a flickable, so it won't take ownership and you can position it where you want. You can even make it rotated.
Is it something that solves your problem?
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
Window {
id: main_window
width: 640
height: 480
visible: true
title: qsTr("Hello World")
color: 'light blue'
Flickable {
id: flickable
anchors.centerIn: parent
width: main_window.width / 3
height: main_window.height / 3
contentWidth: repeater_element.width
contentHeight: repeater_element.height
ScrollBar.vertical: scrollBar
// container
ColumnLayout {
id: my_column
width: main_window.width / 3
height: main_window.height / 3
spacing: 0
// contents
ColumnLayout {
id: repeater_element
Layout.fillWidth: true
Layout.fillHeight: false
spacing: 4
Repeater {
model: 7
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: false
Layout.preferredHeight: 75
Layout.alignment: Qt.AlignTop
color: 'red'
}
}
transform: Translate {
id: rect_translate
y: 0
}
}
}
}
ScrollBar {
id: scrollBar
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
//try this for fun
//rotation: 5
contentItem: Rectangle {
implicitWidth: 20
implicitHeight: 20
color: "blue"
}
}
Rectangle {
color: "transparent"
border.width: 4
anchors.fill: flickable
}
}

How to use qt-component OpacityMask

There is a question How to hide an image using transparent rectangle in QML?
The accepted answer is to use OpacityMask.
I created a qml file follow this answer, but didn't get the expected result.
Is there anything wrong in my codes?
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
ApplicationWindow {
visible: true
width: 1280
height: 800
title: qsTr("Hello World")
Rectangle {
id: background
anchors.fill: parent
color: "black"
}
Image
{
id:underlyingImage
width: 1204
height: 682
fillMode: Image.PreserveAspectCrop
layer.enabled: true
layer.effect: OpacityMask {
maskSource: hiding_rect
}
source:"qrc:/timg.jpg"
}
Rectangle
{
id:hiding_rect
width: underlyingImage.width/2
height: underlyingImage.height/2
color: "yellow"
}
}
the result picture
I'm not familiar with the approach suggested in the other question.
However, following the approach suggested in the documentation (http://doc.qt.io/qt-5/qml-qtgraphicaleffects-opacitymask.html), this works:
Window {
visible: true
width: 1280
height: 800
title: qsTr("Hello World")
Rectangle {
id: background
anchors.fill: parent
color: "black"
}
Image
{
anchors.centerIn: parent
id:underlyingImage
width: 1204
height: 682
fillMode: Image.PreserveAspectCrop
source:"file:///tmp/timg.jpg"
visible: false
}
Item {
id:hiding_rect
anchors.fill: underlyingImage
visible: false
Rectangle
{
anchors.left: parent.left
anchors.top: parent.top
width: underlyingImage.width/2
height: underlyingImage.height/2
color: Qt.rgba(1,1,1,1)
z: underlyingImage.z + 1
}
}
OpacityMask {
anchors.fill: underlyingImage
source: underlyingImage
maskSource: hiding_rect
}
}

How to mask a list view in qml?

I have a list view which need to be masked by an image.
Because am using a highlight component whose width need to be reduced according to the scrolling.
Is it possible with this code ? If anything wrong in this please suggest me some methods.
Item{
id: test
x: 0
y: 0
width: 1920
height: 720
ListView {
id: source_list
x: 0
y: 0
width: 600
height: 720
spacing: 40
model: mediaSongsModel
delegate: mediaSongsDelegate
focus: true
interactive: true
highlightFollowsCurrentItem: true
highlightMoveDuration: 0
highlight: highlightBar
snapMode: ListView.SnapOneItem
preferredHighlightBegin:260/scaleFactor
preferredHighlightEnd: 260/scaleFactor
highlightRangeMode : ListView.ApplyRange
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource:Item {
width: 100
height: 500
Image{
id :crop
x: 0
y: 0
width: 600
height: 720
source :"image/bg.png"
}
}
}
}
Something as you already did:
import QtQuick 2.7
import QtQuick.Window 2.2
import QtGraphicalEffects 1.0
Window {
visible: true
width: 600
height: 600
Rectangle {
id: rect
width: 400
height: 400
anchors.centerIn: parent
color: "black"
visible: (img.status == Image.Ready)
ListView {
anchors.fill: parent
model: 30
delegate: Text { text: "itemmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" + index; color: "yellow" }
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: img
}
}
Image {
id: img
source: "https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/1024px-Google_%22G%22_Logo.svg.png"
width: 400
height: 400
anchors.centerIn: parent
visible: false
}
}
But I don't think you can mask mouse events.

ListView isn't positioned in layout as expected

I have a ColumnLayout with a RowLayout in it. The RowLayout is positioned as expected. This is also true if the windows is being resized. Even if the windows is smaller than the entire ColumnLayout (see second image)
But if I replace the RowLayout by a (horizontal) ListView, this ListView is not positioned as I would expect. I would expect this behaves like the example with the RowLayout but the ListView is positioned higher:
And if the window gets 'to small' the blue rectangle 'moves into' the ListView unlike the first example:
How can I achieve the behaviour of the first example with a ListView?
Source
import QtQuick 2.0
import QtQuick.Layouts 1.1
Rectangle {
width: 360
height: 360
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
Item {
Layout.fillHeight: true
}
/*
ListView {
Layout.fillHeight: true
Layout.fillWidth: true
orientation: ListView.Horizontal
spacing: 5
model: 3
delegate: Rectangle {
width: 50
height: 50
color: "red"
}
}
*/
RowLayout {
Layout.fillHeight: true
Layout.fillWidth: true
Rectangle {
width: 50
height: 50
color: "red"
}
Rectangle {
width: 50
height: 50
color: "red"
}
Rectangle {
width: 50
height: 50
color: "red"
}
}
Item {
Layout.fillHeight: true
}
Rectangle {
width: 50
height: 50
color: "blue"
}
}
}
You just need to define width and height for your ListView. In that way your column layout will consider its size and keep it as a fixed size.
Here your code updated:
import QtQuick 2.0
import QtQuick.Layouts 1.1
Rectangle {
width: 360
height: 360
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
Item {
Layout.fillHeight: true
}
ListView {
//take as much width as possible with a binding
width: parent.width
//keep enought height to display each delegate instance
height: 50
orientation: ListView.Horizontal
spacing: 5
model: 3
delegate: Rectangle {
width: 50
height: 50
color: "red"
}
}
Item {
Layout.fillHeight: true
}
Rectangle {
width: 50
height: 50
color: "blue"
}
}
}

Resources