I have a simple list view. I need the user to be able to manipulate the font size (visual impairment issues). QML quite happily calculates new width and height for the listView items but since the strings are different lengths that leads to listView that looks like badly stacked boxes. What I need is for it to look like a rectangle the width of the longest string and wrap if it reaches the edge of the window. I figure I make the listView item backgrounds transparent and calculate the width of the rectangle the listView is in to fit the updated font size. I have tried a few ways to do this and have not managed to make it work.
Any clues? code below (data coming from c++)
import QtQuick 2.0
Rectangle
{
id: theMenu
property double fontSize: menuManager.menuFontPointSize
property double menuWidth: menuManager.menuItemHeight
Component
{
id: menuEntryDelegate
Rectangle
{
id: menuItemContainer
width: menuEntry.width
height: menuEntry.height * 1.25
anchors.top: prompts.bottom
property double fontSize: theMenu.fontSize
state: ListView.isCurrentItem ? "selected" : "notselected"
Text
{
id: menuEntry
font.pointSize: fontSize
//width: parent.width
wrapMode: Text.WordWrap
text: displayText
clip: true
}
MouseArea
{
hoverEnabled: false
anchors.fill: parent
onClicked: menuHolder.currentIndex = index
onDoubleClicked: menuManager.displayMenu(menuHolder.currentIndex)
}
states:
[
State
{
name: "selected"
PropertyChanges
{
target: menuItemContainer
color: "#FAFCD9"
}
PropertyChanges
{
target: icon
source: iconUrl
}
PropertyChanges
{
target: prompts
text: getPrompt()
}
PropertyChanges
{
target: menuEntry
color: "black"
}
},
State
{
name: "notselected"
PropertyChanges
{
target: menuItemContainer
color: "black"
}
PropertyChanges
{
target: menuEntry
color: "white"
}
},
State
{
name: "hidden"
PropertyChanges
{
target: menuItemContainer
color: "green"
}
}
]
}
}
Rectangle
{
id: menuContainer
width: menuManager.menuWidth
height: (50 * 9) //TBD
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: prompts.bottom
color: "purple"
ListView
{
id: menuHolder
model: menuModel
anchors.fill: parent
opacity: 1
/* header: Rectangle
{
TextBox {}
}*/
header: Rectangle
{
width: menuHolder.width
height: 50
color: "#2A51A3"
Text
{
id: header
anchors.centerIn: parent
text: "FIX" + currentMenu.displayText
font.pointSize: currentMenu.fontPointSize
color: "green"
width: parent.width
wrapMode: Text.WordWrap
}
}
delegate: menuEntryDelegate
focus: true
add: Transition
{
NumberAnimation { properties: "x,y" }
}
Keys.onPressed:
{
if(event.key === Qt.Key_F1)
{
theMenu.z = -1
}
else if(event.key === Qt.Key_F3)
{
theMenu.z = 1
}
else if(event.key === Qt.Key_F2)
{
menuManager.menuFontPointSize *= menuManager.scale
theMenu.fontSize = menuManager.menuFontPointSize
}
else if(event.key === Qt.Key_F10)
{
scaleFactor -= 0.1
menuContainer.scale = scaleFactor
promptsContainer.scale = scaleFactor
//promptsContainer.z = 1
}
else if(event.key === Qt.Key_Right)//zoom in
{
menuContainer.x +=10
}
else if(event.key === Qt.Key_Left)//zoom out
{
menuContainer.x -=10
}
else if(event.key === Qt.Key_Home)//go back to Main menu
{
menuManager.displayMainMenu();
theMenu.fontSize = menuManager.menuFontPointSize
}
//Ways to select a menu item
else if((event.key >= Qt.Key_1 && event.key <= Qt.Key_9)
|| event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
{
if(event.key >= Qt.Key_1 && event.key <= Qt.Key_9)
{
menuHolder.currentIndex = event.key - Qt.Key_1;
}
menuManager.displayMenu(menuHolder.currentIndex);
theMenu.fontSize = menuManager.menuFontPointSize
}
menuEntryDelegate.updateIcon()
}
}
}
}#
Just use :
width: parent.width;
In menuItemContainer Rectangle element in your delegate component, to fill the ListView width.
And then add :
anchors { left: parent.left; right: parent.right }
To menuEntry Text element to give it a max size, so it can know when it must wrap (it will extend to the right infinitely else).
Related
How can I return resize logic of borders in Frameless Window?
The frame windows has this logic:
Code in QML:
import QtQuick
import QtQuick.Controls 2.5
import Qt5Compat.GraphicalEffects
import NR 1.0
Window {
id: mainWindow
width: 640
height: 720
visible: true
title: qsTr("Hello World")
flags: Qt.Window | Qt.FramelessWindowHint
color: "transparent"
// (1)
MouseArea {
id: bottomArea
height: 5
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
cursorShape: Qt.SizeVerCursor
onPressed: {
previousY = mouseY
}
onMouseYChanged: {
var dy = mouseY - previousY
mainWindow.setHeight(mainWindow.height + dy)
}
}
// Some code of another Items here
}
I tried this code for left side:
MouseArea {
id: leftSideMouseArea
anchors.fill: parent
property point lastMousePos: Qt.point(0, 0)
onPressed: { lastMousePos = Qt.point(mouseX, mouseY); }
onMouseXChanged: mainWindow.width += (mouseX + lastMousePos.x)
}
I put this code in (1) place, but it doesn't work - on click (without move) windows resize to the rigth and app crashes with error:
QQuickPaintedItem::textureProvider: can only be queried on the
rendering thread of an exposed window
This looks like on picture:
Can you help me?
Thanks!
Since Qt5.15, we have startSystemResize, which performs a native resizing and is recommended against using methods like comparing the click position to the current position.
The function is very simple; once you pass an edge, the window begins to resize.
An example of a frameless window is shown below:
CustomWindow.QML
Change the offset from the window's edges where the mouse can be pressed by using this property.
property int edgeOffest: 5
Also for moving the window as well You can use a DragHandler, which, when activated, calls startSystemMove.
Window {
width: 200; height: 100
color: '#fab'
flags: Qt.Window | Qt.FramelessWindowHint
DragHandler {
onActiveChanged: if(active) startSystemMove();
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton
property int edges: 0;
property int edgeOffest: 5;
function setEdges(x, y) {
edges = 0;
if(x < edgeOffest) edges |= Qt.LeftEdge;
if(x > (width - edgeOffest)) edges |= Qt.RightEdge;
if(y < edgeOffest) edges |= Qt.TopEdge;
if(y > (height - edgeOffest)) edges |= Qt.BottomEdge;
}
cursorShape: {
return !containsMouse ? Qt.ArrowCursor:
edges == 3 || edges == 12 ? Qt.SizeFDiagCursor :
edges == 5 || edges == 10 ? Qt.SizeBDiagCursor :
edges & 9 ? Qt.SizeVerCursor :
edges & 6 ? Qt.SizeHorCursor : Qt.ArrowCursor;
}
onPositionChanged: setEdges(mouseX, mouseY);
onPressed: {
setEdges(mouseX, mouseY);
if(edges && containsMouse) {
startSystemResize(edges);
}
}
}
}
Preview
Final Notes
Still, I do not recommend developing a custom window with custom functionality, which forces you to handle a lot of functions while still not feeling like a native one.
However, there are a few github projects that offered some helper libraries for this, so take a look at those.
https://github.com/antonypro/QGoodWindow
https://github.com/wangwenx190/framelesshelper
I can't think of better way to do this
Here is a working example:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: window
width: 640
height: 480
visible: true
title: qsTr("Hello World")
flags: Qt.Window | Qt.FramelessWindowHint
Rectangle
{
id: dragItemRight
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 5
color: "red"
x: window.width - width
onXChanged:
{
if (dragItemRightMouse.drag.active)
{
window.width = dragItemRight.x + width
}
}
MouseArea
{
id: dragItemRightMouse
anchors.fill: parent
drag.target: parent
drag.axis: Drag.XAxis
cursorShape: Qt.SizeHorCursor
drag.minimumX: 300
drag.onActiveChanged:
{
if (!drag.active)
{
dragItemRight.x = Qt.binding(function() { return window.width - width })
}
}
}
}
Rectangle
{
id: dragItemBottom
anchors.left: parent.left
anchors.right: parent.right
height: 5
color: "red"
y: window.height - height
onYChanged:
{
if (dragItemBottomMouse.drag.active)
{
window.height = dragItemBottom.y + height
}
}
MouseArea
{
id: dragItemBottomMouse
anchors.fill: parent
drag.target: parent
drag.axis: Drag.YAxis
cursorShape: Qt.SizeVerCursor
drag.minimumY: 300
drag.onActiveChanged:
{
if (!drag.active)
{
dragItemBottom.y = Qt.binding(function() { return window.height - height })
}
}
}
}
Rectangle
{
id: dragItemBottomRight
width: 5
height: 5
color: "green"
x: window.width - width
y: window.height - height
onYChanged:
{
if (dragItemBottomRightMouse.drag.active)
{
window.height = dragItemBottomRight.y + height
}
}
onXChanged:
{
if (dragItemBottomRightMouse.drag.active)
{
window.width = dragItemBottomRight.x + width
}
}
MouseArea
{
id: dragItemBottomRightMouse
anchors.fill: parent
drag.target: parent
drag.axis: Drag.XAndYAxis
drag.minimumX: 300
drag.minimumY: 300
cursorShape: Qt.SizeFDiagCursor
drag.onActiveChanged:
{
if (!drag.active)
{
dragItemBottomRight.x = Qt.binding(function() { return window.width - width })
dragItemBottomRight.y = Qt.binding(function() { return window.height - height })
}
}
}
}
}
I found the solution:
import QtQuick
import QtQuick.Controls 2.15
import Qt5Compat.GraphicalEffects
import NR 1.0
Window {
id: mainWindow
width: 640
height: 720
visible: true
title: qsTr("Hello World")
flags: Qt.Window | Qt.FramelessWindowHint
color: "transparent"
property point startMousePos
property point startWindowPos
property size startWindowSize
function absoluteMousePos(mouseArea) {
var windowAbs = mouseArea.mapToItem(null, mouseArea.mouseX, mouseArea.mouseY)
return Qt.point(windowAbs.x + mainWindow.x,
windowAbs.y + mainWindow.y)
}
MouseArea {
id: moveArea
anchors.fill: title
property point mPos;
onPressed: {
mPos = Qt.point(mouseX, mouseY)
}
onPositionChanged: {
mainWindow.setX(mainWindow.x + mouseX - mPos.x)
mainWindow.setY(mainWindow.y + mouseY - mPos.y)
}
}
MouseArea {
id: leftArea
anchors.top: parent.top
anchors.topMargin: 48
anchors.bottom: parent.bottom
anchors.bottomMargin: 5
cursorShape: Qt.SizeHorCursor
width: 5
onPressed: {
startMousePos = absoluteMousePos(leftArea)
startWindowPos = Qt.point(mainWindow.x, mainWindow.y)
startWindowSize = Qt.size(mainWindow.width, mainWindow.height)
}
onMouseXChanged: {
var abs = absoluteMousePos(leftArea)
var newWidth = Math.max(mainWindow.minimumWidth, startWindowSize.width - (abs.x - startMousePos.x))
var newX = startWindowPos.x - (newWidth - startWindowSize.width)
mainWindow.x = newX
mainWindow.width = newWidth
}
Rectangle {
anchors.fill: parent
color: "red"
}
}
MouseArea {
id: rightArea
width: 5
x: parent.width - rightArea.width
anchors.right: parent.rigth
anchors.top: parent.top
anchors.rightMargin: 5
anchors.topMargin: 48
anchors.bottom: parent.bottom
anchors.bottomMargin: 5
cursorShape: Qt.SizeHorCursor
onPressed: {
startMousePos = absoluteMousePos(rightArea)
startWindowPos = Qt.point(mainWindow.x, mainWindow.y)
startWindowSize = Qt.size(mainWindow.width, mainWindow.height)
}
onMouseXChanged: {
var abs = absoluteMousePos(rightArea)
var newWidth = Math.max(mainWindow.minimumWidth, startWindowSize.width + (abs.x - startMousePos.x))
mainWindow.width = newWidth
}
Rectangle {
anchors.fill: parent
color: "red"
}
}
MouseArea {
id: buttonArea
y: parent.height - buttonArea.height
height: 5
anchors.leftMargin: 5
anchors.left: parent.left
anchors.rightMargin: 5
anchors.right: parent.right
anchors.bottom: parent.bottom
cursorShape: Qt.SizeVerCursor
onPressed: {
startMousePos = absoluteMousePos(buttonArea)
startWindowPos = Qt.point(mainWindow.x, mainWindow.y)
startWindowSize = Qt.size(mainWindow.width, mainWindow.height)
}
onMouseYChanged: {
var abs = absoluteMousePos(buttonArea)
var newHeight = Math.max(mainWindow.minimumHeight, startWindowSize.height + (abs.y - startMousePos.y))
mainWindow.height = newHeight
}
Rectangle {
anchors.fill: parent
color: "red"
}
}
}
I have a question about different shift behavior with QtVirtualKeyboard on text and password fields.
import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.VirtualKeyboard 2.1
import QtQuick.Window 2.0
Window {
id: window
width: 800
height: 480
color: "#F6F6F6"
MouseArea {
id: content
width: window.width
Column {
id: textEditors
spacing: 15
x: 12
y: 12
width: parent.width - 26
Label {
color: "#565758"
text: "Tap fields to enter text"
anchors.horizontalCenter: parent.horizontalCenter
}
TextField {
width: parent.width
placeholderText: "One line field"
inputMethodHints: Qt.ImhPreferLowercase
}
TextField {
id: passwordField
width: parent.width
echoMode: TextField.Password
placeholderText: "Password field"
// changes do not work
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhPreferLowercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
}
}
}
InputPanel {
id: inputPanel
z: 2
y: window.height
width: window.width
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: window.height - inputPanel.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
}
}
If I click on shift basic TextField, only first letter is a capital one. This is the right behavior. But on the echoMode: textInput.Password shift has a caps-lock functionality, which I don't want.
Is it possible to override it somehow, or is it a QT feature, which is not possible to change?
Thank you
import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.VirtualKeyboard 2.1
import QtQuick.Window 2.0
Window {
id: window
width: 800
height: 480
color: "#F6F6F6"
visible: true
MouseArea {
id: content
width: window.width
Column {
id: textEditors
spacing: 15
x: 12
y: 12
width: parent.width - 26
Label {
color: "#565758"
text: "Tap fields to enter text"
anchors.horizontalCenter: parent.horizontalCenter
}
TextField {
width: parent.width
placeholderText: "One line field"
inputMethodHints: Qt.ImhPreferLowercase
}
TextField {
id: passwordField
width: parent.width
echoMode: TextField.Password
placeholderText: "Password field"
// changes do not work
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhPreferLowercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
onTextChanged: console.log(text)
}
}
}
InputPanel {
id: inputPanel
z: 2
y: window.height
width: window.width
property bool shiftActive: (InputContext == null) ? null : InputContext.shiftActive
property variant shiftKey: null
function findKey(parent) {
if (parent === null) {
return null
}
var children = parent.children
if (children === undefined || children === null) {
return null
}
var obj = null
for (var i = 0; i < children.length; i++) {
obj = children[i]
if (obj instanceof ShiftKey) {
return obj
}
obj = findKey(obj)
if (obj === null) {
continue
}
if (obj instanceof ShiftKey) {
return obj
}
}
return null
}
Timer {
id: timer
interval: 0
repeat: false
running: false
onTriggered: {
inputPanel.shiftKey.clicked()
}
}
Connections {
target: (InputContext.inputItem != null && InputContext.inputItem.echoMode === TextField.Password) ? InputContext.inputItem : null
onTextChanged: {
if (inputPanel.shiftActive) {
if (inputPanel.shiftKey == null) {
inputPanel.shiftKey = inputPanel.findKey(inputPanel.keyboard)
}
timer.start()
}
}
}
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: window.height - inputPanel.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
}
}
I've founded one drastic way. Now it won't go into caps-lock state, and works like shift. But there isn't an option to enable it somehow.
import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.VirtualKeyboard 2.1
import QtQuick.Window 2.0
Window {
id: window
width: 800
height: 480
color: "#F6F6F6"
MouseArea {
id: content
width: window.width
Column {
id: textEditors
spacing: 15
x: 12
y: 12
width: parent.width - 26
Label {
color: "#565758"
text: "Tap fields to enter text"
anchors.horizontalCenter: parent.horizontalCenter
}
TextField {
width: parent.width
placeholderText: "One line field"
}
TextField {
id: passwordField
width: parent.width
echoMode: TextField.PasswordEchoOnEdit
placeholderText: "Password field"
inputMethodHints: Qt.ImhAutoUppercase | Qt.ImhPreferLowercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
onTextChanged: {
InputContext.capsLock = false
}
}
}
}
InputPanel {
id: inputPanel
z: 2
y: window.height
width: window.width
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: window.height - inputPanel.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
}
}
The main problem that, that I wanted to the same behavior on the all inputs, not only in this example.
My custom ComboBox works is correctly, but I do not understand how to up popup with list of element above of combobox.
ComboBox {
id: pointsPositionComboBox
anchors.fill: parent
displayText: qsTr("Position point")
model: map.pointPositionModel
textRole: "name"
delegate: Row {
Rectangle {
id: delegateRect
width: pointsPositionRect.width - removePointRect.width
height: pointsPositionRect.height
color: {
if (!delegateRectMouseArea.containsMouse)
return "#25263C"
else if (delegateRectMouseArea.containsMouse
&& !delegateRectMouseArea.pressed) {
return "#5F8C6D"
} else if (delegateRectMouseArea.containsMouse
&& delegateRectMouseArea.pressed) {
return "#73AB84"
}
}
Text {
text: model.name
color: "white"
width: parent.width - removePointItem.width
font.pixelSize: pointsPositionComboBox.font.pixelSize
anchors.topMargin: pointsPositionRect.height / 3
anchors.left: parent.left
anchors.leftMargin: 5
}
MouseArea {
id: delegateRectMouseArea
anchors.fill: delegateRect
hoverEnabled: true
acceptedButtons: Qt.LeftButton
onClicked: {
if (hasUserMenuProperty.hasUserMenu()
&& !globalUserManager.hasAccessToPrivilege("controlMap"))
return
const latitude = model.position.latitude
const longitude = model.position.longitude
map.center.latitude = latitude
map.center.longitude = longitude
map.zoomLevel = model.zoomLevel
globalMapProperty.bearing = model.bearing
pointsPositionComboBox.popup.close()
}
}
}
Rectangle {
id: removePointRect
width: 30
height: 30
z: pointsPositionComboBox.z + 1
color: {
if (!removePointItemMouseArea.containsMouse)
return "#25263C"
else if (removePointItemMouseArea.containsMouse
&& !removePointItemMouseArea.pressed) {
return "#73A081"
} else if (removePointItemMouseArea.containsMouse
&& removePointItemMouseArea.pressed) {
return "#52785E"
}
}
Image {
id: removePointItem
anchors.centerIn: parent
source: removePointItemMouseArea.containsMouse ? "qrc:/icons/compass/remove_point_en.svg"
: "qrc:/icons/compass/remove_point_dis.svg"
}
MouseArea {
id: removePointItemMouseArea
anchors.fill: removePointRect
hoverEnabled: true
acceptedButtons: Qt.LeftButton
onClicked: {
if (hasUserMenuProperty.hasUserMenu()
&& !globalUserManager.hasAccessToPrivilege("controlMap"))
return
map.pointPositionModel.remove(map.pointPositionModel.index(index, 0))
}
}
}
}
}
Component.onCompleted: {
pointsPositionComboBox.popup.contentItem.implicitHeight = Qt.binding(function () {
// Делаем отображение только 10 элементов (остальные скроллом только)
return Math.min(pointsPositionRect.height * 10,
pointsPositionComboBox.popup.contentItem.contentHeight)
})
}
How to display a list of combo box elements from the top, and not from the bottom as in the photo?
P.s. I set implicitHeight for displaying element of combobox in Component.onCompleted
P.s.2 Now it looks like picture:
I've used the implicitHeight of the Popup to set its y property. Have a look at the source below. The default behavior of the Popup is when ever it fits below it will open up below, if there is not enough space below it will open above the ComboBox. The following code is a tweaked version of the Customizing ComboBox example from the Qt documentation.
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
width: 400
height: 300
Row {
anchors.centerIn: parent
ComboBox {
id: control
model: ["First", "Second", "Third"]
delegate: ItemDelegate {
width: control.width
contentItem: Text {
text: modelData
color: "#21be2b"
font: control.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
highlighted: control.highlightedIndex === index
}
indicator: Canvas {
id: canvas
x: control.width - width - control.rightPadding
y: control.topPadding + (control.availableHeight - height) / 2
width: 12
height: 8
contextType: "2d"
Connections {
target: control
function onPressedChanged() { canvas.requestPaint(); }
}
onPaint: {
context.reset();
context.moveTo(0, 0);
context.lineTo(width, 0);
context.lineTo(width / 2, height);
context.closePath();
context.fillStyle = control.pressed ? "#17a81a" : "#21be2b";
context.fill();
}
}
contentItem: Text {
leftPadding: 10
rightPadding: control.indicator.width + control.spacing
text: control.displayText
font: control.font
color: control.pressed ? "#17a81a" : "#21be2b"
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 120
implicitHeight: 40
border.color: control.pressed ? "#17a81a" : "#21be2b"
border.width: control.visualFocus ? 2 : 1
radius: 2
}
popup: Popup {
y: myCheckBox.checked ? -implicitHeight + 1 : control.height - 1
width: control.width
implicitHeight: contentItem.implicitHeight
padding: 1
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: control.popup.visible ? control.delegateModel : null
currentIndex: control.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { }
}
background: Rectangle {
border.color: "#21be2b"
radius: 2
}
}
}
CheckBox {
id: myCheckBox
text: qsTr("above")
}
}
}
I have a ListView which has dynamically added Items, and what I kinda want to know is how I could access items for example by index. Specifically, I want to have the color of the item rectangle change when I change the color using the Color Dialog. I guessed it should be maybe possible to first set a variable to the current item before calling the color dialog and then in the onAccepted method change the color of that item using the variable, but I don't know how to achieve anything of this in QML, because I am rather new to QML. Maybe you can offer even a cleaner way to change the color of the item's rectangle when the color dialog was closed (onAccepted). Thx for any help! :)
import QtQuick 2.0
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.4
Rectangle {
id: listViewContainer
width: parent.width/10*9;
height: 50
Behavior on height {
NumberAnimation {
duration: 100;
}
}
gradient: Gradient {
GradientStop {position: 0.0; color: "white" }
GradientStop {position: 1.0; color: "silver" }
}
radius: 5
ColorDialog {
id: colorDialog
title: "Please choose a color"
onAccepted: {
console.log("You chose: " + colorDialog.color)
Qt.quit()
}
onRejected: {
console.log("Canceled")
Qt.quit()
}
}
Component {
id: playerDelegate
Item {
anchors.left: parent.left
anchors.right: parent.right
height: 50
Column {
Text { text: '<b>Name:</b> ' + name }
Row {
MouseArea {
width: 20
height: 20
onClicked: {
colorDialog.visible = true;
playerColor = colorDialog.color;
//open color dialog
}
Rectangle {
radius: 3
anchors.fill: parent
color: playerColor
}
}
}
}
}
}
ListView {
id: playerListView
anchors.fill: parent
model:
ListModel {
id: playerListViewModel;
ListElement {
name: "Bill Smith"
playerColor: "red"
}
}
delegate: playerDelegate
}
Button {
id: addPlayerButton
anchors.top: playerListView.bottom
anchors.left: playerListView.left
anchors.right: playerListView.right
style: ButtonStyle {
label: Text {
text: "Add new player"
horizontalAlignment: Text.Center
}
}
onClicked: {
root.addnewPlayer(playerListView); //dont worry about this method
playerListViewModel.append({name: "Billy", playerColor: "blue"});
listViewContainer.height += 50;
}
}
}
Here is a sure fire way to make a working colorDialog --
in playerDelegate
Component {
id: playerDelegate
Item {
anchors.left: parent.left
anchors.right: parent.right
height: 50
Column {
Text {
text: '<b>Name:</b> ' + name
}
/* Object to store value from color selector */
Item {
id: colorSelector
property color color: "#000000"
onColorChanged: { playerColor = color; }
}
/* box to display color */
Rectangle {
//Layout.fillWidth: true
height: 120
width: 120
anchors.left: button.right
//Layout.alignment: Qt.AlignHCenter
color: colorSelector.color
border.width: 1
border.color: "#000000"
}
/* button to open dialog -- can be mousearea or other clickable object */
Button {
id: button
text: "Browse..."
onClicked: {
colorDialog.color = colorSelector.color
colorDialog.open()
}
}
/* actual color dialog for this delegate */
ColorDialog {
id: colorDialog
modality: Qt.ApplicationModal
title: "Please choose a color"
onAccepted: colorSelector.color = currentColor
}
}
}
}
In traditional Qt (QWidget) I have one QMainWindow and some dynamically created QWidgets with the content and I change them that one was seen in main windows. What are the ways of achieving when I have couple qml files and I wants to be able to switch between them when eg clicking a button.
There are at least 3 options for solving this problem:
You can use the ready for this purpose component StackView. The point is that you will create 2 components at once and you'll be able to change them by clicking on the button.
Example:
import QtQuick 2.12
import QtQuick.Controls 2.5
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: qsTr("Stack")
header: ToolBar {
contentHeight: toolButton.implicitHeight
ToolButton {
id: toolButton
text: stackView.depth > 1 ? "\u25C0" : "\u2630"
font.pixelSize: Qt.application.font.pixelSize * 1.6
onClicked: {
if (stackView.depth > 1) {
stackView.pop()
} else {
drawer.open()
}
}
}
Label {
text: stackView.currentItem.title
anchors.centerIn: parent
}
}
Drawer {
id: drawer
width: window.width * 0.66
height: window.height
Column {
anchors.fill: parent
ItemDelegate {
text: qsTr("Page 1")
width: parent.width
onClicked: {
stackView.push("Page1Form.qml")
drawer.close()
}
}
ItemDelegate {
text: qsTr("Page 2")
width: parent.width
onClicked: {
stackView.push("Page2Form.qml")
drawer.close()
}
}
}
}
StackView {
id: stackView
initialItem: "HomeForm.qml"
anchors.fill: parent
}
}
Use the Loader here you will dynamically load files during execution, the disadvantage of this method is that if you switch often, it will be time consuming.
Example:
import QtQuick 2.0
Item {
width: 200; height: 200
Loader { id: pageLoader }
MouseArea {
anchors.fill: parent
onClicked: pageLoader.source = "Page1.qml"
}
}
You can create a class in C++ that will be given to an already initialized QML object to an empty qml form. thus, mono place individual components into libraries and use them as plugins (use qqmlcomponent).
I would use a simple Loader, with a button that when is clicked, changes the source file of the Loader. Like so :
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
id: root
Rectangle {
id: page_main
color: "#202020"
anchors.fill : parent
Button {
id: button_page_1
width: 105
text: qsTr("Page 1")
anchors {
left: parent.left
top: parent.top
leftMargin: 6
topMargin: 0
}
checkable: true
onClicked: {
if (loader_main.source == "/page_1.qml") {
loader_main.source = ""
} else {
loader_main.source = "/page_1.qml"
button_page_2.checked = false
button_page_3.checked = false
}
}
}
Button {
id: button_page_2
width: 105
text: qsTr("Page 2")
anchors {
left: button_auto.right
top: parent.top
leftMargin: 6
topMargin: 0
}
checkable: true
onClicked: {
if (loader_main.source == "/page_2.qml") {
loader_main.source = ""
} else {
loader_main.source = "/page_2.qml"
button_page_1.checked = false
button_page_3.checked = false
}
}
}
Button {
id: button_page_3
width: 105
text: qsTr("Page 3")
anchors {
left: button_manual.right
top: parent.top
leftMargin: 6
topMargin: 0
}
checkable: true
onClicked: {
if (loader_main.source == "/page_page_3.qml") {
loader_main.source = ""
} else {
loader_main.source = "/page_page_3.qml"
button_page_1.checked = false
button_page_2.checked = false
}
}
}
}
Loader {
id: loader_main
y: 60
visible: true
anchors {
top: parent.top
bottom: parent.bottom
right: parent.right
left: parent.left
topMargin: 48
bottomMargin: 0
leftMargin: 0
rightMargin: 0
}
}
}