qt - 在 ListView 之间传递焦点

标签 qt focus qml qtquickcontrols

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

但是,给予focus=true ListView 委托(delegate)或 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,以防万一。

<小时/>

我尝试过但不成功的事情:

  • 设置focus=true在 Rectangle 而不是 ListView 上。
  • 设置 focused值得关注的特性ListView.view.focus而不是activeFocus :这允许两个列表同时变成粉红色。
  • 通读Keyboard Focus in Qt Quick并将 Rectangle 或 ListView 包装在 FocusScope 中。 (转发所有接口(interface)是多么痛苦的事情。)

    • 在白天重读该页面并将两个包装起来 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 包装在一个项目中,并将 Keys 处理程序放在该项目上,并尝试聚焦该项目。

  • 设置interactive:false在 ListView 上。
  • 设置keyNavigationEnabled:false在 ListView 上。 (这奇怪地产生 "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”没有显示,因为矩形没有 activeFocus 。此外,当其中任何一个获得焦点时,按 空格 永远不会记录消息。

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

编辑 3:根据下面 Mitch 的评论,设置 focus:true关于FocusScope还允许一切与 FocusScope 一起正常工作。但是,根据我下面的回答(以及 Mitch 的评论),FocusScope 并不是使我的两个简化示例应用程序正常工作所必需的。

最佳答案

如果您打印出原始示例中的事件焦点项目

onActiveFocusItemChanged: print(activeFocusItem)

你可以看到没有一个加载器获得焦点;它始终是窗口的根项目。

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

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']
    }
}

但是,现在当您单击第二个 View 的委托(delegate)时,它没有焦点。如果我们声明它们都具有焦点,那么我们就无法控制哪一个最终获得焦点。事实上,即使我们确实将它们声明为都具有焦点,单击第二个 View 的委托(delegate)也不会为该 View 提供事件焦点,因为当第一个加载器获得焦点时,它所在的加载器会失去焦点。换句话说,要使其工作,您还必须给予加载器焦点,这违背了拥有漂亮的小捆绑委托(delegate)组件的全部目的:

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']
    }
}

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

关于qt - 在 ListView 之间传递焦点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39324578/

相关文章:

QML 组件屏幕渲染

c++ - 当通过引用传递 QObject 时,C++ 信号的参数在 QML 中显示为 `undefined`

java - Swing JxMapViewer : focus on a object and set corresponding zoom level

c# - 如何了解哪个控件已被关注?

qt - 在 QML 中创建不透明动画

c++ - [Qt] : 'CButton' does not name a type

linux - 使用 wmcrtl 在同一应用程序的窗口中循环

c++ - 如何捕捉鼠标键盘操作

c++ - 如何从sqlite中获取一条记录并显示在tableview中

C++ (Qt),使用内存缓冲区而不是重定向的系统命令