QT 5.3 / QML: Resizable StackView depending on currentItem - qt

I'm working on a QML StackView that starts with a list of items to select from. Once selected I want to transition _.push(...) to a input form which has larger dimensions than the initialItem.
The only way I have trial-and-errored my way into a situation that works is by making the form Item a nested borderless window.
Q1. A nested window can't be the right type of concept to use for this... right ? there must be another way to do it. What is the right way ?
Q2. My goal after this is to have a transition animation that grows or shrinks between stacks of different sizes. Advice that doesn't preclude that would be best.
code
Main.qml :
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
ApplicationWindow {
property int itemHeight: 30
property int cornerRadius : 5
visible: true
color: "transparent"
flags: Qt.FramelessWindowHint
ListModel {
id: searchFacets
ListElement {
title: "Topics"
page: "content/TopicSearch.qml"
}
// ListElement {
// title: "Domains"
// }
}
StackView {
id: stackView
focus: true
initialItem: SearchFacets {
id: facets
}
delegate: StackViewDelegate {
pushTransition: StackViewTransition {
PropertyAnimation {
target: enterItem
property: "opacity"
from: 0
to: 1
}
}
}
}
}
Initial Item:
import QtQuick 2.3
Item {
height : listView.count * itemHeight
ListView {
id: listView
model: searchFacets
anchors.fill: parent
focus: true
highlightFollowsCurrentItem: false
highlight: Rectangle {
width: parent.width
height: itemHeight
radius : cornerRadius
color: "green"
opacity: 0.5
z:2
x: listView.currentItem.x;
y: listView.currentItem.y
Behavior on y {
SpringAnimation {
spring: 60
damping: 1.0
}
}
}
delegate: Component {
Item {
width: parent.width
height : itemHeight
Rectangle {
anchors.fill: parent
color: "#212126"
radius: cornerRadius
z:0
border.width: 2
border.color : "white"
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
console.log("clicked index: " + index)
listView.currentIndex = index
// listView.forceActiveFocus()
stackView.push(Qt.resolvedUrl(page))
}
}
Text {
text: title
font.pixelSize: 24
font.bold: true
z:1
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
color: "white"
antialiasing: true
}
}
}
}
}
Input Form:
import QtQuick 2.3
import QtQuick.Window 2.0
Item {
Window {
width: 400
height: 400
visible: true
flags: Qt.FramelessWindowHint
Rectangle {
anchors.fill: parent
visible: true
color: "red"
}
}
}

One possible solution is to update the size of the dimensions of the StackView in the click handler that causes the transition. I do not know if that causes any problems with animating the transition.
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
console.log("clicked index: " + index)
listView.currentIndex = index
var component = Qt.createComponent(page)
var res = component.createObject(stackView)
stackView.height = res.height
stackView.width = res.width
stackView.push(res)
}
}

Related

QT QML how to create a reusable button component

I have some simulated buttons in on rectangle. These buttons I need at different places in the app. Is it possible to make a kind of component out of it?
This is how it is currently
RowLayout {
anchors.fill: parent
Rectangle {
id: button1
height: _buttonsHeight * 0.6
width: height
radius: height / 2
border.width: 1
border.color: "black"
Image {
anchors.centerIn: parent
source: "image://iconProvider/icons/128/button1.png"
sourceSize.height: parent.height * 0.8
}
MouseArea {
anchors.fill: parent
onPressed: btnHome.border.width = 2
onReleased: btnHome.border.width = 0
onClicked: userInputDevice.buttonClicked("button1")
}
}
Rectangle {
id: button2
height: _buttonsHeight * 0.6
width: height
radius: height / 2
border.width: 1
border.color: "black"
Image {
anchors.centerIn: parent
source: "image://iconProvider/icons/128/button2.png"
sourceSize.height: parent.height * 0.8
}
MouseArea {
anchors.fill: parent
onPressed: btnHome.border.width = 2
onReleased: btnHome.border.width = 0
onClicked: userInputDevice.buttonClicked("button2")
}
}
}
Something like this would be the goal
RowLayout {
anchors.fill: parent
Button {id = "button1", height = _buttonsHeight * 0.6, icon = "button1.png", command = "button1", parent = this}
Button {id = "button2", height = _buttonsHeight * 0.6, icon = "button2.png", command = "button2", parent = this}
}
Is something like this feasible ?
Thanks and best regards
Arne
You should read the documentation about QML defining types.
Don't use the assignment operator, but the colon to create bindings. No need to set any parent.
For the nested Image.source property you should create an alias property in the root of your component like so property alias imageSource: <imageID>.source to be able to set it from the outside.
import QtQuick
import QtQuick.Layouts
Window {
id: root
width: 320
height: 240
visible: true
property int btnHeight: 80
component CustomButton : Rectangle {
property alias icon: image.source
signal pressed
signal released
signal clicked
id: buttonRoot
height: 20
width: height
radius: height / 2
border.width: 1
border.color: "black"
Image {
id: image
anchors.centerIn: parent
source: "https://upload.wikimedia.org/wikipedia/commons/7/72/Variable_Resistor.svg"
fillMode: Image.PreserveAspectFit
width: buttonRoot.height / Math.sqrt(2)
height: image.width
}
MouseArea {
anchors.fill: parent
onPressed: buttonRoot.pressed()
onReleased: buttonRoot.released()
onClicked: buttonRoot.clicked()
}
}
RowLayout {
anchors.centerIn: parent
spacing: 20
CustomButton {
id: button1
height: root.btnHeight
onClicked: console.log("Button 1 clicked")
}
CustomButton {
id: button2
height: root.btnHeight
icon: "https://upload.wikimedia.org/wikipedia/commons/f/fd/Crystal_Clear_app_download_manager.svg"
onClicked: console.log("Button 2 clicked")
}
}
}
You can create a new qml file let's name it MyButton.qml
MyButton is a custom button with icon and text you can change it to fit your needs.
MyButton.qml:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Qt.labs.platform 1.0
import QtLocation 5.12
import QtQuick.Controls.Material 2.15
MouseArea {
id: control
width: control.w
height: control.h
property string icon_btn
property bool icon_visible: true
property string text
property int border: 3
property string borderColor: "white"
property string colorr: "#222222"
property int w: 50
property int h: 50
property bool text_visible: true
property bool clicked: false
property int radius: 12
property int contentLeftMargin: 0
property string buttonColor: enabled ? control.colorr : "grey"
cursorShape: Qt.PointingHandCursor
Rectangle {
border.color: control.borderColor
border.width: control.border
radius: control.radius
anchors.fill: parent
color: control.buttonColor
RowLayout{
anchors.fill: parent
Item { Layout.fillWidth: true }
Image{
id: img
visible: control.icon_visible
source: control.icon_btn
Layout.preferredWidth: 35
Layout.preferredHeight: 35
//Layout.leftMargin: control.contentLeftMargin
}
Text{
id: txt
text: control.text
font.pointSize: 17
font.bold: true
color: "white"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
visible: control.text_visible ? x + width + 15 < control.width : 0
//Layout.leftMargin: control.contentLeftMargin
}
Item { Layout.fillWidth: true }
}
}
Layout.bottomMargin: 10
}
Then use it in your main qml file.
For example:
Window {
MyButton{
id: btn_refresh
icon_btn: "qrc:/Icons/outline_refresh_white_48pt_3x.png"
onClicked: {
//do something on click
}
}
}

Checkbox becomes big when clicked on in QML tableview

This code does produce checkboxes in a tableview but when I click on the checkbox it becomes big. I want it to remain of a constant size.
Please guide.
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.1
Rectangle
{
id: rightside
anchors.fill: parent
height: parent.height
width: 1500
TableView
{
anchors.fill: parent
TableViewColumn
{
role: "selectall"
title: "Select All"
width: 100
delegate: CheckBox
{
anchors.fill: parent
checked: false
}
}
TableViewColumn {
role: "size"
title: "Size"
width: 100
}
TableViewColumn
{
role: "last_updated"
title: "Last Updated"
width: 100
delegate: Component
{
Rectangle
{
height: 100
width: 120
id: head
RowLayout
{
height: parent.height
width: parent.width
Rectangle
{
height: 20
width: 20
color: "red"
border.color: "black"
radius: 100
MouseArea
{
anchors.fill: parent
onClicked: parent.color = "grey"
}
}
}
}
}
}
model: ListModel
{
id: mymodel
ListElement { text: "Banana" }
ListElement { text: "Apple" }
ListElement { text: "Coconut" }
}
}
}
There are lots of way to solve your problem. But first, let's do proper distinction between Qt Qtuick Controls versions. To do it, use this import statement:
import QtQuick.Controls 1.4 as QC1
And respectively use all components that requires QC1, e.g.: QC1.TableView, QC1.TableViewColumn.
In your example you are getting overlapping of components. To avoid it in terms of QC1 you can define a higher row delegate for your TableView. But this discards the default style. Simple example of its usage with style goes here:
rowDelegate: Rectangle {
height: 30
SystemPalette {
id: myPalette
colorGroup: SystemPalette.Active
}
color: {
var baseColor = styleData.alternate ? myPalette.alternateBase : myPalette.base
return styleData.selected ? myPalette.highlight : baseColor
}
}
As result you'll get this:
Another option in terms of QC2 is to redefine indicator style of CheckBox. Below you'll find an example that could possibly fit your app, based on Customizing CheckBox documentation; so your CheckBox delegate will look like this:
delegate: CheckBox {
id: control
anchors.fill: parent
checked: false
indicator: Rectangle {
id: outer
readonly property int size: 18
implicitWidth: size
implicitHeight: size
x: control.leftPadding
y: parent.height / 2 - height / 2
radius: 4
border.color: control.down ? "orangered" : "orange"
Rectangle {
id: inner
anchors.centerIn: parent
width: outer.size/2
height: width
radius: 3
color: control.down ? "orangered" : "orange"
visible: control.checked
}
}
}
As result you'll get this:

QML ListView contentHeight property value is incorrect

I have a QML page with a GridLayout that contains the page title, ListView and close button:
GridLayout {
columns: 1
rows: 5
anchors.fill: parent
<page title item>....
ListView
{
id: list
spacing: 15
model: logModel
Layout.fillWidth: true
Layout.fillHeight: true
delegate: Item {
implicitWidth: parent.width
implicitHeight: grid.height
RowLayout
{
id: grid
spacing: 0
width: parent.width
height: commentLabel.implicitHeight
<icon>....
Label {
id: commentLabel
Layout.fillWidth: true
text: comment
wrapMode: Label.Wrap
}
}
}
ScrollIndicator.vertical: ScrollIndicator { }
}
<close button>...
}
Component.onCompleted:
{
console.log("list.contentHeight = ", list.contentHeight, "list.height = ", list.height)
}
when I bind ListView to a model (logModel) that contains items with comment property that contains relatively long multiline text and check contentHeight property of ListView from Component.onCompleted I get the value 161 that is obviously too small (actually it should be greater than 500, because ListView height property value is 461 and it is not enough for all the items).
So I cannot figure out what this 161 is. The only guess is that it is something close to the content height with single-line items (not multiline), but it is not clear what is the logic behind that.
My QT version is 5.13.2.
EDIT1:
The full source code:
ChangeLogPage.qml:
import QtQuick 2.7
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQuick.Controls.Styles 1.4
Page
{
id: root
property alias model: list.model
background: Rectangle {
color: "transparent"
}
function close()
{
stack.pop()
}
SystemPalette {
id: palette
}
GroupBox
{
id: box
background: Rectangle {
color: palette.base
}
anchors.margins: 15
anchors.fill: parent
GridLayout {
columns: 1
rows: 5
anchors.fill: parent
ListView
{
id: list
Layout.fillWidth: true
Layout.fillHeight: true
//It is not clear why, but there is an enough left margin.
Layout.leftMargin: 0
Layout.rightMargin: 0
Layout.topMargin: 15
Layout.bottomMargin: 20
//Looks like it is only vertical spacing.
spacing: 15
clip: true
delegate: Item {
width: parent.width
height: grid.height
RowLayout
{
id: grid
spacing: 0
width: parent.width
//height: commentLabel.height
Label {
//id: commentLabel
Layout.fillWidth: true
text: comment //qsTr(comment)
wrapMode: Label.Wrap
}
}
}
ScrollIndicator.vertical: ScrollIndicator { }
}
Button {
Layout.alignment: Qt.AlignRight
text: qsTr("Close")
onClicked: close()
}
}
}
Component.onCompleted:
{
console.log("list.contentHeight = ", list.contentHeight, "list.height = ", list.height)
}
}
main.qml:
import QtQuick 2.7
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
import LinesGame 1.0
ApplicationWindow {
id: window
visible: true
width: 360
height: 640
title: "Test"
Component {
id: listModelComponent
ListModel {
id: model
}
}
StackView
{
anchors.fill: parent
id: stack
}
function showChangeLogPage(beginIndex, endIndex)
{
if (beginIndex < endIndex)
{
//var component = Qt.createComponent("ChangeLogModel.qml")
//var logModel = component.createObject(this)
var logModel = listModelComponent.createObject(this);
for (var i = beginIndex; i < endIndex; i++)
{
//i.toString()
logModel.append({comment: qsTr("The advertising was added, but it is shown only when you start a new game or load a saved game. Support the developers, tap on the advertising!")})
}
stack.push(Qt.resolvedUrl("ChangeLogPage.qml"), {model: logModel})
}
}
Component.onCompleted:
{
showChangeLogPage(0, 5)
}
}

How to have the components comunicate between them in an accordion like Component? [duplicate]

I'm trying to create accordion qml control like this.
First I thought that I can use combobox and customize it but now I think it is impossible.
Is there any standerd control that I can use? If not can you help me with controls structure?
Just playing with QML
PanelItem.qml
import QtQuick 2.7
import QtQuick.Layouts 1.2
Item {
default property var contentItem: null
property string title: "panel"
id: root
Layout.fillWidth: true
height: 30
Layout.fillHeight: current
property bool current: false
ColumnLayout {
anchors.fill: parent
spacing: 0
Rectangle {
id: bar
Layout.fillWidth: true
height: 30
color: root.current ? "#81BEF7" : "#CEECF5"
Text {
anchors.fill: parent
anchors.margins: 10
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
text: root.title
}
Text {
anchors{
right: parent.right
top: parent.top
bottom: parent.bottom
margins: 10
}
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
text: "^"
rotation: root.current ? "180" : 0
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
root.current = !root.current;
if(root.parent.currentItem !== null)
root.parent.currentItem.current = false;
root.parent.currentItem = root;
}
}
}
Rectangle {
id: container
Layout.fillWidth: true
anchors.top: bar.bottom
implicitHeight: root.height - bar.height
clip: true
Behavior on implicitHeight {
PropertyAnimation { duration: 100 }
}
}
Component.onCompleted: {
if(root.contentItem !== null)
root.contentItem.parent = container;
}
}
}
usage:
import QtQuick 2.7
import QtQuick.Layouts 1.2
import QtQuick.Window 2.0
Window {
visible: true
width: 400
height: 400
ColumnLayout {
anchors.fill: parent
spacing: 1
property var currentItem: null
PanelItem {
title: "Panel 1"
Rectangle {
color: "orange"
anchors.fill: parent
}
}
PanelItem {
title: "Panel 2"
Rectangle {
color: "lightgreen"
anchors.fill: parent
}
}
PanelItem {
title: "Panel 3"
Rectangle {
color: "lightblue"
anchors.fill: parent
}
}
PanelItem {
title: "Panel 4"
Rectangle {
color: "yellow"
anchors.fill: parent
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}
What about using this open source component which I did here
Accordion component and used here Accordion component example.
You only need to initialize:
Components.Accordion {
id: acordion
anchors.fill: parent
anchors.margins: 10
}
And create the data dynamically like this:
propertyAcordion.model = [
{
'menuTitle': value,
'children': [
{
'menuTitle': value,
'children': [
...
Updating the MouseArea click part as below with some added condition. Thanks to folibis for this qml accordian menu.
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
root.current = !root.current;
if(root.parent.currentItem !== null) {
if(root.parent.currentItem !== root)
root.parent.currentItem.current = false;
}
root.parent.currentItem = root;
}
}

Choreography in qml

Is there possible to use choreography animations in qml (in a REUSABLE manner)?
for example in StackView transitions from page1 to page2.
I tried following code, but ParentChange does not work as expected. This code just changes red rectangle's position.
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
StackView {
id: stack
anchors.fill: parent
initialItem: Page {
id: page1
Label {
text: qsTr("First page")
anchors.centerIn: parent
}
Rectangle {
id: rect
width: 50
height: 50
x: 600
y: 100
color: "red"
states: [
State {
when: stack.depth > 1
ParentChange { target: rect; parent: stack.currentItem; x: 100; y: 100; }
}
]
transitions: Transition {
ParentAnimation {
NumberAnimation { properties: "x,y"; duration: 1000 }
}
}
}
MouseArea {
anchors.fill: parent
onClicked: stack.push(page2)
}
}
Component {
id: page2
Page {
background: Rectangle { color: "yellow"; anchors.fill: parent }
Label {
text: qsTr("Second page")
anchors.centerIn: parent
}
}
}
}
Rectangle {
id: back
color: "blue"
width: 50
height: 50
radius: 25
MouseArea {
anchors.fill: parent
onClicked: stack.pop()
}
}
}
but the usage is not limited to StackView. It can be used in many other situations. (just like above link)

Resources