我正在创建一个支持多项选择的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']
}
}
单击任一列表不会将所选行变为粉红色,按向上/向下键不会调整选择。
如何将焦点传递给特定的ListView
,以便(a)一次只能有一个ListView,以及(b)我可以检测到ListView何时保持此焦点,并且(c) )它只允许键盘信号在聚焦时才能用于ListView?
我正在使用Qt 5.7,以防万一。
我尝试过的事情,但未成功:
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处理程序放在该项目上,并尝试将该项目作为焦点。
interactive:false
。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
被授予,空间正常工作。也许我上面的问题是因为ListView
是FocusScope
(根据文档)。
编辑3 :根据下面的每个Mitch的评论,在focus:true
上设置FocusScope
也可以让所有内容与FocusScope一起正常运行。但是,根据我在下面的回答(以及Mitch的评论),FocusScope不一定能让我的简化示例应用程序正常工作。
答案 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']
}