How do I set row and column in a GridLayout when working with components? - qml

I would like to fill a GridLayout with different Components which are depending on the modelData retrieved by a simple array model.
Item {
id: root
property var xdata: [
{"row": 1, "col": 1, "type": "A"},
{"row": 2, "col": 2, "type": "B"},
{"row": 3, "col": 3, "type": "C"}
]
GridLayout {
id: grid
width: 300
height: 300
rows: 3
columns: 3
Repeater {
model: root.xdata
delegate: Component {
Loader {
sourceComponent: switch(modelData["type"]) {
case "A": return test_a
case "B": return test_b
case "C": return test_c
}
}
}
}
Component {
id: test_a
Rectangle {
Layout.row: 1
Layout.column: 1
width: 100
height: 100
color: "red"
}
}
Component {
id: test_b
Rectangle {
Layout.row: 2
Layout.column: 2
width: 100
height: 100
color: "green"
}
}
Component {
id: test_c
Rectangle {
Layout.row: 3
Layout.column: 3
width: 100
height: 100
color: "blue"
}
}
}
}
This works just fine in automatic order, but when I set (staticly or dynamically) the components attached property Layout.row or Layout.column, nothing happens.

[EDIT/REWRITE]
Generally, when using Layout, it's best to use Layout.preferredWidth and Layout.preferredHeight instead of width and height. Also, I implemented DelegateChooser instead of Loader.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.qmlmodels
Page {
property var xdata: [
{"row": 1, "col": 1, "type": "A"},
{"row": 2, "col": 2, "type": "B"},
{"row": 3, "col": 3, "type": "C"}
]
GridLayout {
id: grid
width: 300
height: 300
Repeater {
model: xdata
delegate: DelegateChooser {
role: "type"
DelegateChoice { roleValue: "A"; TestA { } }
DelegateChoice { roleValue: "B"; TestB { } }
DelegateChoice { roleValue: "C"; TestC { } }
}
}
}
}
// TestA.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Rectangle {
Layout.row: modelData.row
Layout.column: modelData.col
Layout.preferredWidth: 100
Layout.preferredHeight: 100
color: "red"
}
// TestB.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Rectangle {
Layout.row: modelData.row
Layout.column: modelData.col
Layout.preferredWidth: 100
Layout.preferredHeight: 100
color: "green"
}
// TestC.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Rectangle {
Layout.row: modelData.row
Layout.column: modelData.col
Layout.preferredWidth: 100
Layout.preferredHeight: 100
color: "blue"
}
You can Try it Online!

Related

TableView is overlapping TabBar

I have this inside of my main.qml:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Controls.Imagine
ApplicationWindow {
visible: true
width: Screen.width
height: Screen.height
title: "Portmod"
TabBar {
id: bar
width: parent.width
TabButton {
text: "Manage"
width: implicitWidth
}
TabButton {
text: "Search"
width: implicitWidth
}
}
StackLayout {
width: parent.width
height: parent.height
currentIndex: bar.currentIndex
TableView {
id: manageTab
columnSpacing: 1
rowSpacing: 1
clip: true
model: installed_pkgs_model
selectionModel: ItemSelectionModel {}
delegate: Rectangle {
implicitWidth: 300
implicitHeight: 50
color: selected ? "blue" : "lightgray"
required property bool selected
Text {
text: display
}
}
}
Item {
id: searchTab
}
}
}
It's displaying my TableView and TabBar with two TabButtons fine, but the TableView is overlapping with the TabBar and I have to drag the TableView out of the way to see it.
I'd like the TableView to be moved down so the TabBar is always visible, but I'm not sure how to do this. The layout system is a bit confusing to me.
A simple way would be to move the TabBar into a header:
header: TabBar {
id: bar
width: parent.width
TabButton {
text: "Manage"
width: implicitWidth
}
TabButton {
text: "Search"
width: implicitWidth
}
}
Using layouts, you could do it like this:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Controls.Imagine
import Qt.labs.qmlmodels 1.0
ApplicationWindow {
width: Screen.width
height: Screen.height
title: "Portmod"
visible: true
ColumnLayout {
anchors.fill: parent
spacing: 0
TabBar {
id: bar
Layout.fillWidth: true
TabButton {
text: "Manage"
width: implicitWidth
}
TabButton {
text: "Search"
width: implicitWidth
}
}
StackLayout {
currentIndex: bar.currentIndex
Layout.fillWidth: true
Layout.fillHeight: true
TableView {
id: manageTab
columnSpacing: 1
rowSpacing: 1
clip: true
model: TableModel {
TableModelColumn { display: "name" }
TableModelColumn { display: "color" }
rows: [
{
"name": "cat",
"color": "black"
},
{
"name": "dog",
"color": "brown"
},
{
"name": "bird",
"color": "white"
}
]
}
selectionModel: ItemSelectionModel {}
delegate: Rectangle {
implicitWidth: 300
implicitHeight: 50
color: selected ? "blue" : "lightgray"
required property bool selected
required property string display
Text {
text: display
}
}
}
Item {
id: searchTab
}
}
}
}

How to show/hide KDE plasmoid's tooltip programatically?

Is there a way to make a plasmoid tooltip to show/hide programatically?
I tried by setting a ToolTipArea over the compact representation, and trying to trigger it with a Timer - it does not work (the regular tooltip keeps showing but only when hovering the plasmoid icon (aka compactRepresentation):
import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
Item {
Layout.preferredWidth: 200
Layout.preferredHeight: 300
Plasmoid.preferredRepresentation: Plasmoid.compactRepresentation
Plasmoid.compactRepresentation: Item
{
anchors.fill: parent
MouseArea
{
onClicked:
{
plasmoid.expanded = !plasmoid.expanded
}
}
PlasmaCore.ToolTipArea
{
id: toolTip
width: parent.width
height: parent.height
anchors.fill: parent
mainItem: tooltipContentItem
active: false
interactive: true
}
Timer
{
interval: 3000
running: true
repeat: true
onTriggered:
{
if (tooltipContentItem.active == false)
{
toolTip.showToolTip()
toolTip.active == true
}
else
{
toolTip.hideToolTip()
toolTip.active == false
}
}
}
}
Item
{
id: tooltipContentItem
implicitWidth: 300
implicitHeight: 200
ColumnLayout
{
id: mainLayout
anchors
{
left: parent.left
top: parent.top
margins: PlasmaCore.Units.gridUnit / 2
}
PlasmaExtras.Heading
{
id: tooltipMaintext
level: 3
Layout.minimumWidth: Math.min(implicitWidth, preferredTextWidth)
Layout.maximumWidth: preferredTextWidth
elide: Text.ElideRight
text: "Test"
}
PlasmaComponents.Label
{
id: tooltipSubtext
Layout.minimumWidth: Math.min(implicitWidth, preferredTextWidth)
Layout.maximumWidth: preferredTextWidth
text: "Testing text"
opacity: 0.6
}
}
}
}
There's the toolTipItem QQuickItem too, but I cannot figure out if it is possible to make it show or hide on command (this bit was borrowed from KDE's digitalclock plasmoid:
import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
Item {
Layout.preferredWidth: 200
Layout.preferredHeight: 300
Plasmoid.preferredRepresentation: Plasmoid.compactRepresentation
Plasmoid.compactRepresentation: Item
{
anchors.fill: parent
MouseArea
{
onClicked:
{
plasmoid.expanded = !plasmoid.expanded
}
}
}
plasmoid.toolTipItem: Loader
{
id: toolTipLoader
source: "Tooltip.qml" // Just holds the tooltip contents
}
}

QML Repeater for adding multiple Items to GridLayout?

How do you add multiple elements to a GridLayout using a single Repeater?
The GridLayout component ensures that rows and columns are sized to fit to the largest item in each cell, but if you want to populate it using a Repeater it seems impossible because a Repeater can only have one delegate.
There is another SO page asking how to do it without a wrapping Item, but I've never seen a wrapping Item solution and don't know why it would not be preferable.
How would you create the following?
Here is the model data for that image:
model: [ // JSON model
{
name: "February:",
types: [
{ name: "R-1", size: 24, color: "red" },
{ name: "O--2", size: 16, color: "orange" },
{ name: "Y---3", size: 8, color: "yellow" },
]
},
{
name: "March:",
types: [
{ name: "G-1", size: 24, color: "green" },
]
},
{
name: "April:",
types: [
{ name: "B-1", size: 24, color: "blue" },
{ name: "I--2", size: 16, color: "indigo" },
{ name: "V---3", size: 8, color: "violet" },
]
},
]
Add multiple Items as children to the Repeater's delegate Item and use JavaScript to reparent them into the GridLayout when the delegate Item is completed.
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
Window {
width: 360
height: 280
visible: true
title: qsTr("Repeater With Multiple Items")
function reparentChildren(source, target) {
while (source.children.length)
source.children[0].parent = target;
}
GridLayout {
id: grid
anchors.centerIn: parent
columns: 3
Repeater {
Item {
id: wrappingItem
Component.onCompleted: reparentChildren(this, grid)
Text {
Layout.alignment: Qt.AlignTop|Qt.AlignRight
Layout.rowSpan: modelData.types.length
text: modelData.name
}
Repeater {
model: modelData.types
Item {
Component.onCompleted: reparentChildren(this, wrappingItem)
Text {
Layout.alignment: Qt.AlignTop|Qt.AlignHCenter
text: modelData.name
}
Rectangle {
width: modelData.size; height: width
color: modelData.color
}
}
}
}
model: [ // JSON model
{
name: "February:",
types: [
{ name: "R-1", size: 24, color: "red" },
{ name: "O--2", size: 16, color: "orange" },
{ name: "Y---3", size: 8, color: "yellow" },
]
},
{
name: "March:",
types: [
{ name: "G-1", size: 24, color: "green" },
]
},
{
name: "April:",
types: [
{ name: "B-1", size: 24, color: "blue" },
{ name: "I--2", size: 16, color: "indigo" },
{ name: "V---3", size: 8, color: "violet" },
]
},
]
}
}
}

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 Render the same Entity in two Scene3Ds

How can I insert the same entity to be displayed into more than one Scene3Ds in QML?
(it'll probably be abundantly clear that I'm pretty new to QML)
I would like to create something similar to the Multi Viewport QML Example, except I would like to have the viewports in differet layouts (e.g. a SplitView).
Ultimately I would like my entities (Mesh or SceneLoader entities) to be created completely outside of my views (my Scene3D), and be able to show the same data in multiple views without having copies.
I haven't had much luck putting a Layout into a Scene3D, and even so, that would constrain me against having the same data show up elsewhere. I simply can't figure out how to define my data outside the views, especially since I can't figure out how to append to the components/data/children properties.
For example, in the (long) example below, I figured out how to define torusMesh2 outside of the Scene3D by setting it's .parent to inject it into scene3DRightEntity, but I cannot figure out how to inject it into both Scene3Ds
import QtQuick 2.0
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Input 2.0
import QtQuick.Layouts 1.3
import QtQuick.Controls 1.3
import QtQuick.Scene3D 2.0
import Qt3D.Extras 2.0
Item {
Button {
text: 'Button'
onClicked: {
console.log('Button clicked');
}
}
SphereMesh {
id: torusMesh2; radius: 5
parent: scene3DRightEntity
}
PhongMaterial { id: material2; parent: scene3DRightEntity }
Transform { id: torusTransform2; scale3D: Qt.vector3d(1.5, 1, 0.5); rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45); parent: scene3DRightEntity }
Rectangle {
id: topRect
anchors.fill: parent; anchors.margins: 50
color: 'green'
SplitView {
anchors.fill: parent; orientation: Qt.Horizontal
Rectangle {
id: scene
anchors.margins: 50; width: 200; Layout.minimumWidth: 100; Layout.maximumWidth: 500
color: "darkRed"
Text { text: "View 1"; anchors.centerIn: parent }
Scene3D {
id: scene3dLeft
anchors.fill: parent; anchors.margins: 10; focus: true
aspects: ["input", "logic"]
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
Entity {
SimpleCamera {
id: camera1; fieldOfView: 45; position: Qt.vector3d( 0.0, 0.0, 40.0 )
}
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
camera: camera1.camera
clearColor: "transparent"
}
}
, InputSettings { }
]
TorusMesh {
id: torusMesh1; radius: 5; minorRadius: 1; rings: 100; slices: 20
}
PhongMaterial { id: material1 }
Transform { id: torusTransform1; scale3D: Qt.vector3d(1.5, 1, 0.5); rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45) }
Entity {
id: torusEntity1
components: [ torusMesh1, material1, torusTransform1 ]
}
}
}
}
Rectangle {
id: scene2
Layout.fillWidth: true; Layout.minimumWidth: 50; Layout.maximumWidth: 400; height: 300
color: "darkBlue"
Scene3D {
id: scene3dRight
anchors.fill: parent; anchors.margins: 50; focus: true;
aspects: ["input", "logic"]
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
Entity {
id: scene3DRightEntity
SimpleCamera {
id: camera2
position: Qt.vector3d( 0.0, 0.0, 40.0 )
}
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
camera: camera2.camera
clearColor: "transparent"
}
}
, InputSettings { }
]
Entity {
id: torusEntity2
components: [ torusMesh2, material2, torusTransform2 ]
}
}
}
}
}
}
} // Item
UPDATE
My first post was creating torusMesh2 as a child of the SplitView, this edit moves it to the top of the tree and sets its .parent.