单击 \ 轻拂一个 Flickable 的空白区域,该区域与另一个 Flickable 部分重叠
Click \ flick through emptry area of a Flickable that partially overlaps another Flickable
我有一个场景"editor"可以在底部滑动,并停靠在它的右侧,有一个场景"outliner"可以在它上面滑动,显示了场景结构树。
Flickable {
id: editor
anchors.fill: parent
contentWidth: 5000
contentHeight: 5000
Repeater {
model: 500
delegate: Rectangle {
width: Math.random() * 200 + 50
height: width
x: Math.random() * editor.contentWidth
y: Math.random() * editor.contentHeight
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
border.color: "black"
}
}
}
Flickable {
id: outliner
anchors.right: editor.right
width: contentWidth
height: parent.height
contentWidth: contentItem.childrenRect.width
contentHeight: contentItem.childrenRect.height
Column {
Repeater {
model: 500
delegate: Rectangle {
width: Math.random() * 200 + 50
x: outliner.contentWidth - width
height: 50
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
border.color: "black"
}
}
}
}
自然地,我希望能够轻拂两者来导航两者,因为它们都比可用的屏幕空间大。
问题是,即使我从大纲项目不占据的位置轻拂,大纲轻拂将简单地阻止编辑器轻拂。我不想这样,我想要的是仅当轻弹发生在大纲项目顶部时才轻弹大纲,否则我想跳到编辑器,这样我就可以单击并将其从该区域轻弹到对了。
由于 Flickable
的工作方式,我没能做到。它将首先检查拖动,只有当点击不超过拖动阈值时,它才会让点击下层元素。所以我想不出只有在那个位置有物体的情况下才轻弹的方法。
有什么方法可以让事件处理按照我的意愿进行吗?
您可以通过检查场景中的每个印刷机来做到这一点,如果印刷机下方有大纲代表。如果不是,请将 outliner
标记为非交互式。在 editor
.
中有移动后立即重新启用它
像这样:
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
ApplicationWindow {
id: app
visible: true
width: 600
height: 480
Item { // a common parent not containing the MouseArea to be able to call childAt on press
id: flickablesParent
anchors.fill: parent
Flickable {
id: editor
anchors.fill: parent
contentWidth: 5000
contentHeight: 5000
onMovementStarted: outliner.interactive = true //re-enable the outliner user interaction so it can be flicked again.
Repeater {
model: 500
delegate: Rectangle {
width: Math.random() * 200 + 50
height: width
x: Math.random() * editor.contentWidth
y: Math.random() * editor.contentHeight
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
border.color: "black"
}
}
}
Flickable {
id: outliner
anchors.right: editor.right
width: contentWidth
height: parent.height
contentWidth: contentItem.childrenRect.width
contentHeight: contentItem.childrenRect.height
Column {
id: column // set an id so it can be referenced in the MouseArea
Repeater {
model: 500
delegate: Rectangle {
width: Math.random() * 200 + 50
x: outliner.contentWidth - width
height: 50
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
border.color: "black"
}
}
}
}
}
MouseArea {
id: dispatcher
anchors.fill: parent
onPressed: {
var flickable = flickablesParent.childAt(mouse.x, mouse.y); //find out on what flickable we pressed (the check could be ommited but I guess it's more performant with)
if (flickable === outliner) {
var mappedPos = mapToItem(column, mouse.x, mouse.y);
var delegate = column.childAt(mappedPos.x, mappedPos.y);
if (!delegate)
outliner.interactive = false; //if there's no delegate where we pressed in the column, we disable the interactions of the outliner flickable.
}
mouse.accepted = false; // let the underlying Flickable deal with this event
}
}
}
编辑:您真的不需要在 onMovementStarted
中重新启用大纲视图,您也可以在 MouseArea onPressed
中重新启用。
喜欢
onPressed: {
var flickable = flickablesParent.childAt(mouse.x, mouse.y); //find out on what flickable we pressed (the check could be ommited but I guess it's more performant with)
if (flickable === outliner) {
var mappedPos = mapToItem(column, mouse.x, mouse.y);
var delegate = column.childAt(mappedPos.x, mappedPos.y);
outliner.interactive = delegate ? true : false;
}
mouse.accepted = false; // let the underlying Flickable deal with this event
}
Mkay, I think I did it, the key was to disable the interactivity for the outliner, then intercept input via a MouseArea
in the delegate, then carefully time and measure the manual dragging delta and if fast enough, schedule a manual flick on release.
Getting it to both stop an ongoing flick on press and subsequently launching another flick from the next drag turned out to be problematic after a flick has just been cancelled, but by delaying each flick to the next event loop cycle I got it working.
I also added wheel support and such.
If anyone experiences problems with it on any platform, do let me know in the comments.
// the editor code is the same from the OP
Flickable {
id: outliner
anchors.right: editor.right
width: contentWidth
height: parent.height
contentWidth: contentItem.childrenRect.width
contentHeight: contentItem.childrenRect.height
interactive: false
property int ly: 0
property real lt: 0
property int dy: 0
function go(yy) {
dy = contentY - (contentY -= yy - ly)
ly = yy
lt = Date.now()
}
Timer {
id: trigger
interval: 1
onTriggered: outliner.flick(0, outliner.dy * 150)
}
Column {
Repeater {
model: 500
delegate: Rectangle {
width: Math.random() * 200 + 50
x: outliner.contentWidth - width
height: 50
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
border.color: "black"
MouseArea {
anchors.fill: parent
Drag.active: drag.active
drag.target: Item { id: dummy }
onPressed: {
dummy.y = 0
if (outliner.flicking) outliner.cancelFlick()
}
onWheel: outliner.flick(0, wheel.angleDelta.y * 10)
onPositionChanged: {
if (drag.active) outliner.go(dummy.y)
}
onReleased: {
if (Date.now() - outliner.lt < 50) trigger.start()
outliner.ly = 0
outliner.returnToBounds()
}
}
}
}
}
}
我有一个场景"editor"可以在底部滑动,并停靠在它的右侧,有一个场景"outliner"可以在它上面滑动,显示了场景结构树。
Flickable {
id: editor
anchors.fill: parent
contentWidth: 5000
contentHeight: 5000
Repeater {
model: 500
delegate: Rectangle {
width: Math.random() * 200 + 50
height: width
x: Math.random() * editor.contentWidth
y: Math.random() * editor.contentHeight
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
border.color: "black"
}
}
}
Flickable {
id: outliner
anchors.right: editor.right
width: contentWidth
height: parent.height
contentWidth: contentItem.childrenRect.width
contentHeight: contentItem.childrenRect.height
Column {
Repeater {
model: 500
delegate: Rectangle {
width: Math.random() * 200 + 50
x: outliner.contentWidth - width
height: 50
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
border.color: "black"
}
}
}
}
自然地,我希望能够轻拂两者来导航两者,因为它们都比可用的屏幕空间大。
问题是,即使我从大纲项目不占据的位置轻拂,大纲轻拂将简单地阻止编辑器轻拂。我不想这样,我想要的是仅当轻弹发生在大纲项目顶部时才轻弹大纲,否则我想跳到编辑器,这样我就可以单击并将其从该区域轻弹到对了。
由于 Flickable
的工作方式,我没能做到。它将首先检查拖动,只有当点击不超过拖动阈值时,它才会让点击下层元素。所以我想不出只有在那个位置有物体的情况下才轻弹的方法。
有什么方法可以让事件处理按照我的意愿进行吗?
您可以通过检查场景中的每个印刷机来做到这一点,如果印刷机下方有大纲代表。如果不是,请将 outliner
标记为非交互式。在 editor
.
像这样:
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
ApplicationWindow {
id: app
visible: true
width: 600
height: 480
Item { // a common parent not containing the MouseArea to be able to call childAt on press
id: flickablesParent
anchors.fill: parent
Flickable {
id: editor
anchors.fill: parent
contentWidth: 5000
contentHeight: 5000
onMovementStarted: outliner.interactive = true //re-enable the outliner user interaction so it can be flicked again.
Repeater {
model: 500
delegate: Rectangle {
width: Math.random() * 200 + 50
height: width
x: Math.random() * editor.contentWidth
y: Math.random() * editor.contentHeight
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
border.color: "black"
}
}
}
Flickable {
id: outliner
anchors.right: editor.right
width: contentWidth
height: parent.height
contentWidth: contentItem.childrenRect.width
contentHeight: contentItem.childrenRect.height
Column {
id: column // set an id so it can be referenced in the MouseArea
Repeater {
model: 500
delegate: Rectangle {
width: Math.random() * 200 + 50
x: outliner.contentWidth - width
height: 50
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
border.color: "black"
}
}
}
}
}
MouseArea {
id: dispatcher
anchors.fill: parent
onPressed: {
var flickable = flickablesParent.childAt(mouse.x, mouse.y); //find out on what flickable we pressed (the check could be ommited but I guess it's more performant with)
if (flickable === outliner) {
var mappedPos = mapToItem(column, mouse.x, mouse.y);
var delegate = column.childAt(mappedPos.x, mappedPos.y);
if (!delegate)
outliner.interactive = false; //if there's no delegate where we pressed in the column, we disable the interactions of the outliner flickable.
}
mouse.accepted = false; // let the underlying Flickable deal with this event
}
}
}
编辑:您真的不需要在 onMovementStarted
中重新启用大纲视图,您也可以在 MouseArea onPressed
中重新启用。
喜欢
onPressed: {
var flickable = flickablesParent.childAt(mouse.x, mouse.y); //find out on what flickable we pressed (the check could be ommited but I guess it's more performant with)
if (flickable === outliner) {
var mappedPos = mapToItem(column, mouse.x, mouse.y);
var delegate = column.childAt(mappedPos.x, mappedPos.y);
outliner.interactive = delegate ? true : false;
}
mouse.accepted = false; // let the underlying Flickable deal with this event
}
Mkay, I think I did it, the key was to disable the interactivity for the outliner, then intercept input via a MouseArea
in the delegate, then carefully time and measure the manual dragging delta and if fast enough, schedule a manual flick on release.
Getting it to both stop an ongoing flick on press and subsequently launching another flick from the next drag turned out to be problematic after a flick has just been cancelled, but by delaying each flick to the next event loop cycle I got it working.
I also added wheel support and such.
If anyone experiences problems with it on any platform, do let me know in the comments.
// the editor code is the same from the OP
Flickable {
id: outliner
anchors.right: editor.right
width: contentWidth
height: parent.height
contentWidth: contentItem.childrenRect.width
contentHeight: contentItem.childrenRect.height
interactive: false
property int ly: 0
property real lt: 0
property int dy: 0
function go(yy) {
dy = contentY - (contentY -= yy - ly)
ly = yy
lt = Date.now()
}
Timer {
id: trigger
interval: 1
onTriggered: outliner.flick(0, outliner.dy * 150)
}
Column {
Repeater {
model: 500
delegate: Rectangle {
width: Math.random() * 200 + 50
x: outliner.contentWidth - width
height: 50
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
border.color: "black"
MouseArea {
anchors.fill: parent
Drag.active: drag.active
drag.target: Item { id: dummy }
onPressed: {
dummy.y = 0
if (outliner.flicking) outliner.cancelFlick()
}
onWheel: outliner.flick(0, wheel.angleDelta.y * 10)
onPositionChanged: {
if (drag.active) outliner.go(dummy.y)
}
onReleased: {
if (Date.now() - outliner.lt < 50) trigger.start()
outliner.ly = 0
outliner.returnToBounds()
}
}
}
}
}
}