Columns of grouped checked boxes overlapping with one another - qml

So, I'm trying to create a filter box for an ecommerce-based application that I'm working on and I want to make multiple button groups of check boxes within columns. I tried to separate the different columns but they overlap with one another. For example, the priceColumn overlaps with the conditionColumn. I am looking for a way to separate each button group by line. Is there anyway around this or any other solution that you would suggest? I'm new to QML by the way.
Here's my code for example:
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import "." as NeroshopComponents
Pane {
id: filterBox
clip: true // The area in which the contents of the filterBox will be bounded to (set width and height) // If clip is false then the contents will go beyond/outside of the filterBox's bounds
width: 250; height: 540
background: Rectangle {
color: (NeroshopComponents.Style.darkTheme) ? "#2e2e2e"/*"#121212"*/ : "#a0a0a0"
radius: 3
}
// conditionGroup
ButtonGroup {
id: conditionButtonGroup
buttons: conditionColumn.children
exclusive: false // more than one button in the group can be checked at any given time
checkState: conditionParentBox.checkState
onClicked: {
console.log("Selected condition:", button.text)
if(checkState == Qt.Unchecked) {
console.log("checkState: NO button is checked")
}
if(checkState == Qt.Checked) {
console.log("checkState: All buttons are checked")
}
if(checkState == Qt.PartiallyChecked) {
console.log("checkState: One or more buttons are checked")
}
}
}
// priceGroup
ButtonGroup {
id: priceButtonGroup
buttons: priceColumn.children
exclusive: false // more than one button in the group can be checked at any given time
checkState: priceParentBox.checkState
onClicked: {
console.log("Selected price range:", button.text)
if(checkState == Qt.Unchecked) {
console.log("checkState: NO button is checked")
}
if(checkState == Qt.Checked) {
console.log("checkState: All buttons are checked")
}
if(checkState == Qt.PartiallyChecked) {
console.log("checkState: One or more buttons are checked")
}
}
}
////////////////////
Rectangle {
id: filterOptions
//anchors.fill: parent
x: -horizontalScrollBar.position * width
y: -verticalScrollBar.position * height
color: "transparent"
width: parent.width
height: parent.height
Column {//ColumnLayout {
id: conditionColumn
Text {
text: qsTr("Condition");
font.bold: true
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
}
CheckBox {
id: conditionParentBox
//text: qsTr("Any")//qsTr("Parent")
checkState: conditionButtonGroup.checkState
Text {
text: qsTr("Any")
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
anchors.left: parent.right
anchors.leftMargin: 0
anchors.top: parent.top
anchors.topMargin: (parent.height - this.contentHeight) / 2
}
}
CheckBox {
//checked: false
//text: qsTr("New")
leftPadding: indicator.width
ButtonGroup.group: conditionButtonGroup
Text {
text: qsTr("New")
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
anchors.left: parent.right
anchors.leftMargin: 0
anchors.top: parent.top
anchors.topMargin: (parent.height - this.contentHeight) / 2
}
}
CheckBox {
//text: qsTr("Used")
leftPadding: indicator.width
ButtonGroup.group: conditionButtonGroup
Text {
text: qsTr("Used")
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
anchors.left: parent.right
anchors.leftMargin: 0
anchors.top: parent.top
anchors.topMargin: (parent.height - this.contentHeight) / 2
}
}
CheckBox {
//text: qsTr("Refurbished/Renewed")
leftPadding: indicator.width
ButtonGroup.group: conditionButtonGroup
Text {
text: qsTr("Refurbished/Renewed")
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
anchors.left: parent.right
anchors.leftMargin: 0
anchors.top: parent.top
anchors.topMargin: (parent.height - this.contentHeight) / 2
}
}
}
////////////////////////
Column {//ColumnLayout {
id: priceColumn
Text {
text: qsTr("Price")
font.bold: true
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
}
CheckBox {
id: priceParentBox
checkState: priceButtonGroup.checkState
Text {
text: qsTr("Any")
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
anchors.left: parent.right
anchors.leftMargin: 0
anchors.top: parent.top
anchors.topMargin: (parent.height - this.contentHeight) / 2
}
}
CheckBox {
leftPadding: indicator.width
ButtonGroup.group: priceButtonGroup
Text {
text: qsTr("$0.00-$1.00")
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
anchors.left: parent.right
anchors.leftMargin: 0
anchors.top: parent.top
anchors.topMargin: (parent.height - this.contentHeight) / 2
}
}
}
} // Rectangle: filterOptions
ScrollBar {
id: verticalScrollBar
hoverEnabled: true
active: hovered || pressed
orientation: Qt.Vertical
size: (filterBox.height - 20) / filterOptions.height//filterBox.background.height / conditionColumn.height // 20 is the bottomMargin I guess?
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom
policy: ScrollBar.AlwaysOn
}
ScrollBar {
id: horizontalScrollBar
hoverEnabled: true
active: hovered || pressed
orientation: Qt.Horizontal
size: filterBox.background.width / filterOptions.width
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
policy: ScrollBar.AsNeeded
}
// todo: sort by category, price, ratings, brand, color, etc.
}

I replaced your anchoring with ColumnLayout. I replaced Rectangle for Frame has an automatic border and padding effect. I replaced CheckBox for RadioButton, since, for exclusivity per group since your UI implied that use case. Qt QML automatically figures out how to deal with the two RadioButton groups since they fall in their own Frame/ColumnLayout so we needn't explicitly define a ButtonGroup. I replaced your ScrollBar implementation with ScrollBar.vertical
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page {
anchors.fill: parent
Flickable {
id: flickable
anchors.fill: parent
contentWidth: columnLayout.width
contentHeight: columnLayout.height
clip: true
ColumnLayout {
id: columnLayout
width: flickable.width - 20
Frame {
Layout.fillWidth: true
ColumnLayout {
width: parent.width
Label { text: qsTr("Condition") }
RadioButton { id: cond1; text: qsTr("Any") }
RadioButton { id: cond2; text: qsTr("New") }
RadioButton { id: cond3; text: qsTr("Used") }
RadioButton { id: cond4; text: qsTr("Refurbished/Renew") }
Text {
text: (cond1.checked && cond1.text)
|| (cond2.checked && cond2.text)
|| (cond3.checked && cond3.text)
|| (cond4.checked && cond4.text)
|| qsTr("Nothing Selected")
}
}
}
Frame {
Layout.fillWidth: true
ColumnLayout {
width: parent.width
Label { text: qsTr("Price") }
RadioButton { id: price1; text: qsTr("Any") }
RadioButton { id: price2; text: qsTr("$0.00-$1.00") }
Text {
text: (price1.checked && price1.text)
|| (price2.checked && price2.text)
|| qsTr("Nothing Selected")
}
}
}
}
ScrollBar.vertical: ScrollBar {
width: 20
policy: ScrollBar.AlwaysOn
}
}
}

Related

Create a submenu like kicker in Qml

I am on a kde plasmoid for my own usage.It is an applicaton menu like kicker and I am trying to make to open a submenu when I click on an item in main menu like in the picture :
But all I can manage is this:
How can I open the submenu outside the main item of the plasmoid.Take into account that the submenu is a ListView as also is the main menu.
This is the "calling" of the menu:
ListDelegate {
id: recentitemsItem
text: i18n("Recent Items")
highlight: delegateHighlight
PlasmaComponents.Label {
id: submenuArrow
text: "⏵"
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
onClicked: {
subMenu.visible = !subMenu.visible
}
}
This is the menu:
Item {
id: subMenu
visible : false
width: units.gridUnit * 14
height: units.gridUnit * 43
x : units.gridUnit * 16
y : aboutComputerItem.height + separatorItem.height + systemsettingsItem.height + appStoreItem.height + separatorItem1.height + recentitemsItem.height
PlasmaComponents.Label {
id: applications
enabled: false
text: "Applications"
}
ListView {
id: row
anchors.top: applications.bottom
width: parent.width
height: parent.height
model: Kicker.RecentUsageModel {
favoritesModel: globalFavorites
}
delegate: ListDelegate {
height: 24
width: parent.width
highlight: delegateHighlight
onClicked: if(model.url == undefined){
executable.exec("gtk-launch " + model.favoriteId);
}
else {executable.exec("xdg-open '" + model.url + "'");
}
PlasmaCore.IconItem {
id: icon
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 10
width: 16
height: width
visible: true
source: model.decoration
}
PlasmaComponents.Label {
id: label
enabled: true
anchors.verticalCenter: icon.verticalCenter
anchors.left : icon.right
anchors.leftMargin: 10
width: parent.width
verticalAlignment: Text.AlignVCenter
textFormat: Text.PlainText
wrapMode: Text.NoWrap
elide: Text.ElideRight
text: model.display
}
}
}
}
One answer to this problem is to use PlasmaCore.ToolTipArea{} like this:
import org.kde.plasma.core 2.0 as PlasmaCore
ListDelegate {
id: recentitemsItem
text: i18n("Recent Items")
highlight: delegateHighlight
PlasmaCore.ToolTipArea {
id: toolTip
anchors.fill: parent
active: true
interactive : true
timeout : -1
location: PlasmaCore.Types.LeftEdge
mainItem: toolTipDelegate
}
onClicked: {
toolTip.showToolTip()
}
}
And setting as mainItem: toolTipDelegate the code below:
Item {
id: toolTipDelegate
width: units.gridUnit * 16
height: units.gridUnit * 40
visible: false
ListView {
id: row
width: parent.width
height: parent.height
model: Kicker.RecentUsageModel {
favoritesModel: globalFavorites
}
delegate: ListDelegate {
height: 24
width: parent.width
highlight: delegateHighlight
onClicked: if(model.url == undefined){
executable.exec("gtk-launch '" + model.favoriteId + "'");
}
else {executable.exec("xdg-open '" + model.url + "'");
}
PlasmaCore.IconItem {
id: icon
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 10
width: 16
height: width
visible: true
source: model.decoration
}
PlasmaComponents.Label {
id: label
enabled: true
anchors.verticalCenter: icon.verticalCenter
anchors.left : icon.right
anchors.leftMargin: 10
width: parent.width
verticalAlignment: Text.AlignVCenter
textFormat: Text.PlainText
wrapMode: Text.NoWrap
elide: Text.ElideRight
text: model.display
}
}
}
}
Then any time mouse hovers over Recent Items item or being clicked opens a tooltip window with the listview (the menu I want).

GridLayout Arrangement

Following is my main.qml:
Window {
id: window
visible: true
width: 800
height: 480
title: qsTr("Hello World")
ListModel {
id: _listModel
ListElement {
textData: "E1"
isEnabled: false
}
ListElement {
textData: "E2"
isEnabled: false
}
ListElement {
textData: "E3"
isEnabled: false
}
ListElement {
textData: "E4"
isEnabled: false
}
ListElement {
textData: "E5"
isEnabled: false
}
ListElement {
textData: "E6"
isEnabled: false
}
}
ListView {
id: _listview
model: _listModel
width: 100
height: parent.height
anchors.right: parent.right
delegate: Item {
width: parent.width
height: 50
anchors.right: parent.right
Component.onCompleted:
{
if (isEnabled)
visibleRecs++;
}
RowLayout {
Text {
id: itemText
text: qsTr(textData)
}
CheckBox {
height: 30
width: height
checked: isEnabled
onCheckedChanged: {
isEnabled = checked
}
}
}
}
}
ScrollView {
id: _scrollView
width: parent.width / 2
height: parent.height
clip: true
GridLayout {
id: _gridLayout
anchors.fill: parent
anchors.horizontalCenter: parent.horizontalCenter
columnSpacing: 10
rowSpacing: 10
columns: 2
Repeater {
model: _listModel
Loader {
id: _loader
sourceComponent: isEnabled ? _recComponent : null
onLoaded: {
item.text = textData
}
}
}
}
}
Component {
id: _recComponent
Rectangle {
property alias text : _txt.text
id: _rec
width: 100
height: 50
radius: 5
color: "yellow"
Text {
id: _txt
anchors.centerIn: parent
}
}
}
}
The above code creates following (when all check boxes are ticked):
When all checkboxes are ticked:
When checkbox E3 is unchecked:
I want the items to be rearranged in the gridlayout if any of the item goes invisible.
E.g. in the above case when E3 is unchecked, I want my view to be something like this:
Please let me know, if it is possible to achieve. Thanks in advance.
The problem is that you still instantiate the Loader, you just set the sourceComponent to null. You have to make the item invisible to not use space in the GridLayout (or put width/height to 0)
ScrollView {
id: _scrollView
width: parent.width / 2
height: parent.height
clip: true
GridLayout {
id: _gridLayout
anchors.fill: parent
anchors.horizontalCenter: parent.horizontalCenter
columnSpacing: 10
rowSpacing: 10
columns: 2
Repeater {
model: _listModel
Loader {
id: _loader
visible: isEnabled
sourceComponent: isEnabled ? _recComponent : null
onLoaded: {
item.text = textData
}
}
}
}
}

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))
}

Custom Button with conditional image and conditional text

I have the following custom QML Button:
import QtQuick.Controls 1.4
import QtQuick 2.5
import QtQuick.Layouts 1.2
import QtMultimedia 5.5
Rectangle {
id: ueButton
property string ueText: ""
property string ueIconPath: ""
width: 256
height: 128
signal ueSignalButtonClicked
gradient: Gradient {
GradientStop {
position: 0
color: "#ffffff"
SequentialAnimation on color {
id: ueButtonClickAnimation
loops: 1
running: false
ColorAnimation {
from: "#ffffff"
to: "#000000"
duration: 25
} // ColourAnimation
ColorAnimation {
from: "#000000"
to: "#ffffff"
duration: 25
} // ColorAnimation
} // SequentialAnimation
} // GradientStop
GradientStop {
position: 0.418
color: "#000000"
} // GradientStop
} // Gradient
border.color: "steelblue"
border.width: 1
radius: 4
antialiasing: true
smooth: true
ColumnLayout {
anchors.fill: parent
antialiasing: true
Image {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignTop
Layout.margins: 8
asynchronous: true
horizontalAlignment: Image.AlignHCenter
verticalAlignment: Image.AlignVCenter
fillMode: Image.PreserveAspectFit
smooth: true
source: ueIconPath
} // Image
Text {
text: ueText
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignVCenter
Layout.topMargin: 8
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.bottomMargin: 16
color: "#ffffff"
font.bold: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pointSize: 16
textFormat: Text.RichText
} // Text
} // ColumnLayout
MouseArea {
id: ueButtonMouseArea
antialiasing: true
anchors.fill: parent
onClicked: {
ueButtonClickAnimation.running=true
ueSignalButtonClicked()
}
} // MouseArea
} // ueButton
This works fine, but what I want to do now is to solve the following two specular issues:
If urtext is empty the corresponding text is ignored and whole ColumnLayout place is reserved for Image
If ueIconPath is empty, the corresponding image is ingnored and the whole ColumnLayout is reserved for Text
How can I achieve that?
As #BaCaRoZzo already suggested, using styles is preferable solution in this case since the item itself is still Button and so you can use all its properties and signals, including text property to pass your ueText text.
As for hiding elements inside, you can use visible property as suggested below:
UeButton.qml
Button {
id: ueButton
style: ButtonStyle {
background: Rectangle {
color: control.pressed ? "#CCC" : "#DEDEDE"
border.width: 1
border.color: "#999"
radius: 3
}
label: ColumnLayout {
id: layout
spacing: 10
Image {
Layout.alignment: Qt.AlignHCenter
source: control.iconSource
visible: control.iconSource !== ""
}
Text {
text: control.text
visible: control.text !== ""
}
}
}
}
Now using of this component is similar to regular Button:
UeButton {
anchors.centerIn: parent
text: "Some text"
iconSource: "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
}
Commenting out text or iconSource will change the button view

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.