I'm trying to create a reusable slider. I'm having trouble to set implicit sizes correctly so that the CustomSlider includes the Slider and the Label. I would like to have a implicit size specified, but let the user set a width for the slider itself.
I tried using childrenRect but that gives me a binding loop error.
How can I have the yellow background span across all the components: the slider and the green label?
Currently:
Would like:
CustomSlider.qml
Item {
id: root
property int startval: 0
property int endval: 20
property int sliderWidth: 200
// This results in binding loop
//implicitHeight: childrenRect.height
implicitHeight: control.height + label.height
implicitWidth: sliderWidth
Rectangle {
color: "yellow"
width: root.width
height: root.height
}
Slider {
id: control
stepSize: 1
anchors.centerIn: parent
snapMode: Slider.SnapOnRelease
width: root.sliderWidth
from: root.startval
to: root.endval
handle: Rectangle {
id: handleId
x: control.visualPosition * (control.width - width)
y: (control.height - height) / 2
width: 20
height: 20
radius: 20
color: "gray"
}
background: Rectangle {
y: (control.height - height) / 2
height: 4
radius: 2
color: "green"
Rectangle {
width: control.visualPosition * parent.width
height: parent.height
color: "red"
radius: 2
}
}
}
Label {
id: label
width: 20
height: 20
text: control.value
font.pixelSize: 15
color: "black"
x: handleId.x + control.x
y: handleId.y - 20
Rectangle {
color: "green"
anchors.fill: parent
opacity: 0.3
}
}
}
Main.qml
CustomSlider {
anchors.centerIn: parent
startval: 0
endval: 10
//sliderWidth: 100
}
I usually set the Width and height based on the parent.
I set sliderWidth: parent.width/2.0 and for your green label I add TextMetrics and calculate its width based on the text that it wants to show.
import QtQuick 2.15
import QtQuick.Controls 2.15
Item {
id: root
property int startval: 0
property int endval: 0
property int sliderWidth: parent.width/2.0
// This results in binding loop
//implicitHeight: childrenRect.height
implicitHeight: control.height + label.height
implicitWidth: sliderWidth
Rectangle {
color: "yellow"
width: root.width
height: root.height
}
Slider {
id: control
stepSize: 1
anchors.centerIn: parent
snapMode: Slider.SnapOnRelease
width: root.sliderWidth
from: root.startval
to: root.endval
handle: Rectangle {
id: handleId
x: control.visualPosition * (control.width - width)
y: (control.height - height) / 2
width: 20
height: 20
radius: 20
color: "gray"
}
background: Rectangle {
y: (control.height - height) / 2
height: 4
radius: 2
color: "green"
Rectangle {
width: control.visualPosition * parent.width
height: parent.height
color: "red"
radius: 2
}
}
}
Label {
id: label
width: t_metrics.tightBoundingRect.width +4
height: t_metrics.tightBoundingRect.height +7
text: control.value
font.pixelSize: 15
color: "black"
x: handleId.x + control.x
y: handleId.y - 20
Rectangle {
color: "green"
anchors.fill: parent
opacity: 0.3
}
}
TextMetrics {
id: t_metrics
text: control.value.toString()
}
}
Updated:
in CustomSlider.qml , changed it to this code:
import QtQuick 2.15
import QtQuick.Controls 2.15
Item {
id: root
property int startval: 0
property int endval: 0
property int sliderWidth: parent.width/2.0
implicitHeight: control.height + label.height
implicitWidth: sliderWidth
Rectangle {
color: "yellow"
width: root.width +10
height: root.height +control.height + label.height + t_metrics.tightBoundingRect.height +7
Slider {
id: control
stepSize: 1
anchors.centerIn: parent
snapMode: Slider.SnapOnRelease
width: root.sliderWidth
from: root.startval
to: root.endval
handle: Rectangle {
id: handleId
x: control.visualPosition * (control.width - width)
y: (control.height - height) / 2
width: 20
height: 20
radius: 20
color: "gray"
}
background: Rectangle {
y: (control.height - height) / 2
height: 4
radius: 2
color: "green"
Rectangle {
width: control.visualPosition * parent.width
height: parent.height
color: "red"
radius: 2
}
}
}
Label {
id: label
width: t_metrics.tightBoundingRect.width +4
height: t_metrics.tightBoundingRect.height +7
text: control.value
font.pixelSize: 15
color: "black"
x: handleId.x + control.x
y: handleId.y /2 + 4
Rectangle {
color: "green"
anchors.fill: parent
opacity: 0.3
}
}
TextMetrics {
id: t_metrics
text: control.value.toString()
}
}
}
This makes your text label shows inside the yellow rectangle and in main.qml I add one Row with labels and spinboxes for the test.
If you want that user to set sliderWidth or startval and endval and then show him Slider you need to create that object dynamically.
and if you want to be displayed in the Column and don't overlap you need ColumnLayout.
If you want to scroll them then you need to use ScrollView.
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Window {
width: 660
height: 480
visible: true
title: qsTr("Hello World")
ScrollView {
width: parent.width
height : parent.height
contentWidth: slidersColumn.width
contentHeight: slidersColumn.height
clip : true
ColumnLayout {
id: slidersColumn
anchors.fill: parent
spacing:50
}
}
function addSlider(sliderWidth,startval,endval) {
var obj = Qt.createComponent("CustomSlider.qml");
var slider = obj.createObject(slidersColumn);
slider.startval=startval;
slider.endval=endval;
slider.sliderWidth=sliderWidth;
}
Row
{
x: 0
y: 5
width: parent.width
height: 30
Label {
id: lbl_slider_width
text: qsTr(" Slider width ")
}
SpinBox {
id: spinBox_slider_width
editable: true
from:0
to:parent.width
}
Label {
id: lbl_startval
text: qsTr(" Startval ")
}
SpinBox {
id: spinBox_startval
editable: true
from:0
to:parent.width
}
Label {
id: lbl_endval
text: qsTr(" Endval ")
}
SpinBox {
id: spinBox_endval
editable: true
from:0
to:parent.width
}
Button {
id: button
text: qsTr(" Create Slider")
onClicked:
{
addSlider(spinBox_slider_width.value,spinBox_startval.value,spinBox_endval.value)
}
}
}
}
The result is:
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:
I have a StackLayout with 3 stack items (2 static and one dynamically generated) which switches on button click. I want to make the second and third stack items a Droparea (third one is dynamically created) where I want to drag items from a Listview which is outside the StackLayout. Is it possible or I am doing something wrong?
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page{
id: pageid
width: parent.width
height: parent.height
Row{
id: row1
Button{
text: "0"
onClicked: layout.currentIndex = 0
}
Button{
text: "1"
onClicked: layout.currentIndex = 1
}
Button{
text: "2"
onClicked:{
var str = 'import QtQuick 2.15; Rectangle {id: rect2; color: "red"; DropArea{anchors.fill: parent; onEntered: {rect2.color = "silver"}}}'
var comp = Qt.createQmlObject(str,layout,"dynamicSnippet1")
onClicked: layout.currentIndex = 2
}
}
}
// Stacklayout block
StackLayout {
id: layout
anchors.top: row1.bottom
height: parent.height - row1.height - dragger.height
width: parent.width
currentIndex: 0
// Component 0
Rectangle {
id: rect0
color: 'teal'
}
// Component 1
Rectangle {
id:rect1
color: 'plum'
DropArea{
anchors.fill: parent
onEntered: {rect1.color = "gold"}
}
}
}
// Drag rectangles
ListView{
id: dragger
anchors.top: layout.bottom
height: 30
width: parent.width
orientation: Qt.Horizontal
model: 3
delegate: Rectangle{
id: xrect
height: 30
width: 60
color:"grey"
border.width: 1
border.color: "orange"
MouseArea{
id: ma
anchors.fill: parent
onReleased: parent.Drag.drop()
drag.target: parent
}
}
}
}
You forgot to set property Drag.active for your draggable target, e.g. put statement
Drag.active: ma.drag.active
into your xrect to make it work.
In addition you could check more dragging signals in your DropArea:
onDropped: console.error("# dropped")
onContainsDragChanged: console.error("# containsDrag", containsDrag)
I have a file named page2.qml like below
Page {
id: page
Rectangle {
id: container
anchors.fill: parent
width: parent.width * 0.8
Rectangle {
id: title
anchors.top: parent.top
width: parent.width
height: 50
color: "salmon"
}
ListView {
id: listView
currentIndex: -1
anchors.top: title.bottom
anchors.bottom: parent.bottom
delegate: Rectangle {
height: 20
width: 100
border.color: "red"
color: "pink"
Text {
text: model.index
}
}
model: 100
}
}
}
the result is in this image:
Since the listview contain 100 item, how can i make the whole page scrollable? i can make just listview scrollable but not the whole page.
If you don't need the ListView to be scrollable by itself but your whole container need to, you could use a Repeater instead and put it inside a Column wrapped in a Flickable :
Flickable {
id: container
contentHeight: column.implicitHeight
contentWidth: width
width: parent.width * 0.8
height: parent.height
Column {
id: column
width: parent.width
Rectangle {
id: title
width: parent.width
height: 50
color: "salmon"
}
Repeater {
id: listView
model: 100
delegate: Rectangle {
height: 20
width: 100
border.color: "red"
color: "pink"
Text {
text: model.index
}
}
}
}
}
I would like to have centered items in QML ListView and therefore I've added following code of my ListView:
import QtQuick 2.0
import QtMultimedia 5.5
import QtQuick.Controls 1.3
import QtQuick.Extras 1.4
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
import QtTest 1.1
Rectangle {
id: ueKeypad
width: ueMainColumnLayout.implicitWidth+2*radius
height: ueMainColumnLayout.implicitHeight+2*radius
color: "grey"
radius: 8
border.color: "#99c6f0"
border.width: 4
ColumnLayout {
id: ueMainColumnLayout
anchors.fill: parent
anchors.margins: radius
spacing: 4
RowLayout {
id: ueTextLayout
Text {
id: ueStaffLoginText
text: qsTr("Staff Login")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.family: "Padauk"
textFormat: Text.RichText
font.pointSize: 16
font.bold: true
color: ueKeypad.border.color
Layout.fillWidth: true
} // ueStaffLoginText
} // ueTextLayout
RowLayout {
id: uePeopleViewLayout
ListView {
id: uePeopleView
keyNavigationWraps: true
spacing: 4
antialiasing: true
model: uePeopleModel
Layout.fillWidth: true
Layout.fillHeight: false
//Layout.minimumWidth: 64
Layout.minimumHeight: 64
//Layout.preferredWidth: 96
Layout.preferredHeight: 96
//Layout.maximumWidth: 128
Layout.maximumHeight: 128
orientation: ListView.Horizontal
layoutDirection: Qt.LeftToRight
snapMode: ListView.SnapToItem
highlightRangeMode: ListView.ApplyRange
Component.onCompleted: {
var newIndex=(count%2==0)?(count/2):(Math.round(count/2));
positionViewAtIndex(newIndex, ListView.Center);
currentIndex=newIndex;
print(newIndex)
} // onCompleted - center items
delegate: Rectangle {
id: uePersonDelegate
width: 32
height: 32
ColumnLayout {
id: uePersonDelegateMainLayout
anchors.fill: parent
anchors.margins: radius
RowLayout {
id: uePersonDelegateImageLayout
Image {
id: uePersonImage
antialiasing: true
fillMode: Image.PreserveAspectFit
source: "image://uePeopleModel/"+model.ueRoleImage
} // uePersonImage
} // uePersonDelegateImageLayout
RowLayout {
id: uePersonDelegateNameLayout
Text {
id: ueTextPersonName
color: "#ffffff"
text: model.ueRoleName
font.bold: true
font.pixelSize: 16
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
} // ueTextPersonName
} // uePersonDelegateNameLayout
} // uePersonDelegateMainLayout
} // uePersonDelegate
add: Transition {
NumberAnimation {
property: "opacity";
from: 0;
to: 1.0;
duration: 100
} // NumberAnimation
NumberAnimation {
property: "scale";
from: 0;
to: 1.0;
duration: 100
} // NumberAnimation
} // Transition
displaced: Transition {
NumberAnimation {
properties: "x,y";
duration: 100;
easing.type: Easing.OutBounce
} // NumberAnimation
} // Transition
} // uePeopleView
} // uePeopleViewLayout
RowLayout {
id: ueTumblerLayout
Tumbler {
id: ueLoginKeypadTumbler
Layout.fillWidth: true
Layout.fillHeight: false
height: 100
antialiasing: true
TumblerColumn {
id: ueNumericTumblerColumnDigit1000
model: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
} // ueNumericTumblerColumnDigit1000
TumblerColumn {
id: ueNumericTumblerColumnDigit100
model: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
} // ueNumericTumblerColumnDigit100
TumblerColumn {
id: ueNumericTumblerColumnDigit10
model: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
} // ueNumericTumblerColumnDigit10
TumblerColumn {
id: ueNumericTumblerColumnDigit1
model: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
} // ueNumericTumblerColumnDigit1
} // ueLoginKeypadTumbler
} // ueTumblerLayout
RowLayout {
id: ueButtonsLayout
Button {
id: ueButtonLogin
Layout.fillWidth: true
text: qsTr("Login")
} // ueButtonLogin
Button {
id: ueButtonClear
Layout.fillWidth: true
text: qsTr("Clear")
} // ueButtonClear
Button {
id: ueButtonQuitApp
Layout.fillWidth: true
text: qsTr("Quit")
} // ueButtonQuitApp
} // ueButtonsLayout
} // ueMainColumnLayout
states: [
State {
name: "ueStateLoginOk"
PropertyChanges {
target: ueKeypad
border.color: "#00ff00"
}
PropertyChanges {
target: ueLoginText
color: "#00ff00"
}
}, // ueStateLoginOk
State {
name: "ueStateLoginOkFailed"
PropertyChanges {
target: ueKeypad
border.color: "#ff0000"
}
PropertyChanges {
target: ueLoginText
color: "#ff0000"
}
} // ueStateLoginOkFailed
] // states
} // ueKeypad
Now, print(newIndex) statement prints out correct value 3 (in my case, since at the moment I have 5 items), and I would like the 3rd item to be in the center of ListView and other two items on the left side and the right side. Is this possible? And out of scope of this question, why Transitions also does not work, taken from example?
I've also set highlightRangeMode: ListView.ApplyRange taken from comment hint .
Here is a screenshot of the problem:
The problem here is that you are trying to place ListView delegates according to your needs. That's plain wrong since the ListView (much like all the other views) is meant to do that for you, according to the ListView size. The ListView will always use all the available space, resulting in the delegates all positioned to the left, as you experienced.
Instead of forcing the behaviour on the delegates, you should constrain the ListView size and position it to be in the center. Then, you can exploit highlightRangeMode with preferredHighlightBegin and preferredHighlightEnd from this answer to ensure the currently selected Item is in the center of the list. Also, by using StrictlyEnforceRange, you can force the selected Item to stay always in the center, which would result in an IMO better way to select the desired Item.
Here is an example implementing this approach. Sizes are hard-coded for simplicity but can be easily parametrised. As said, I went a little bit further by setting the highlighting policy. Hope it helps.
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
Window {
id: win
width: 300
height: 300
visible: true
ColumnLayout {
anchors.fill: parent
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "blue"
}
ListView {
Layout.alignment: Qt.AlignCenter
Layout.minimumWidth: 30 * 5 + 40
Layout.preferredHeight: 50
clip: true
spacing: 15
model: 10
orientation: ListView.Horizontal
delegate: Item {
width: 30
height: 50
Rectangle{
anchors.centerIn: parent
color: parent.ListView.isCurrentItem ? "red" : "steelblue"
width: 30
height: 30
Text {
text: index
anchors.centerIn: parent
}
scale: parent.ListView.isCurrentItem ? 1.5 : 1
Behavior on scale { NumberAnimation { duration: 200 } }
}
}
preferredHighlightBegin: width / 2 - 15
preferredHighlightEnd: width / 2 + 15
highlightRangeMode: ListView.StrictlyEnforceRange
Component.onCompleted: currentIndex = count / 2
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "yellow"
}
}
}