@GrecKo said in How to add Items dynamically to a Row? (in javascript):
state: barIndex === toolBar.selectedIndex ? "Selected" : "Inactive"
onClicked: toolBar.selectedIndex = barIndex
Hum I don't catch something.... The "state binding" doesn't seem to work correctly.
When I click on a button, so the selectedIndex "main variable" is set to the index of my button but then the state of the other buttons is not updated due to the change of the value of selectedIndex.
What in your solution would trigger that? (What my changeMainMenu is actually doing)
Put that information in your model instead
Cool will do, it sounds better but for now it is just a mock, I would get this info from C++. Hum, still for the initialization I could use that ListModel. I didn't read the QML model view framework yet...
Sorry if I'm being a purist, I just want to share my vision of how one should write QML ;)
don't be sorry, that's exactly what I'm looking for, get the best practices asap. Thanks for helping ;)
I should also mention that I'm not a fan of the state property, but that's more opinion based.
well I'm not a big fan either of states but I saw that in an example and it seems the easiest way to use Transitions.
// an array of JS objects would work here too
how you do that?
Otherwise for the mainArea I used a Loader that is reloaded in my changeMainMenu function. How would this work with your solution?
Here is my full main.qml:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
id: root
visible: true
width: 400
height: 600
title: qsTr("my QML app!")
property int toolBarIndex : 0
property int toolBarHeight : 50;
property int toolBarSpacing : 15;
property int toolBarMargin : 20;
property int toolBarButtonWidth : 50;
property int headerButtonSize : 30;
property int mainMargin : 20;
// property var menuList : ["home", "mail", "compas", "stack", "profile"]
ListModel {
id: toolBarModel
ListElement { name : "home" ; badge: "0" ; color: "lightgreen" }
ListElement { name : "mail" ; badge: "3" ; color: "lightblue" }
ListElement { name : "compas" ; badge: "0" ; color: "lightgray" }
ListElement { name : "stack" ; badge: "12" ; color: "lightyellow" }
ListElement { name : "profile" ; badge: "0" ; color: "ivory" }
}
Rectangle {
id: background
anchors {
fill: parent;
margins: 0;
}
color: "green"
Image {
source: "img/bg.png";
fillMode: Image.Stretch;
anchors.fill: parent;
opacity: 1
}
}
Loader {
id: mainArea
anchors {
top: parent.top
left: parent.left
topMargin: mainMargin + headerButtonSize / 2;
bottomMargin: mainMargin
leftMargin: mainMargin
rightMargin: mainMargin
}
width: parent.width - 2*mainMargin
height: parent.height - 2*mainMargin - toolBarHeight - toolBarMargin
}
ToolBarButton
{
id: search
anchors {
top: parent.top
left: parent.left
margins: mainMargin;
}
width : headerButtonSize
height: headerButtonSize
imagePath : "img/search.png"
state : "Inactive"
selectable: false
}
ToolBarButton
{
id: chat
anchors {
top: parent.top
right: parent.right
margins: mainMargin;
}
width : headerButtonSize
height: headerButtonSize
imagePath : "img/chat.png"
state : "Inactive"
selectable: false
}
Text {
text: title
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
topMargin: mainMargin;
}
color: "#ff0000"
font.pointSize: 24
font.bold: true
}
function changeMainMenu(selectedMenu){
console.log("New idx: "+selectedMenu)
for (var i = 0; i < toolBar.children.length; ++i)
{
if (i !== selectedMenu)
toolBar.children[i].state = "Inactive";
}
mainArea.setSource("SimpleMainArea.qml");
mainArea.item.lbl.text = toolBarModel.get(selectedMenu).name;//menuList[selectedMenu];
mainArea.item.color = toolBarModel.get(selectedMenu).color;
}
Row {
id: toolBar
anchors {
bottom: parent.bottom
bottomMargin: toolBarMargin;
horizontalCenter: parent.horizontalCenter
}
height : toolBarHeight
spacing: toolBarSpacing
Repeater {
model: toolBarModel; //menuList
ToolBarButton {
id: button
height: toolBar.height
width: toolBarButtonWidth
// imagePath: "img/" + model.modelData + ".png"
imagePath: "img/" + model.name + ".png"
barIndex: model.index
// state: root.toolBarIndex === barIndex ? "Selected" : "Inactive"
// onSelected: root.toolBarIndex = barIndex
state: barIndex === root.toolBarIndex ? "Selected" : "Inactive"
onSelected: changeMainMenu(barIndex)
}
}
Component.onCompleted: {
for (var i = 0; i < toolBar.children.length; ++i)
toolBar.children[i].setBadge(toolBarModel.get(i).badge);
}
}
Component.onCompleted: {
changeMainMenu(0);
}
}
The SimpleMainArea.qml
import QtQuick 2.0
import QtQuick.Controls 2.14
Rectangle {
property alias lbl: lbl
radius: 10
opacity: 0.8
Text {
id: lbl
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter;
}
text: "not set...";
}
}
and my ToolBarButton.qml so you can tell me if you see bad practices ;)
import QtQuick 2.12
import QtQuick.Controls 2.14
Item {
id: button
property string imagePath;
property int barIndex : 0;
property bool selectable : true;
property color color: "transparent"
property double opacityInactive: 0.2
property double opacityHover : 0.4
property double opacitySelected: 1
property int borderWidth : 0
property int borderRadius: 0
property string previousState: "Inactive";
property color badgeColor : "#ec3e3a"; // redish color (exactly the one used in OS X 10.10)
// Allow the programmer to define the text to display.
// Note that this control can display both text and numbers.
// property alias badgeText: badgeLbl.text
signal selected(int idx);
//RectangItemle to draw the button
Rectangle {
id: rect
anchors.fill: parent
radius: borderRadius
color: button.enabled ? button.color : "grey"
border.width: borderWidth
border.color: "black"
opacity: opacityInactive
Image {
id: img
anchors.fill: parent;
source: imagePath;
fillMode: Image.PreserveAspectFit;
}
}
Rectangle {
id: badge
visible: false
smooth: true
// Create an animation when the opacity changes
Behavior on opacity {NumberAnimation{}}
// Setup the anchors so that the badge appears on the bottom right
// area of its parent
anchors.right: rect.right
anchors.top: rect.top
// This margin allows us to be "a little outside" of the object in which
// we add the badge
// anchors.margins: -parent.width / 5 + device.ratio(1)
color: badgeColor
// Make the rectangle a circle
radius: width / 2
// Setup height of the rectangle (the default is 18 pixels)
height: 18
// Make the rectangle and ellipse if the length of the text is bigger than 2 characters
width: badgeLbl.text.length > 2 ? badgeLbl.paintedWidth + height / 2 : height
// Create a label that will display the number of connected users.
Label {
id: badgeLbl
color: "#fdfdfdfd"
font.pixelSize: 9
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
// We need to have the same margins as the badge so that the text
// appears perfectly centered inside its parent.
anchors.margins: parent.anchors.margins
}
}
//Mouse area to react on click events
MouseArea {
hoverEnabled: true
anchors.fill: button
onEntered: {
button.previousState = button.state;
if (button.state != 'Selected')
button.state='Hover';
}
onExited : { button.state = previousState; }
onClicked: {
if (selectable)
{
button.previousState = 'Selected';
button.state = 'Selected';
}
button.selected(button.barIndex);
}
}
function setBadge(msg)
{
if (msg !== "" && msg !== "0")
{
badge.visible = true;
badgeLbl.text = msg;
badge.width = badgeLbl.text.length > 2 ? badgeLbl.paintedWidth + badgeLbl.height / 2 : badgeLbl.height;
}
else
badge.visible = false;
}
function clearBadge() {badge.visible = false;}
//change the color of the button in differen button states
states: [
State {
name: "Inactive"
PropertyChanges {
target : rect
opacity: opacityInactive
}
},
State {
name: "Hover"
PropertyChanges {
target : rect
opacity: opacityHover
}
},
State {
name: "Selected"
PropertyChanges {
target : rect
opacity: opacitySelected
}
}
]
state: previousState;
//define transmission for the states
transitions: [
Transition {
from: ""; to: "Hover"
OpacityAnimator { duration: 200 }
},
Transition {
from: "*"; to: "Selected"
OpacityAnimator { duration: 10 }
}
]
}
Edit: here is a small video of the desired behaviour
Edit2: my commented
// state: root.toolBarIndex === barIndex ? "Selected" : "Inactive"
// onSelected: root.toolBarIndex = barIndex
is not stable, it is working the first time but then it seems the change of root.toolBarIndex doesn't impact all the buttons.. Any idea why?