在ListView

时间:2016-09-05 05:50:18

标签: qt focus qml qtquickcontrols

我正在创建一个支持多项选择的QML ListView,并将在我的应用程序中实例化多个这些。我需要将键盘焦点放在特定列表上,处理该列表的按键,并根据焦点绘制所选行的突出显示。

但是,将focus=true赋予ListView委托或ListView本身不会导致activeFocus出现在ListView上,也不会导致按键信号被触发。

我创建了一个简单的示例应用程序来显示我的问题:

enter image description here

import QtQuick 2.7
import QtQuick.Window 2.0

Window {
  id:window; visible:true; width:400; height:160; color:'darkgray'

  Component {
    id: row
    Rectangle {
      id: root
      property int  i: index
      property bool current: ListView.isCurrentItem
      property bool focused: ListView.view.activeFocus
      width:parent.width; height:20
      color: current ? ( focused ? 'pink' : 'lightblue' ) : 'lightgray'
      MouseArea {
        anchors.fill: parent
        onClicked: {
          root.ListView.view.currentIndex = i;
          root.ListView.view.focus = true;
        }
      }
      Text { anchors.fill:parent; text:modelData }
    }
  }

  Component {
    id: myList
    ListView {
      delegate: row
      width:window.width/2-10; height:window.height; y:5
      Keys.onUpPressed:   decrementCurrentIndex()
      Keys.onDownPressed: incrementCurrentIndex()
    }
  }

  Loader {
    sourceComponent:myList; x:5
    onLoaded: item.model = ['a','b','c','d']
  }
  Loader {
    sourceComponent:myList; x:window.width/2
    onLoaded: item.model = ['1','2','3','4','5']
  }
}

单击任一列表不会将所选行变为粉红色,按向上/向下键不会调整选择。

如何将焦点传递给特定的ListView,以便(a)一次只能有一个ListView,以及(b)我可以检测到ListView何时保持此焦点,并且(c) )它只允许键盘信号在聚焦时才能用于ListView?

我正在使用Qt 5.7,以防万一。

我尝试过的事情,但未成功:

  • 在Rectangle而不是ListView上设置focus=true
  • 设置focused属性以关注ListView.view.focus而不是activeFocus:这样两个列表都可以同时变为粉红色。
  • 阅读Keyboard Focus in Qt Quick并在FocusScope中包装Rectangle或ListView。 (多么痛苦,转发所有接口。)

    • 在白天重新阅读该页面并将两个Loader包装在一个FocusScope中。诅咒,因为两个ListView显然都被允许同时关注,违反了我对文档这一部分的阅读:

        

      在每个焦点范围内,一个对象可能将Item::focus设置为true。如果多个Item设置了focus属性,则设置焦点的最后一个类型将具有focus而其他类型未设置,类似于没有焦点范围时。

  • 在项目中包装ListView并将Keys处理程序放在该项目上,并尝试将该项目作为焦点。

  • 在ListView上设置interactive:false
  • 在ListView上设置keyNavigationEnabled:false。 (这奇怪地产生"ListView.keyNavigationEnabled" is not available in QtQuick 2.7,尽管帮助声明它是在5.7中引入的,并且暗示QtQuick 2.7是正确的导入语句。)
  • focus:true放在我能找到的每个对象上,以防万一。

注意:我意识到标准ListView使用highlight来显示选择,使用键盘导航来调整currentItem。但是,因为我的需要需要多个同时选择的项目,所以我必须专门管理高亮和键盘。

修改:这是一个更简单的测试用例,表现不像我预期的那样:

import QtQuick 2.7
import QtQuick.Window 2.0

Window {
  id:window; visible:true; width:400; height:160; color:'darkgray'

  FocusScope {
    Rectangle {
      width: window.width/2-6; height:window.height-8; x:4; y:4
      color: focus ? 'red' : 'gray'
      MouseArea { anchors.fill:parent; onClicked:parent.focus=true }
      Text { text:'focused'; visible:parent.activeFocus }
      Keys.onSpacePressed: console.log('space left')
    }
    Rectangle {
      width: window.width/2-6; height:window.height-8; x:window.width/2+2; y:4
      color: focus ? 'red' : 'gray'
      MouseArea { anchors.fill:parent; onClicked:parent.focus=true }
      Text { text:'focused'; visible:parent.activeFocus }
      Keys.onSpacePressed: console.log('space right')
    }
  }
}

单击每个矩形使其变为红色,并且在任何给定时间只允许一个为红色。 (这很好。)然而,文字"聚焦"没有显示,因为Rectangle没有activeFocus。此外,按 space 从不记录任何一个消息。

编辑2 :哇。如果我删除了FocusScope,它会按预期工作:焦点仍然是独占的,activeFocus被授予,空间正常工作。也许我上面的问题是因为ListViewFocusScope(根据文档)。

编辑3 :根据下面的每个Mitch的评论,在focus:true上设置FocusScope也可以让所有内容与FocusScope一起正常运行。但是,根据我在下面的回答(以及Mitch的评论),FocusScope不一定能让我的简化示例应用程序正常工作。

2 个答案:

答案 0 :(得分:4)

如果您在原始示例中打印出活动焦点项目

onActiveFocusItemChanged: print(activeFocusItem)

你可以看到没有任何装载者得到关注;它始终是窗口的根项。

如果您在其中一个加载器上设置focus: true,那么它将具有焦点:

import QtQuick 2.7
import QtQuick.Window 2.2

Window {
    id: window
    visible: true
    width: 400
    height: 160
    color: 'darkgray'

    onActiveFocusItemChanged: print(activeFocusItem)

    Component {
        id: row
        Rectangle {
            id: root
            property int i: index
            property bool current: ListView.isCurrentItem
            property bool focused: ListView.view.activeFocus
            width: parent.width
            height: 20
            color: current ? (focused ? 'pink' : 'lightblue') : 'lightgray'

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    root.ListView.view.currentIndex = i
                    root.ListView.view.focus = true
                }
            }
            Text {
                anchors.fill: parent
                text: modelData
            }
        }
    }

    Component {
        id: myList
        ListView {
            delegate: row
            width: window.width / 2 - 10
            height: window.height
            y: 5
            Keys.onUpPressed: decrementCurrentIndex()
            Keys.onDownPressed: incrementCurrentIndex()
        }
    }

    Loader {
        objectName: "loader1"
        sourceComponent: myList
        focus: true
        x: 5
        onLoaded: item.model = ['a', 'b', 'c', 'd']
    }
    Loader {
        objectName: "loader2"
        sourceComponent: myList
        x: window.width / 2
        onLoaded: item.model = ['1', '2', '3', '4', '5']
    }
}

但是,现在第二个视图在您单击其委托时没有焦点。如果我们宣称它们都具有焦点,我们将无法控制哪一个最终得到它。实际上,即使我们确实将它们声明为具有焦点,单击第二个视图的委托也不会给该视图提供主动焦点,因为当第一个加载器获取它时,它处于丢失焦点的加载器。换句话说,为了使它能够工作,你必须同时给出 loader 焦点,这会破坏拥有很好的小捆绑代理组件的整个目的:

import QtQuick 2.7
import QtQuick.Window 2.2

Window {
    id: window
    visible: true
    width: 400
    height: 160
    color: 'darkgray'

    onActiveFocusItemChanged: print(activeFocusItem)

    Component {
        id: row
        Rectangle {
            id: root
            property int i: index
            property bool current: ListView.isCurrentItem
            property bool focused: ListView.view.activeFocus
            width: parent.width
            height: 20
            color: current ? (focused ? 'pink' : 'lightblue') : 'lightgray'

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    root.ListView.view.currentIndex = i
                    root.ListView.view.parent.focus = true
                    root.ListView.view.focus = true
                }
            }
            Text {
                anchors.fill: parent
                text: modelData
            }
        }
    }

    Component {
        id: myList
        ListView {
            delegate: row
            width: window.width / 2 - 10
            height: window.height
            y: 5
            Keys.onUpPressed: decrementCurrentIndex()
            Keys.onDownPressed: incrementCurrentIndex()
        }
    }

    Loader {
        objectName: "loader1"
        sourceComponent: myList
        x: 5
        onLoaded: item.model = ['a', 'b', 'c', 'd']
    }
    Loader {
        objectName: "loader2"
        sourceComponent: myList
        x: window.width / 2
        onLoaded: item.model = ['1', '2', '3', '4', '5']
    }
}

此时我会放弃并强制主动关注:

import QtQuick 2.7
import QtQuick.Window 2.2

Window {
    id: window
    visible: true
    width: 400
    height: 160
    color: 'darkgray'

    onActiveFocusItemChanged: print(activeFocusItem)

    Component {
        id: row
        Rectangle {
            id: root
            objectName: ListView.view.objectName + "Rectangle" + index
            property int i: index
            property bool current: ListView.isCurrentItem
            property bool focused: ListView.view.activeFocus
            width: parent.width
            height: 20
            color: current ? (focused ? 'pink' : 'lightblue') : 'lightgray'
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    root.ListView.view.currentIndex = i
                    root.ListView.view.forceActiveFocus()
                }
            }
            Text {
                anchors.fill: parent
                text: modelData
            }
        }
    }

    Component {
        id: myList
        ListView {
            objectName: parent.objectName + "ListView"
            delegate: row
            width: window.width / 2 - 10
            height: window.height
            y: 5
            Keys.onUpPressed: decrementCurrentIndex()
            Keys.onDownPressed: incrementCurrentIndex()
        }
    }

    Loader {
        id: loader1
        objectName: "loader1"
        sourceComponent: myList
        x: 5
        onLoaded: item.model = ['a', 'b', 'c', 'd']
    }
    Loader {
        objectName: "loader2"
        sourceComponent: myList
        x: window.width / 2
        onLoaded: item.model = ['1', '2', '3', '4', '5']
    }
}

我真的不喜欢焦点系统。我并不是说我能想出更好的东西,但它并不容易使用。

答案 1 :(得分:0)

原始示例应用中的问题似乎与我使用Loader有关。忽略Window并委托组件代码,用显式列表替换我的Loader代码有效:

ListView {
  width:window.width/2-6; height:window.height-8; x:4; y:4
  delegate: row
  model:    ['a','b','c','d']
  Keys.onUpPressed:   decrementCurrentIndex()
  Keys.onDownPressed: incrementCurrentIndex()
}
ListView {
  width:window.width/2-6; height:window.height-8; x:window.width/2+2; y:4
  delegate: row
  model:    ['1','2','3','4','5']
  Keys.onUpPressed:   decrementCurrentIndex()
  Keys.onDownPressed: incrementCurrentIndex()
}

...如果我使用基于文件的组件,它也有效:

<强> MyList.qml

import QtQuick 2.7
ListView {
    delegate: row
    width:window.width/2-6; height:window.height-8; y:4
    Keys.onUpPressed:   decrementCurrentIndex()
    Keys.onDownPressed: incrementCurrentIndex()
}

<强> main.qml

// ...Window and row component as in the question above..
MyList { x:4;                model:['a','b','c','d']      }
MyList { x:window.width/2+2; model: ['1','2','3','4','5'] }

...但此原始代码不起作用,因为永远不会ListView activeFocus

Component {
  id: myList
  ListView {
    delegate: row
    width:window.width/2-6; height:window.height-8; y:4
    Keys.onUpPressed:   decrementCurrentIndex()
    Keys.onDownPressed: incrementCurrentIndex()
  }
}
Loader {
  sourceComponent:myList; x:4
  onLoaded: item.model = ['a','b','c','d']
}
Loader {
  sourceComponent:myList; x:window.width/2+2
  onLoaded: item.model = ['1','2','3','4','5']
}