Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. How to add Items dynamically to a Row? (in javascript)
Forum Updated to NodeBB v4.3 + New Features

How to add Items dynamically to a Row? (in javascript)

Scheduled Pinned Locked Moved Solved QML and Qt Quick
13 Posts 4 Posters 2.1k Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • mbruelM mbruel

    Hi,
    I'm just learning QML and I'd like to factorize the creation of a Row using a js function.
    So basically in a js function create an Item using Qt.createComponent, affect all the properties and then add it to my Row.
    Is that possible?
    I'd expect sth like this

        Row {
            id: toolBar
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 20;
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: 15
    
            height: 50
    
            function addItem(idName, imagePath){
                console.log("adding item: " + idName);
                var button = Qt.createComponent("ToolBarButton.qml");
                button.id = idName;
                button.imagePath = imagePath;
                button.height = toolBar.height;
                button.width = 50;
                button.state = "Inactive";
                button.barIndex = toolBar.children.length;
                toolBar.add(button);
            }
    
            Component.onCompleted: {
                addItem("home", "img/home.png");
            }
        }
    
    

    But I'm getting the error qrc:/main.qml:131: TypeError: Property 'add' of object QQuickRow_QML_2(0x56297448e670) is not a function

    ODБOïO Offline
    ODБOïO Offline
    ODБOï
    wrote on last edited by
    #2

    hi

    @mbruel said in How to add Items dynamically to a Row? (in javascript):

    toolBar.add(button);

    @mbruel said in How to add Items dynamically to a Row? (in javascript):

    TypeError: Property 'add' of object QQuickRow_QML_2

    QML Row type has no add mathod

    see Dynamic QML Object Creation from JavaScript

    example

        Component.onCompleted: {
            createButton();
        }
    
        Row{
            id: row
            width: parent.width
            height: 50
        }
    
        function createButton() {
            var btn = btnComponent.createObject(row,{ text : "myButton"});
        }
    
        Component{
            id: btnComponent
            Button{}
        }
    
    1 Reply Last reply
    3
    • mbruelM Offline
      mbruelM Offline
      mbruel
      wrote on last edited by
      #3

      hum I see, so I've updated my code to be:

              function addItem(idName, imagePath){
                  console.log("adding item: " + idName);
                  var c = Qt.createComponent("ToolBarButton.qml");
                  var button = c.createObject(toolBar, {
                                                          id: idName,
                                                          imagePath: imagePath,
                                                          height: toolBar.height,
                                                          width: 50,
                                                          state: "Inactive",
                                                          barIndex: toolBar.children.length
                                                      });
              }
      
              Component.onCompleted: {
                  addItem("profile", "img/profile.png");
              }
      
              Connections{
                  target: profile
                  onSelected: changeMainMenu(idx);
              }
      

      I'm getting now getting the issue: qrc:/main.qml:186: ReferenceError: profile is not defined

      So if I understand well I can't set the id of an object created dynamically? Is that right?
      This wouldn't be such a big deal but then how can I connect a signal of that object created dynamically?

      ODБOïO 1 Reply Last reply
      0
      • mbruelM mbruel

        hum I see, so I've updated my code to be:

                function addItem(idName, imagePath){
                    console.log("adding item: " + idName);
                    var c = Qt.createComponent("ToolBarButton.qml");
                    var button = c.createObject(toolBar, {
                                                            id: idName,
                                                            imagePath: imagePath,
                                                            height: toolBar.height,
                                                            width: 50,
                                                            state: "Inactive",
                                                            barIndex: toolBar.children.length
                                                        });
                }
        
                Component.onCompleted: {
                    addItem("profile", "img/profile.png");
                }
        
                Connections{
                    target: profile
                    onSelected: changeMainMenu(idx);
                }
        

        I'm getting now getting the issue: qrc:/main.qml:186: ReferenceError: profile is not defined

        So if I understand well I can't set the id of an object created dynamically? Is that right?
        This wouldn't be such a big deal but then how can I connect a signal of that object created dynamically?

        ODБOïO Offline
        ODБOïO Offline
        ODБOï
        wrote on last edited by
        #4

        @mbruel

        function createButton() {
               var btn = btnComponent.createObject(row,{ text : "myButton"});
               btn.onClicked.connect(btnClicked)
           }
        
           function btnClicked(){console.log("button clicked")}
        
        1 Reply Last reply
        1
        • mbruelM Offline
          mbruelM Offline
          mbruel
          wrote on last edited by
          #5

          great, I got what I wanted with that piece of code:

              Row {
                  id: toolBar
                  anchors.bottom: parent.bottom
                  anchors.bottomMargin: 20;
                  anchors.horizontalCenter: parent.horizontalCenter
                  spacing: 15
          
                  height: 50
          
          
                  function addItem(idName){
                      console.log("adding item: " + idName);
                      var c = Qt.createComponent("ToolBarButton.qml");
                      var barIdx = toolBar.children.length;
                      var button = c.createObject(toolBar, {
                                                              id: idName,
                                                              imagePath: "img/"+idName+".png",
                                                              height: toolBar.height,
                                                              width: 50,
                                                              state: barIdx === 0 ? "Selected": "Inactive",
                                                              barIndex: toolBar.children.length
                                                          });
                      button.onSelected.connect(changeMainMenu);
                  }
          
          
                  Component.onCompleted: {
                      addItem("home");
                      addItem("mail");
                      addItem("compas");
                      addItem("stack");
                      addItem("profile");
                  }
              }
          

          my ToolBarButton Item is emitting the "selected" signal.

          Last question that is a bit out of the scope of this thread. How can I create a List of string that I would iterate in my onCompleted method?
          Like a property or simple variable of my root Window.

          ODБOïO 1 Reply Last reply
          0
          • mbruelM mbruel

            great, I got what I wanted with that piece of code:

                Row {
                    id: toolBar
                    anchors.bottom: parent.bottom
                    anchors.bottomMargin: 20;
                    anchors.horizontalCenter: parent.horizontalCenter
                    spacing: 15
            
                    height: 50
            
            
                    function addItem(idName){
                        console.log("adding item: " + idName);
                        var c = Qt.createComponent("ToolBarButton.qml");
                        var barIdx = toolBar.children.length;
                        var button = c.createObject(toolBar, {
                                                                id: idName,
                                                                imagePath: "img/"+idName+".png",
                                                                height: toolBar.height,
                                                                width: 50,
                                                                state: barIdx === 0 ? "Selected": "Inactive",
                                                                barIndex: toolBar.children.length
                                                            });
                        button.onSelected.connect(changeMainMenu);
                    }
            
            
                    Component.onCompleted: {
                        addItem("home");
                        addItem("mail");
                        addItem("compas");
                        addItem("stack");
                        addItem("profile");
                    }
                }
            

            my ToolBarButton Item is emitting the "selected" signal.

            Last question that is a bit out of the scope of this thread. How can I create a List of string that I would iterate in my onCompleted method?
            Like a property or simple variable of my root Window.

            ODБOïO Offline
            ODБOïO Offline
            ODБOï
            wrote on last edited by ODБOï
            #6

            @mbruel said in How to add Items dynamically to a Row? (in javascript):

            Last question that is a bit out of the scope of this thread. How can I create a List of string that I would iterate in my onCompleted method?

            Like a property or simple variable of my root Window.

            https://doc.qt.io/qt-5/qtqml-syntax-objectattributes.html

            property var someList: [ "three", "four"]
            
            1 Reply Last reply
            0
            • mbruelM Offline
              mbruelM Offline
              mbruel
              wrote on last edited by
              #7

              great, @LeLev you rock!

              here is the updated code:

                  Row {
                      property var menuList : ["home", "mail", "compas", "stack", "profile"]
                      
                      id: toolBar
                      anchors.bottom: parent.bottom
                      anchors.bottomMargin: 20;
                      anchors.horizontalCenter: parent.horizontalCenter
                      spacing: 15
              
                      height: 50
              
              
                      function addItem(idName){
                          console.log("adding item: " + idName);
                          var c = Qt.createComponent("ToolBarButton.qml");
                          var barIdx = toolBar.children.length;
                          var button = c.createObject(toolBar, {
                                                                  id: idName,
                                                                  imagePath: "img/"+idName+".png",
                                                                  height: toolBar.height,
                                                                  width: 50,
                                                                  state: barIdx === 0 ? "Selected": "Inactive",
                                                                  barIndex: toolBar.children.length
                                                              });
                          button.onSelected.connect(changeMainMenu);
                      }
              
                      Component.onCompleted: {
                          for (var i=0; i < menuList.length; ++i)
                              addItem(menuList[i]);
                      }
                  }
              
              1 Reply Last reply
              0
              • fcarneyF Offline
                fcarneyF Offline
                fcarney
                wrote on last edited by
                #8

                Here is another way to add unique items using a repeater and a loader (see last post):
                https://forum.qt.io/topic/117076/random-customs-controls-in-a-row/3

                C++ is a perfectly valid school of magic.

                mbruelM 1 Reply Last reply
                1
                • fcarneyF fcarney

                  Here is another way to add unique items using a repeater and a loader (see last post):
                  https://forum.qt.io/topic/117076/random-customs-controls-in-a-row/3

                  mbruelM Offline
                  mbruelM Offline
                  mbruel
                  wrote on last edited by
                  #9

                  @fcarney
                  sounds less intuitive but I'll have a look, thanks ;)

                  1 Reply Last reply
                  0
                  • GrecKoG Offline
                    GrecKoG Offline
                    GrecKo
                    Qt Champions 2018
                    wrote on last edited by
                    #10

                    You should refrain from using Qt.createComponent and manual dynamic instantiation in QML.

                    Stay declarative.

                    Your task can be achieved like so :

                    Row {
                       id: toolBar
                       property var menuList : ["home", "mail", "compas", "stack", "profile"]
                       property int currentIndex: 0
                        
                        anchors {
                            bottom: parent.bottom
                            bottomMargin: 20;
                            horizontalCenter: parent.horizontalCenter
                        }
                        spacing: 15
                        height: 50
                    
                        Repeater {
                            model: toolBar.menuList
                            ToolBarButton {
                                id: button
                                imagePath: "img/" + model.modelData + ".png"
                                height: toolBar.height
                                width: 50
                                barIndex: model.index
                                state:barIndex === toolBar.currentIndex ?  "Selected" : "Inactive"
                                onSelected: toolBar.currentIndex = barIndex
                            }
                        }
                    }
                    

                    I also changed the selected button logic here to be self contained and because I didn't know what were the definition of your selected signal or changeMainMenu function.

                    mbruelM 1 Reply Last reply
                    2
                    • GrecKoG GrecKo

                      You should refrain from using Qt.createComponent and manual dynamic instantiation in QML.

                      Stay declarative.

                      Your task can be achieved like so :

                      Row {
                         id: toolBar
                         property var menuList : ["home", "mail", "compas", "stack", "profile"]
                         property int currentIndex: 0
                          
                          anchors {
                              bottom: parent.bottom
                              bottomMargin: 20;
                              horizontalCenter: parent.horizontalCenter
                          }
                          spacing: 15
                          height: 50
                      
                          Repeater {
                              model: toolBar.menuList
                              ToolBarButton {
                                  id: button
                                  imagePath: "img/" + model.modelData + ".png"
                                  height: toolBar.height
                                  width: 50
                                  barIndex: model.index
                                  state:barIndex === toolBar.currentIndex ?  "Selected" : "Inactive"
                                  onSelected: toolBar.currentIndex = barIndex
                              }
                          }
                      }
                      

                      I also changed the selected button logic here to be self contained and because I didn't know what were the definition of your selected signal or changeMainMenu function.

                      mbruelM Offline
                      mbruelM Offline
                      mbruel
                      wrote on last edited by
                      #11

                      @GrecKo
                      Nice, thanks a lot!
                      Indeed it looks much better that way. I've checked the Repeater documentation and it's defo what I should use.
                      Here is the updated code which is nearly exactly what you gave me:

                          Row {
                              id: toolBar
                              anchors {
                                  bottom: parent.bottom
                                  bottomMargin: toolBarMargin;
                                  horizontalCenter: parent.horizontalCenter
                              }
                      
                              height:  toolBarHeight
                              spacing: toolBarSpacing
                      
                              Repeater {
                                  model: menuList
                                  ToolBarButton {
                                      id: button
                                      imagePath: "img/" + model.modelData + ".png"
                                      height: toolBar.height
                                      width: toolBarButtonWidth
                                      barIndex: model.index
                                      state: model.index === 0 ?  "Selected" : "Inactive"
                                      onSelected: changeMainMenu(barIndex);
                                  }
                              }
                      
                              Component.onCompleted: {
                                  toolBar.children[1].setBadge("3");
                                  toolBar.children[3].setBadge("12");
                              }
                          }
                      

                      The result is something like this:
                      alt text

                      My ToolBarButton have their opacity changing whether their state is Inactive, Hovered or Selected. When they emit the selected signal, the changeMainMenu function would change the state of the other buttons and the content of the main area (not sure yet how I'll achieve that, probably using a StackedView)

                          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";
                              }
                          }
                      
                      GrecKoG 1 Reply Last reply
                      0
                      • mbruelM mbruel

                        @GrecKo
                        Nice, thanks a lot!
                        Indeed it looks much better that way. I've checked the Repeater documentation and it's defo what I should use.
                        Here is the updated code which is nearly exactly what you gave me:

                            Row {
                                id: toolBar
                                anchors {
                                    bottom: parent.bottom
                                    bottomMargin: toolBarMargin;
                                    horizontalCenter: parent.horizontalCenter
                                }
                        
                                height:  toolBarHeight
                                spacing: toolBarSpacing
                        
                                Repeater {
                                    model: menuList
                                    ToolBarButton {
                                        id: button
                                        imagePath: "img/" + model.modelData + ".png"
                                        height: toolBar.height
                                        width: toolBarButtonWidth
                                        barIndex: model.index
                                        state: model.index === 0 ?  "Selected" : "Inactive"
                                        onSelected: changeMainMenu(barIndex);
                                    }
                                }
                        
                                Component.onCompleted: {
                                    toolBar.children[1].setBadge("3");
                                    toolBar.children[3].setBadge("12");
                                }
                            }
                        

                        The result is something like this:
                        alt text

                        My ToolBarButton have their opacity changing whether their state is Inactive, Hovered or Selected. When they emit the selected signal, the changeMainMenu function would change the state of the other buttons and the content of the main area (not sure yet how I'll achieve that, probably using a StackedView)

                            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";
                                }
                            }
                        
                        GrecKoG Offline
                        GrecKoG Offline
                        GrecKo
                        Qt Champions 2018
                        wrote on last edited by GrecKo
                        #12

                        @mbruel said in How to add Items dynamically to a Row? (in javascript):

                        My ToolBarButton have their opacity changing whether their state is Inactive, Hovered or Selected. When they emit the selected signal, the changeMainMenu function would change the state of the other buttons and the content of the main area (not sure yet how I'll achieve that, probably using a StackedView)

                        That's still not very declarative :)
                        Act on your data, not your visual items.
                        Items should follow the data.

                        I believe my proposed solution is better, maybe relocate the currentIndex property if you want.

                        Your solution:

                                   // in your main or elsewhere:
                                   function changeMainMenu(selectedMenu) {
                                        for (var i = 0; i < toolBar.children.length; ++i) {
                                              if (i !== selectedMenu)    
                                                  toolBar.children[i].state = "Inactive";
                                        }
                                    }
                                   // in your button
                                    state: model.index === 0 ?  "Selected" : "Inactive"
                                    onSelected: changeMainMenu(barIndex);
                        

                        You manipulate the properties of your items imperatively, you define in the state binding of the button (not really an actual binding since it will be overwritten by the changeMainMenu function anyway) that the first button is selected

                        My solution:

                                    // in the toolbar or elsewhere:
                                    property int selectedIndex: 0 // 0 is the initial selectedIndex
                                    // in your buttons
                                    state: barIndex === toolBar.selectedIndex ?  "Selected" : "Inactive"    
                                    onClicked: toolBar.selectedIndex = barIndex
                                    // ^ note that I changed it to a clicked signal, the button just tells how the user interacted with it
                                    //   it doesn't set itself as selected, the state binding does that.
                                    //   both your parent code and the button code becomes simpler
                        

                        @mbruel said in How to add Items dynamically to a Row? (in javascript):

                            Component.onCompleted: {
                                toolBar.children[1].setBadge("3");
                                toolBar.children[3].setBadge("12");
                            }
                        

                        Put that information in your model instead:

                        Repeater {
                            model: ListModel { // an array of JS objects would work here too
                                ListElement { image: "home"; badge: 0 }
                                ListElement { image: "mail"; badge: 3 }
                                ListElement { image: "compas"; badge: 0 }
                                ListElement { image: "stack"; badge: 12 }
                                ListElement { image: "profile"; badge: 0 } 
                            }
                            ToolBarButton {
                                ...
                                imagePath: "img/" + model.image+ ".png"
                                badge: model.badge
                            }
                        }
                        

                        Sorry if I'm being a purist, I just want to share my vision of how one should write QML ;)

                        I should also mention that I'm not a fan of the state property, but that's more opinion based.
                        In your case I would add a property bool selected property. Bind like so in the repeater: selected: model.index === selectedIndex
                        and in the button itself do: opacity: selected ? 1 : (hovered ? 0.7 : 0.4)

                        1 Reply Last reply
                        1
                        • mbruelM Offline
                          mbruelM Offline
                          mbruel
                          wrote on last edited by mbruel
                          #13

                          @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?

                          1 Reply Last reply
                          0

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved