Computing linear gradients by section so as to mimic the effect of a larger one - qml

Here is my issue. I need a see through hole (square one) in my application. The problem is that my real application contains a gradient over its entire background. To simulate this problem I've written a MWE.
In order to create the hole, I need to create 4 rectangles that I will fill with the proper gradient so as to simulate the over all gradient shown in the code.
Like so:
I'm asking for help on how to write the gradient description in each of the rectangles (1 through 4) so that they would look exactly the same as they do in the picture which is showing 1 big gradient.
import QtQuick
import QtQuick.Controls
import QtQuick.Window
import Qt5Compat.GraphicalEffects
ApplicationWindow {
width: 1920
height: 1080
visible: true
title: qsTr("Render Server Development Helper App")
color: "transparent"
Rectangle {
id: mainRect
anchors.fill: parent
LinearGradient {
anchors.fill: parent
start: Qt.point(0, 0)
end: Qt.point(width,height)
gradient: Gradient {
GradientStop { position: 0.0; color: "#ff0000" }
GradientStop { position: 0.034; color: "#ff0000" }
GradientStop { position: 0.7792; color: "#0000ff" }
GradientStop { position: 1.0; color: "#0000ff" }
}
}
Column {
id: buttonRow
width: 0.1*parent.width
anchors.left: parent.left
anchors.top: parent.top
Button {
id: btnConnect
hoverEnabled: false;
width: parent.width
text: "CONNECT"
onClicked: {
console.log("Connecting ....");
}
}
}
Rectangle {
id: frame
width: parent.width*0.7;
height: parent.height*0.8;
border.color: "#000000";
border.width: 5;
//anchors.centerIn: renderServerView
anchors.centerIn: parent
color: "transparent"
}
}
}

I would use the same approach as we discussed here and avoid complicated gradient stitching.
Be aware that translucent windows only work on systems that have compositing enabled. Have a look here.
Canvas
import QtQuick
Window {
id: root
width: 640
height: 480
visible: true
title: qsTr("Hello World")
color: "transparent"
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext("2d")
var gradient = ctx.createLinearGradient(0, 0, root.width, root.height)
gradient.addColorStop(0.0, "red")
gradient.addColorStop(1.0, "blue")
ctx.fillStyle = gradient
ctx.beginPath()
ctx.fillRect(0, 0, root.width, root.height)
ctx.fill()
ctx.clearRect(220, 140, 200, 200)
}
}
}
OpacityMask
import QtQuick
import Qt5Compat.GraphicalEffects
Window {
id: root
width: 640
height: 480
visible: true
color: "transparent"
LinearGradient {
id: bg
visible: false
anchors.fill: parent
start: Qt.point(0, 0)
end: Qt.point(root.width, root.height)
gradient: Gradient {
GradientStop { position: 0.0; color: "red" }
GradientStop { position: 1.0; color: "blue" }
}
}
Item {
id: cutout
visible: false
anchors.fill: parent
Rectangle {
width: 200
height: 200
anchors.centerIn: parent
}
}
OpacityMask {
anchors.fill: bg
source: bg
maskSource: cutout
invert: true
}
}

Related

QML ListView delegate with Component Row does't render correctly

And now I have a problem with my qml script. here's the simple code:
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: root
anchors.fill: parent
color: "green"
SystemPalette { id: activePalette }
ColumnLayout {
id: rightPanel
Layout.fillHeight: true
Layout.fillWidth: true
Layout.rightMargin: 10
anchors.fill: parent
Component.onCompleted: {
console.log(this, width, parent, parent.width)
}
ListView {
spacing: 2
Layout.fillHeight: true
Layout.fillWidth: true
model: ListModel {
ListElement {
name: "nihao"
value: "1"
}
ListElement {
name: "fds"
value: "2"
}
ListElement {
name: "fdssd"
value: "4"
}
}
delegate: Component {
// Rectangle {
// color: "yellow"
// height: 40
// width: parent.width
Row {
anchors.fill: parent
spacing: 2
height: 40
width: 200
Rectangle {
color: activePalette.window
height: 25
width: 100
border.color: "white"
border.width: 3
Text {
anchors.centerIn: parent
text: name
}
}
Rectangle {
color: activePalette.window
height: 25
width: 100
border.color: "white"
border.width: 3
Text {
anchors.centerIn: parent
text: value
}
}
}
//}
}
}
}
}
}
The code does't display correctly when I use qmlscene to render it, it even render nothing if the ListModel is too long.
But, if I uncomment out the "Rectangle" code in delegate component, it works well. So I'm comfused with the difference between Reactangle and Row for that they are all inherited from Item. And what should be placed into the delegate component as its direct child?

QML: TableView and Columns

I noticed that the column that is on the right always overlaps the data from the column that is on the left. How to change this behavior?
The panel with shadow is defined into TableViewColumn.delegate. I need the panel to overlap the neighbor in the right cell.
// Minimal code example that represent a problem!
Page {
TableView {
anchors.fill: parent
selectionMode: 1
TableViewColumn {
role: "login"
title: "Login"
width: 100
delegate: Rectangle {
color: "transparent"
anchors.fill: parent
Rectangle {
anchors.verticalCenter: parent.verticalCenter
height: 40
width: 150
color: "green"
}
}
}
TableViewColumn {
role: "fullName"
title: "FullName"
width: 100
delegate: Rectangle {
color: "transparent"
anchors.fill: parent
Rectangle {
anchors.verticalCenter: parent.verticalCenter
height: 30
width: 150
color: "red"
}
}
}
TableViewColumn {
role: "status"
title: "Status"
width: 100
delegate: Rectangle {
color: "transparent"
anchors.fill: parent
Rectangle{
anchors.verticalCenter: parent.verticalCenter
height: 20
width: 150
color: "blue"
}
}
}
model: ListModel {
ListElement { text: "ghdjbf" }
ListElement { text: "sgkjdnf" }
ListElement { text: "sgkjdnf" }
ListElement { text: "sgkjdnf" }
ListElement { text: "sgkjdnf" }
}
property Component headerDelegate: BorderImage {
property bool sortSwitcher: false
height: 40
id: tableHeader
objectName: "TableHeader"
Rectangle {
id: viewableSection
anchors.fill: parent
color: "white" // "#f2f2f2"
Text {
id: textItem
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
anchors.leftMargin: 25
font.pixelSize: 11
font.family: "Roboto"
text: styleData.value
font.bold: true
elide: Text.ElideRight
color: "#646464"
renderType: Text.NativeRendering
}
Rectangle {
id: bottomHeaderBorder
anchors.top: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: "#ebebeb"
}
Rectangle {
id: headerSeparator
z: 1
anchors.left: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
anchors.topMargin: 8
width: 1
color: "lightgrey"
MouseArea {
id: mouseAreaSeparator
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.SizeHorCursor
onEntered: {
mouseAreaSeparator.cursorShape = Qt.SizeVerCursor
}
}
}
}
}
property Component rowDelegate: Rectangle {
id: rowDel
objectName: "RowDelegate"
height: 50
//property color selectedColor: styleData.hasActiveFocus ? primaryColor : secondaryColor
property bool selectionMaskVisible
property bool selected: false
Rectangle {
id: rowSelectionMask
anchors.fill: parent
color: rowDel.selectionMaskVisible ? "#f2f2f2" : "#ffffff"
visible: parent.activeFocus ? false : true
z: parent.activeFocus ? 1 : 2
}
Rectangle {
id: activeFocusMask
anchors.fill: parent
color: parent.activeFocus ? styleData.row !== undefined ? secondaryColor : "White" : "White"
opacity: styleData.row !== undefined ? 0.9 : 0
z: parent.activeFocus ? 2 : 1
}
MouseArea {
id: rowMouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onEntered: {
if (styleData.row !== undefined)
{
rowDel.selectionMaskVisible = true
}
}
onExited: {
rowDel.selectionMaskVisible = false
}
}
Rectangle {
id: bottomBorder
objectName: "BottomBorder"
z: 2
anchors.bottom: parent.bottom
height: 1
width: parent.width
color: "#ebebeb"
opacity: styleData.row !== undefined ? 1 : 0
}
Rectangle {
id: separator
z: 1
anchors.left: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.bottomMargin: 1
anchors.topMargin: 1
width: 1
color: "red"
}
}
}
}
Judging from the code there is no clean solution to it.
But as you can see, the delegate of the TableViewColumn is instantiated by a Loader via a Repeater into a Row.
So to change get the first column on top of the second column, we need to change the z-index - but not of the delegate itself (as this would only affect the children of the same Loader) but of the Loader. This can be achieved e.g in the Component.onCompleted-handler.
TableViewColumn {
role: "login"
title: "Login"
width: 100
delegate: Rectangle {
color: "transparent"
anchors.fill: parent
Component.onCompleted: parent.z = 3 // <- Put the loader on top of its siblings
Rectangle {
anchors.verticalCenter: parent.verticalCenter
height: 40
width: 150
color: "green"
}
}
Component.onCompleted: console.log(Object.keys(this))
}

QML: Problems with mousearea overlapping

I have a QML application and problems with MouseAreas.
In a small test app, there is a red rectangle and when mouse enters this rect, a grey menu appears below (created with a Loader).
This grey menu must be open while mouse is over the red rectangle or the menu. For this purpose, I have 2 MouseAreas, 1 over the red rect and 1 over the menu.
Both are 'hoverEnabled' and with 'enter' and 'exit' I control 'hoverDialog' and 'hoverTopZone'.
When both are false, it means that the mouse is out, so I close the menu (using a signal, the Loader gets inactive).
The timer is required since when passing from 'mouseAreaTopZone' to 'mouseAreaDialog' there is just a moment with 'hoverDialog' and 'hoverTopZone' are both false.
Fixed with the timer.
In the middle of the menu there is a green rect, and (only) when mouse is over there, a yellow rect must be visible.
There is my problem. I have a MouseArea inside the green rect, but the yellow rect is not visible when required.
If I move 'rectGreen' below 'mouseAreaTopZone' and 'mouseAreaDialog' (that is, at the end of the file) I get the yellow rect visible when the mouse is over green rect, since its mouse area is then 'topmost'
BUT in this case, the menu dialog is closed, since when the mouse enters the MouseArea inside green rect, hoverDialog and hoverTopZone are false...
I hope U can understand my problem... Here is my code:
Test.qml
import QtQuick 2.5
import QtQuick.Controls 1.3
import QtQuick.Window 2.0
Item {
width: 800
height: 800
Rectangle{
id: rect
anchors { top: parent.top; topMargin: 100; horizontalCenter: parent.horizontalCenter }
height: 50; width: 50
color: "red"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: loader_dialog.active = true
}
}
Loader {
id: loader_dialog
anchors { top: rect.bottom; horizontalCenter: rect.horizontalCenter}
active: false
sourceComponent: TestMenu {
onClose: loader_dialog.active = false;
}
}
}
TestMenu.qml
import QtQuick 2.0
Rectangle {
id: id_dialog
signal close()
width: 400
height: 600
color: "lightgrey"
property bool hoverDialog: false
property bool hoverTopZone: false
function update() {
if (!hoverDialog && !hoverTopZone)
timer.start();
}
function check() {
if (!hoverDialog && !hoverTopZone)
{
console.log("close");
id_dialog.close();
}
}
Timer {
id: timer
interval: 100
running: false
repeat: false
onTriggered: check();
}
Rectangle {
id: rectGreen
width: 200; height: 100
anchors.centerIn: parent
color: "green"
Rectangle {
id: rectYellow
anchors.centerIn: parent
width: 50; height: 50
color: "yellow"
visible: false
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: { rectYellow.visible = true; }
onExited: { rectYellow.visible = false }
}
}
MouseArea {
id: mouseAreaTopZone
anchors { bottom: parent.top; horizontalCenter: parent.horizontalCenter}
width: 50; height: 50
hoverEnabled: true
onEntered: { hoverTopZone = true; id_dialog.update(); }
onExited: { hoverTopZone = false; id_dialog.update(); }
}
MouseArea {
id: mouseAreaDialog
anchors.fill: parent
hoverEnabled: true
onEntered: { hoverDialog = true; id_dialog.update(); }
onExited: { hoverDialog = false; id_dialog.update(); }
}
}
Thanks in advance,
Diego
Thanks Mark Ch for your help.
I need to close the dialog when the mouse exits, so I think I can not use 'Popup' control...
I solved the problem. Using only one variable to know if the mouse is over my dialog ('m_iNumHovered'), I add a reference every time I enter in a Mouse Area, and I decrease it when I exit. The key was to add/remove a reference in the MouseArea over the green rectangle, to keep it 'm_iNumHovered=true' (dialog visible)
New code for TestMenu.qml:
import QtQuick 2.0
Rectangle {
id: id_dialog
signal close()
width: 400
height: 600
color: "lightgrey"
property int m_iNumHovered: 0
onM_iNumHoveredChanged: update();
function update() {
if (m_iNumHovered == 0)
timer.start();
}
function check() {
if (m_iNumHovered == 0)
id_dialog.close();
}
Timer {
id: timer
interval: 100
running: false
repeat: false
onTriggered: check();
}
MouseArea {
id: mouseAreaTopZone
anchors { bottom: parent.top; horizontalCenter: parent.horizontalCenter}
width: 50; height: 50
hoverEnabled: true
onEntered: m_iNumHovered++;
onExited: m_iNumHovered--;
}
MouseArea {
id: mouseAreaDialog
anchors.fill: parent
hoverEnabled: true
onEntered: m_iNumHovered++;
onExited: m_iNumHovered--;
}
Rectangle {
id: rectGreen
width: 200; height: 100
anchors.centerIn: parent
color: "green"
Rectangle {
id: rectYellow
anchors.centerIn: parent
width: 50; height: 50
color: "yellow"
visible: false
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: { m_iNumHovered++; rectYellow.visible = true; }
onExited: { m_iNumHovered--; rectYellow.visible = false }
}
}
}

Span a Gradient on several Rectangle

I want a single LinearGradient to be used as the background, spanned into several sibling Rectangles. In each Rectangle, you can see a different part of the gradient.
I use an OpacityMask to « fill » the Rectangles with the gradient.
The LinearGradient width is the sum of each Rectangle width.
If I use a single Rectangle, it works. Beginning with two, it does not behave correctly, whatever I try (I leave bits in the code below to imagine). The best result I had so far is having the same part of the LinearGradient repeated in each Rectangle.
I guess I could use one LinearGradient per Rectangle, changing the GradientStop values, but it looks complicated and I guess there is a simple an elegant solution.
Rectangle
{
id: page1
// anchors.fill: parent
// id: masqCont
anchors.centerIn: parent
border.color: "blue"
width: childrenRect.width
height: childrenRect.height
visible: false
Rectangle
{
id: masq1
y:0
border.color: "red"
border.width: 10
width: 100
height: 100
radius: 40
Text {text: "Un"}
visible: true
}
Rectangle
{
x:width
id: masq2
border.color: "red"
border.width: 10
width: 100
height: 100
radius: 40
Text {text: "deux"}
Text {text: "deux"}
visible: true
}
}
LinearGradient {
id:grad
width: 200 //masqCont.childrenRect.width
height: 100//masqCont.childrenRect.height
//anchors.fill: masqCont
start: Qt.point(0, 0)
end: Qt.point(200,100)//masqCont.width, masqCont.height)
gradient: Gradient {
GradientStop { position: 0.0; color: "white" }
GradientStop { position: 1.0; color: "black" }
}
visible: false
}
OpacityMask {
id: om21
anchors.fill: page1;
source: grad
maskSource: page1;
}
// OpacityMask {
// id: om21
// anchors.fill: masq1;
// source: grad
// maskSource: masq1;
// }
// OpacityMask {
// id: om22
// anchors.fill: masq2;
// source: grad
// maskSource: masq2;
// }
// }
// }
You were almost there.
The major problem is you chose a white Rectangle as a container for your child rectangles. This causes the entire LinearGradient to be displayed, as the mask is fully opaque (no alpha).
See below for a working example (you can move the rectangles by dragging them)
import QtQuick 2.7
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
import QtGraphicalEffects 1.0
ApplicationWindow
{
visible: true
width: 500
height: 400
Item
{
id: page1
anchors.fill: parent
opacity: 0
Repeater
{
model: 20
Rectangle
{
id: masq1
x:Math.random()*(page1.width-width)
y:Math.random()*(page1.height-height)
width: 50
height: 50
radius: 5
MouseArea
{
anchors.fill: parent
drag.target: parent
}
}
}
}
LinearGradient {
id:grad
anchors.fill: page1
start: Qt.point(0, 0)
end: Qt.point(page1.width, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "white" }
GradientStop { position: 1.0; color: "black" }
}
visible: false
}
OpacityMask {
anchors.fill: page1;
source: grad
maskSource: page1;
}
}
If it is just Rectangles you want, OpacityMask is an overkill.
You might just use a ShaderEffectSource instead.
You set the soruceItem to be your gradient, and then select the sourceRect to be equal to you Rectangle. You might need to use map[From/To]Item to map the coordinates correctly.
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: win
visible: true
width: 400
height: 400
Rectangle {
id: grad
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0; color: 'blue' }
GradientStop { position: 0.33; color: 'red' }
GradientStop { position: 0.66; color: 'yellow' }
GradientStop { position: 1; color: 'blue' }
}
visible: false
}
ShaderEffectSource {
sourceItem: grad
x: 50
y: 50
height: 100
width: 200
sourceRect: Qt.rect(x, y, width, height)
}
ShaderEffectSource {
sourceItem: grad
x: 280
y: 80
height: 150
width: 70
sourceRect: Qt.rect(x, y, width, height)
}
}

MouseArea events on a Listview Component in QtQuick2

We are working on a touchscreen device and we are facing an issue with the following code:
import QtQuick 2.1
import QtQuick.Window 2.0
Window {
visible: true
id: box
width: 360
height: 360
ListView {
id: list
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
model: 30
delegate: data
clip: true
focus: true
smooth: true
}
Component{
id: data
Item{
id: item
width: list.width
height: 30
clip: true; smooth: true; visible: true
property string cellColor: getCellColor(index)
Rectangle{
id: condData_item_line
width: parent.width
height: parent.height
color: cellColor
clip: true; smooth: true
Text{
text: index
anchors.centerIn: parent
}
MouseArea{
anchors.fill: parent
onClicked: {
console.log("click", index, mouse.x, mouse.y)
}
onPressed : {
console.log("press", index, mouse.x, mouse.y)
}
onReleased: {
console.log("release", index, mouse.x, mouse.y)
}
onCanceled: {
console.log("cancel")
}
}
}
}
}
function getCellColor(index){
var color;
if(index % 2 == 0){
color = "white";
}else{
color = "blue";
}
return color;
}
}
Whereas we defined handles for the clicked and released events they are never called. Is this normal? If yes, how should we code the event handling so that clicked and released events are fired on the Rectangle components?
Additional info: With this code, touching the first item (index:0), clicked and released events are fired properly. But touching the other items we only get canceled events.