在 ListView 之间传递焦点
Passing focus between ListView
我正在创建一个支持多选的 QML ListView
,并将在我的应用程序中实例化其中的多个。我需要为特定列表提供键盘焦点,处理该列表的按键,并根据焦点为选定的行绘制突出显示。
但是,将 focus=true
赋予 ListView 委托或 ListView 本身不会导致 activeFocus
出现在 ListView 上,也不会导致触发按键信号。
我创建了一个简单的示例应用来展示我的问题:
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']
}
}
单击任一列表不会将所选行变成粉红色,并且按 up/down 键不会调整选择。
如何将焦点传递给特定的 ListView
以便 (a) 一次只有一个 ListView 持有它,并且 (b) 我可以检测到 ListView 何时持有此焦点,并且 (c) 它允许键盘信号仅在聚焦时为该 ListView 工作?
我正在使用 Qt 5.7,以防万一。
我尝试过但未成功的事情:
- 在 Rectangle 而不是 ListView 上设置
focus=true
。
- 将
focused
属性 设置为监视 ListView.view.focus
而不是 activeFocus
:这允许两个列表同时变为粉红色。
通读 Keyboard Focus in Qt Quick 并将 Rectangle 或 ListView 包装在 FocusScope
中。 (好痛苦,转发所有接口。)
在白天重新阅读页面并将两个 Loader
合并为一个 FocusScope
。诅咒,因为两个 ListView 显然都允许同时获得焦点,这违反了我对文档这一部分的阅读:
Within each focus scope one object may have Item::focus
set to true. If more than one Item has the focus
property set, the last type to set the focus will have the focus
and the others are unset, similar to when there are no focus scopes.
将 ListView 包装在项目中并将键处理程序放在该项目上,并尝试聚焦该项目。
- 在 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')
}
}
}
点击每个矩形使其变为红色,并且在任何给定时间只允许一个为红色。 (很好。)但是,文本 "focused" 没有出现,因为 Rectangle 没有 activeFocus
。此外,按下 space 永远不会在任何一个被聚焦时记录消息。
编辑 2:哇哦。如果我删除 FocusScope,它会按预期工作:焦点仍然是独占的,activeFocus
被授予,并且 space 正常工作。也许我上面的问题是因为 ListView
是 FocusScope
(根据文档)。
编辑 3:根据下面 Mitch 的评论,在 FocusScope
上设置 focus:true
还可以让一切都与 FocusScope 一起正常工作。但是,根据我在下面的回答(以及 Mitch 的评论),FocusScope 并不是让我的任何一个简化示例应用程序正常工作所必需的。
我原来的示例应用程序中的问题似乎与我使用 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']
}
如果您打印出原始示例中的活动焦点项
onActiveFocusItemChanged: print(activeFocusItem)
您可以看到 none 的加载程序获得了焦点;它始终是 window.
的根项
如果您在其中一个加载程序上设置 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']
}
}
我真的不喜欢对焦系统。我并不是说我可以想出更好的东西,但它并不容易使用。
我正在创建一个支持多选的 QML ListView
,并将在我的应用程序中实例化其中的多个。我需要为特定列表提供键盘焦点,处理该列表的按键,并根据焦点为选定的行绘制突出显示。
但是,将 focus=true
赋予 ListView 委托或 ListView 本身不会导致 activeFocus
出现在 ListView 上,也不会导致触发按键信号。
我创建了一个简单的示例应用来展示我的问题:
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']
}
}
单击任一列表不会将所选行变成粉红色,并且按 up/down 键不会调整选择。
如何将焦点传递给特定的 ListView
以便 (a) 一次只有一个 ListView 持有它,并且 (b) 我可以检测到 ListView 何时持有此焦点,并且 (c) 它允许键盘信号仅在聚焦时为该 ListView 工作?
我正在使用 Qt 5.7,以防万一。
我尝试过但未成功的事情:
- 在 Rectangle 而不是 ListView 上设置
focus=true
。 - 将
focused
属性 设置为监视ListView.view.focus
而不是activeFocus
:这允许两个列表同时变为粉红色。 通读 Keyboard Focus in Qt Quick 并将 Rectangle 或 ListView 包装在
FocusScope
中。 (好痛苦,转发所有接口。)在白天重新阅读页面并将两个
Loader
合并为一个FocusScope
。诅咒,因为两个 ListView 显然都允许同时获得焦点,这违反了我对文档这一部分的阅读:Within each focus scope one object may have
Item::focus
set to true. If more than one Item has thefocus
property set, the last type to set the focus will have thefocus
and the others are unset, similar to when there are no focus scopes.
将 ListView 包装在项目中并将键处理程序放在该项目上,并尝试聚焦该项目。
- 在 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')
}
}
}
点击每个矩形使其变为红色,并且在任何给定时间只允许一个为红色。 (很好。)但是,文本 "focused" 没有出现,因为 Rectangle 没有 activeFocus
。此外,按下 space 永远不会在任何一个被聚焦时记录消息。
编辑 2:哇哦。如果我删除 FocusScope,它会按预期工作:焦点仍然是独占的,activeFocus
被授予,并且 space 正常工作。也许我上面的问题是因为 ListView
是 FocusScope
(根据文档)。
编辑 3:根据下面 Mitch 的评论,在 FocusScope
上设置 focus:true
还可以让一切都与 FocusScope 一起正常工作。但是,根据我在下面的回答(以及 Mitch 的评论),FocusScope 并不是让我的任何一个简化示例应用程序正常工作所必需的。
我原来的示例应用程序中的问题似乎与我使用 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']
}
如果您打印出原始示例中的活动焦点项
onActiveFocusItemChanged: print(activeFocusItem)
您可以看到 none 的加载程序获得了焦点;它始终是 window.
的根项如果您在其中一个加载程序上设置 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']
}
}
我真的不喜欢对焦系统。我并不是说我可以想出更好的东西,但它并不容易使用。