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. Custom Combo Box Auto hide Drop Down when lost focus

Custom Combo Box Auto hide Drop Down when lost focus

Scheduled Pinned Locked Moved Solved QML and Qt Quick
comboboxdrop-downhidefocus
4 Posts 2 Posters 5.2k Views
  • 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.
  • D Offline
    D Offline
    Dong
    wrote on 5 Jul 2016, 04:54 last edited by
    #1

    Hi Everyone !

    I know, this is such noob Question but I don't have time for research

    What I want is create a Custom Combo Box.

    • Drop down Item must be same style on Windows & Mac
      (Original Combo Box show a tick mark on MAC)
    • It can Show / Hide and Enable / Disable some items in model when needed
      (I tried custom ComboBoxStyle but it doesn't work. Invisible Item in Drop Down List still take an empty space)

    So, I implement my ComboBox like this

    FocusScope {
        id: root
        width: 160
        height: 24
        property var model
        ....
        Rectangle {
            id: recButton
            anchors.fill: parent
            border.width: 1
            border.color: MDStyle.borderColor
            radius: 2
            clip: true
    
            Label {
                id : lblDisplayText
                anchors.left: parent.left
                anchors.leftMargin: 2
                anchors.right: parent.right
                anchors.rightMargin: recButton.width + 2
                anchors.verticalCenter: parent.verticalCenter
                text: "Selected Item"
            }
    
            Rectangle {
                id: recArrowContainer
                height: parent.height
                width: height
                anchors.right: parent.right
                color: "transparent"
                Image {
                    id: imgArrow
                    anchors.centerIn: parent
                    width: sourceSize.width
                    height: sourceSize.height
                    source: "qrc:/Images/GUI/metrixa-icon-down-arrow.png"
                }
            }
    
            MouseArea {
                anchors.fill: parent
                hoverEnabled: true
                onClicked: {
                    root.focus = true
                    root.state = root.state==="dropDown"?"":"dropDown"
                }
            }
        }
    
        Rectangle {
            id: recDropDown
            width:root.width;
            height:0;
            clip:true;
            radius:2;
            anchors.top: recButton.bottom;
            anchors.margins: 1;
            border.width: 1
            border.color: MDStyle.borderColor
        }
    
        states: State {
            name: "dropDown";
    
            PropertyChanges { target: recDropDown; height:24 * (root.model ? root.model.length : 4) }
        }
    }
    

    The problem is "How can I detect when my control is lost focus to hide the Drop Down.
    Included focus on next form item by tab key, mouse click on other form item, or click on form back ground

    I also look in original ComboBox qml file and notice about FocusScope, Menu, Popup but doesn't know how it work.

    Please help me ! ASAP !!!

    M 1 Reply Last reply 5 Jul 2016, 11:57
    0
    • D Dong
      5 Jul 2016, 04:54

      Hi Everyone !

      I know, this is such noob Question but I don't have time for research

      What I want is create a Custom Combo Box.

      • Drop down Item must be same style on Windows & Mac
        (Original Combo Box show a tick mark on MAC)
      • It can Show / Hide and Enable / Disable some items in model when needed
        (I tried custom ComboBoxStyle but it doesn't work. Invisible Item in Drop Down List still take an empty space)

      So, I implement my ComboBox like this

      FocusScope {
          id: root
          width: 160
          height: 24
          property var model
          ....
          Rectangle {
              id: recButton
              anchors.fill: parent
              border.width: 1
              border.color: MDStyle.borderColor
              radius: 2
              clip: true
      
              Label {
                  id : lblDisplayText
                  anchors.left: parent.left
                  anchors.leftMargin: 2
                  anchors.right: parent.right
                  anchors.rightMargin: recButton.width + 2
                  anchors.verticalCenter: parent.verticalCenter
                  text: "Selected Item"
              }
      
              Rectangle {
                  id: recArrowContainer
                  height: parent.height
                  width: height
                  anchors.right: parent.right
                  color: "transparent"
                  Image {
                      id: imgArrow
                      anchors.centerIn: parent
                      width: sourceSize.width
                      height: sourceSize.height
                      source: "qrc:/Images/GUI/metrixa-icon-down-arrow.png"
                  }
              }
      
              MouseArea {
                  anchors.fill: parent
                  hoverEnabled: true
                  onClicked: {
                      root.focus = true
                      root.state = root.state==="dropDown"?"":"dropDown"
                  }
              }
          }
      
          Rectangle {
              id: recDropDown
              width:root.width;
              height:0;
              clip:true;
              radius:2;
              anchors.top: recButton.bottom;
              anchors.margins: 1;
              border.width: 1
              border.color: MDStyle.borderColor
          }
      
          states: State {
              name: "dropDown";
      
              PropertyChanges { target: recDropDown; height:24 * (root.model ? root.model.length : 4) }
          }
      }
      

      The problem is "How can I detect when my control is lost focus to hide the Drop Down.
      Included focus on next form item by tab key, mouse click on other form item, or click on form back ground

      I also look in original ComboBox qml file and notice about FocusScope, Menu, Popup but doesn't know how it work.

      Please help me ! ASAP !!!

      M Offline
      M Offline
      medyakovvit
      wrote on 5 Jul 2016, 11:57 last edited by
      #2

      @Dong
      Maybe this can help:

      Whether or not an Item has active focus can be queried through the property Item::activeFocus property
      
      D 1 Reply Last reply 5 Jul 2016, 13:01
      0
      • M medyakovvit
        5 Jul 2016, 11:57

        @Dong
        Maybe this can help:

        Whether or not an Item has active focus can be queried through the property Item::activeFocus property
        
        D Offline
        D Offline
        Dong
        wrote on 5 Jul 2016, 13:01 last edited by
        #3

        @medyakovvit

        I'm already tried focus & activeFocus. but doesn't work.

        I set focus for popup Item (Rectangle) when clicked on MouseArea
        And print out focus & activeFocus value (it both : true)

        But when I click outside (Click on Window's background)
        Nothing happen.
        It only change to :false: when I switch to another Windows.

        D 1 Reply Last reply 7 Jul 2016, 09:32
        0
        • D Dong
          5 Jul 2016, 13:01

          @medyakovvit

          I'm already tried focus & activeFocus. but doesn't work.

          I set focus for popup Item (Rectangle) when clicked on MouseArea
          And print out focus & activeFocus value (it both : true)

          But when I click outside (Click on Window's background)
          Nothing happen.
          It only change to :false: when I switch to another Windows.

          D Offline
          D Offline
          Dong
          wrote on 7 Jul 2016, 09:32 last edited by Dong 7 Jul 2016, 09:35
          #4

          Finally, I made it !!!

          1. To implement "Drop Down List" just like Normal Combo

          • "Drop Down List" need to be on top of all other control
          • "Drop Down List" must be disappear when click outside it

          To do that, I need to set the "Root Item" (ApplicationWindow or other Component) as parent of maskMouseArea & recDropDown
          (There is a little tricky here and I write a javascript function to return the "Root Item" & "Referred Coordinate" to the Combo - You need this to display the popup by set x, y position)

          function getRootComponent(component) {
              var result = {Component : component, refX : 0, refY : 0 }
              while (result.Component.parent) {
                  result.refX = result.refX + result.Component.x
                  result.refY = result.refY + result.Component.y
                  result.Component = result.Component.parent
              }
              return result;
          }
          

          When user Click on maskMouseArea it'll close the recDropDown

          Bellow is source code of MyComboBox.qml

          (Note CommonScript.jsBinding is my function to support binding with Dynamic Property, you can use normal binding)

          import QtQuick 2.2
          import QtQuick.Controls 1.2
          import QtQuick.Controls.Private 1.0
          import QtQuick.Controls.Styles 1.2
          
          import "../"
          import "../commonScripts.js" as CommonScript
          
          Rectangle {
              id: root
              width: 160
              height: MDStyle.comboBoxHeight
              border.width: 1
              border.color: MDStyle.borderColor
              color: MDStyle.backGroundColor
              radius: 2
              clip: true
          
              signal activated(int index)
          
              property int currentIndex: -1
              property var selectedValue
              property var selectedItem
              property string displayMember: "Text"
              property string valueMember: "Value"
              property string imageSourceMember: "ImageSource"
              property bool showIcon: false
              property int dropDownMinWidth: root.width
              property int dropDownMaxWidth: 2 * dropDownMinWidth
              property int maxRowsCount: 6
              property var model
          
              property bool popupVisible : false
              property int rowHeight : 28
          
              property string displayText: ""
          
              Item {
                  id: privateProperties
                  property var rootAncestor: CommonScript.getRootComponent(root)
                  property int popupWidth : root.dropDownMinWidth
                  property bool popupWidthAutoAdjusted: false
              }
          
              property Component displayItem:
                  Label {
                  id : lblDisplayText
                  anchors.left: parent.left
                  anchors.leftMargin: MDStyle.textFontSize / 2
                  anchors.right: parent.right
                  anchors.rightMargin: recArrowContainer.width + 2
                  anchors.verticalCenter: parent.verticalCenter
                  font.family: MDStyle.fontFamily
                  font.pixelSize: MDStyle.textFontSize
                  color: MDStyle.fontColor
                  clip: true
          
                  text: displayText != "" ? displayText :
                      listItems.selectedIndex >= 0 ? listItems.model[listItems.selectedIndex].binding(displayMember) : ""
              }
          
              Rectangle {
                  id: recControlHover
                  anchors.fill: parent
                  anchors.margins: 2
                  border.width: 0
                  color: "#113399FF"
                  visible: clickableArea.containsMouse
              }
          
              //MouseArea to show popup when clicked
              MouseArea {
                  id: clickableArea
                  anchors.fill: parent
                  hoverEnabled: true
                  onClicked: {
                      root.popupVisible = true
                      return true;
                  }
              }
          
              //Loader for Display Item
              Loader {
                  id : displayItemLoader
                  anchors.left: parent.left
                  anchors.right: parent.right
                  anchors.verticalCenter: parent.verticalCenter
          
                  sourceComponent: displayItem
              }
          
              //Arrow Image
              Rectangle {
                  id: recArrowContainer
                  height: parent.height - 2 * anchors.margins
                  width: height - 2 * anchors.margins
                  anchors.top: parent.top
                  anchors.right: parent.right
                  anchors.margins: 1
                  radius: 2
                  color: "transparent"
                  Image {
                      id: imgArrow
                      anchors.centerIn: parent
                      width: sourceSize.width
                      height: sourceSize.height
                      source: "qrc:/Images/GUI/metrixa-icon-down-arrow.png"
                  }
              }
          
              //Mask Mouse Area (used to hide Drop Down List when click outside Drop Down List)
              MouseArea {
                  id: maskMouseArea
                  parent: privateProperties.rootAncestor.Component
                  anchors.fill: parent
                  visible: root.popupVisible
                  onClicked: {
                      root.popupVisible = false
                      return false
                  }
                  z: 9998
              }
          
              //Drop Down List
              Rectangle {
                  id: recDropDown
                  parent: privateProperties.rootAncestor.Component
                  width: {
                      return CommonScript.max(root.width,
                          privateProperties.popupWidth
                          + listItems.anchors.leftMargin + listItems.anchors.rightMargin
                          + ddlScrollView.anchors.leftMargin + ddlScrollView.anchors.rightMargin)
                  }
                  height: root.rowHeight * CommonScript.min(root.maxRowsCount, (root.model ? root.model.length : 0))
                          + ddlScrollView.anchors.topMargin + ddlScrollView.anchors.bottomMargin
                          + listItems.anchors.topMargin + listItems.anchors.bottomMargin
                  clip: true
                  radius:2
                  x: privateProperties.rootAncestor.refX
                  y: privateProperties.rootAncestor.refY + root.height + 1
                  border.width: 1
                  border.color: MDStyle.borderColor
                  color: "#FFFFFF"
                  visible: root.popupVisible
                  z: 9999
          
                  ScrollView {
                      id: ddlScrollView
                      anchors.fill: parent
                      anchors.margins: 1
                      clip: true
          
                      ListView {
                          id: listItems
                          anchors.left : parent.left
                          anchors.right : parent.right
                          anchors.top : parent.top
                          orientation: Qt.Vertical
                          anchors.margins: 2
                          height: {
                              //Only count displayed items
                              var visibleItemsCount = 0
                              for (var i = 0; i < listItems.model.length; i++) {
                                  var isVisible = true;
                                  if (listItems.model[i].binding("Visible") !== undefined)
                                      visible = listItems.model[i].binding("Visible");
          
                                  if (isVisible) visibleItemsCount = visibleItemsCount + 1
                              }
          
                              return CommonScript.min(maxRowsCount, visibleItemsCount) * root.rowHeight
                          }
                          property int selectedIndex: -1
                          model: root.model
          
                          delegate: Rectangle {
                              id: recItemContainer
                              width: parent.width
                              height: bVisible ? root.rowHeight : 0
                              clip: true
          
                              property var itemModel: model
                              property bool bVisible: CommonScript.jsBinding(recItemContainer, "bVisible", model ? model.modelData : undefined, "Visible", true)
                              property bool bEnable: CommonScript.jsBinding(recItemContainer, "bEnable", model ? model.modelData : undefined, "Enable", true)
                              property bool isHovered: false
          
                              Rectangle {
                                  id: recHilight
                                  color: "#663399FF"
                                  anchors.fill: parent
                                  anchors.margins: 0
                                  radius: 2
                                  visible: index === listItems.selectedIndex
                              }
          
                              Rectangle {
                                  id: recDisabled
                                  color: "#666666"
                                  anchors.fill: parent
                                  anchors.margins: 0
                                  radius: 2
                                  visible: false //!recItemContainer.bEnable
                              }
          
                              Rectangle {
                                  id: recItemHover
                                  color: "#333399FF"
                                  anchors.fill: parent
                                  anchors.margins: 1
                                  radius: 2
                                  visible: itemMouseArea.containsMouse
                              }
          
                              Loader {
                                  id: listItemLoader
                                  sourceComponent: listItem
                                  //anchors.fill: parent
                                  property var model: recItemContainer.itemModel
          
                                  onLoaded: {
                                      adjustPopupWidth();
                                  }
          
                                  function adjustPopupWidth()
                                      {
                                      var pWidth = CommonScript.max(listItemLoader.item.width, root.dropDownMinWidth)
                                      if (pWidth > privateProperties.popupWidth) privateProperties.popupWidthAutoAdjusted = true
                                      pWidth = CommonScript.max(pWidth, privateProperties.popupWidth)
                                      pWidth = CommonScript.min(pWidth, root.dropDownMaxWidth)
          
                                      root.rowHeight = CommonScript.max(listItemLoader.item.height, root.rowHeight)
                                      privateProperties.popupWidth = pWidth
                                      if (listItemLoader.width == 0) listItemLoader.width = pWidth
                                  }
                              }
          
                              MouseArea {
                                  id: itemMouseArea
                                  anchors.fill: parent
                                  enabled: recItemContainer.bEnable
                                  hoverEnabled: true
                                  onClicked: {
                                      if (recItemContainer.bEnable) {
                                          if (listItems.selectedIndex != index) {
                                              listItems.selectedIndex = index
                                              listItems.updateSelection()
                                              activated(index)
                                          }
                                          root.popupVisible = false
                                      }
                                      return true
                                  }
                              }
                          }
          
                          onCurrentIndexChanged: {
                              updateSelection()
                          }
          
                          function updateSelection ()
                          {
                              displayText = ""
                              root.currentIndex = listItems.selectedIndex
                              root.selectedValue = listItems.model[listItems.selectedIndex].binding(root.valueMember)
                              root.selectedItem = listItems.model[listItems.selectedIndex]
                          }
                      }
                  }
              }
          
              property Component listItem:
                  Rectangle {
                  width: recImage.width + lblItemText.contentWidth + 2 * lblItemText.anchors.leftMargin
                  height: MDStyle.textBoxHeight
          
                  Rectangle {
                      id: recImage
                      width: parent.height
                      height: parent.height
                      anchors.top: parent.top
                      anchors.left: parent.left
                      color: "transparent"
                      visible: root.showIcon
          
                      Image {
                          id: imgItemIcon
                          width: sourceSize.width
                          height: sourceSize.height
                          anchors.centerIn: parent
                          source: CommonScript.jsBinding(imgItemIcon, "source", model ? model.modelData : undefined, imageSourceMember, "")
                      }
                  }
          
                  Label {
                      id: lblItemText
                      anchors.left: root.showIcon ? recImage.right : parent.left
                      anchors.right: parent.right
                      anchors.verticalCenter: parent.verticalCenter
                      anchors.leftMargin: font.pixelSize / 2
                      font.family: MDStyle.fontFamily
                      font.pixelSize: MDStyle.textFontSize
                      property bool bEnable: CommonScript.jsBinding(lblItemText, "bEnable", model ? model.modelData : undefined, "Enable", true)
                      color: bEnable ? MDStyle.fontColor : MDStyle.fontColorDisable
                      clip: true
                      text: CommonScript.jsBinding(lblItemText, "text", model ? model.modelData : undefined, displayMember, "")
                  }
              }
          
              onCurrentIndexChanged: {
                  if (listItems.selectedIndex != currentIndex) listItems.selectedIndex = currentIndex
              }
          
              onSelectedValueChanged: {
                  updateCurrentIndex()
              }
          
              onModelChanged: {
                  updateCurrentIndex()
              }
          
              function updateCurrentIndex() {
                  if (!selectedValue) {
                      displayText = CommonScript.VariesText
                  }
                  else if (selectedValue === undefined) {
                      console.log("Selected Value is undefined")
                  }
                  else {
                      console.log("Selected Value: " + selectedValue)
                  }
          
                  displayText = ""
          
                  if (root.currentIndex >= 0) {
                      if (model[root.currentIndex].binding(valueMember) === selectedValue) return;
                  }
          
                  for (var i = 0; i < model.length; i++) {
                      if (model[i].binding(valueMember) === selectedValue) {
                          root.currentIndex = i;
                          return;
                      }
                  }
              }
          }
          
          
          1 Reply Last reply
          0

          1/4

          5 Jul 2016, 04:54

          • Login

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