Flow
对象从左到右添加,但每一行都向右对齐。
import QtQuick 2.0
import QtQuick.Controls 2.0
Flow {
width: 400
anchors.right: parent.right
spacing: 2
Repeater {
model: ['a', 'b', 'c', 'd', 'e', 'f', 'g']
delegate: Button {
text: modelData
width: (83 * (model.index + 1)) % 100 + 30
}
}
}
我Flow
向右对齐,但其中的行将始终从Flow
的左边缘开始。我可以设置
layoutDirection: Qt.RightToLeft
这会将行向右对齐,但项目的顺序也会颠倒。
如果我反转模型(在这个例子中可以通过调用reverse()
,ListModel
我需要反转ProxyModel
(,a
会向左,但行顺序不对。
我怎么能实现这样的东西?
import QtQuick 2.5
import QtQuick.Controls 2.1
ApplicationWindow {
id: root;
width: 640
height: 480
visible: true
Rectangle {
z: -1
color: "#80000000"
width: flow.width
height: flow.height
anchors.centerIn: flow
}
Flow {
id: flow
width: 400
anchors.right: parent.right
spacing: 2
Repeater {
id: repeater
property real remainingRowSpace: flow.width
property real indexOfFirstItemInLastRow: 0
model: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q']
delegate: Rectangle {
property real buttonWidth: (83 * (model.index + 1)) % 100 + 30
color: "#80000000"
width: buttonWidth
height: childrenRect.height
Button {
x: parent.width - width
text: modelData
width: parent.buttonWidth
}
}
onItemAdded: {
var spacing = parent.spacing
// Check if new row.
if (item.width > remainingRowSpace) {
// Increase width of first item push row right.
itemAt(indexOfFirstItemInLastRow).width += remainingRowSpace
// Reset available space
remainingRowSpace = parent.width
// Store index of item
indexOfFirstItemInLastRow = index
// Don't need to subtract spacing for the first item
spacing = 0
}
remainingRowSpace -= (item.width + spacing)
// Handle when no more rows will be created.
if (index === repeater.count - 1) {
itemAt(indexOfFirstItemInLastRow).width += remainingRowSpace
}
}
}
}
}
这不是一个非常通用或动态的解决方案,但它有效。
这仍然是一项正在进行的工作,但可能仍然适用于某些人
就像托尼·克利夫顿(Tony Clifton(的解决方案一样,它使用Repeater
并且是onItemAdded
方法,但使用Qt.binding
来保持事物的动态性。它并不优于托尼·克利夫顿的解决方案,选择可能取决于用例。
优点:
- 动态更改单个
Item
宽度 - 动态到反向流的宽度
缺点:
- 首先填充底行。第一行可能没有满(对于某些用例,这可能更好(
当与ListModel
一起使用并且正在insert()
新条目时,这两种解决方案都不起作用。当模型被完全替换时,这两种解决方案都应该有效(例如,使用JSArray
s 和手动调用modelChanged()
(。
从某种意义上说,这两种解决方案都不Flow
,您可以在Repeater
处理布局时放入任意对象。因此,Repeater
必须创建Item
。我没有让它与ObjectModel
一起工作.摆脱中继器将是我最后关心的问题。
Item {
id: root
visible: false
width: 400
height: (rep.count > 0 ? rep.itemAt(0).y * -1 : 0)
anchors.centerIn: parent
property real spacing: 5
Item {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
Repeater {
id: rep
model: ListModel {
id: bm
ListElement { text: "a" }
ListElement { text: "b" }
ListElement { text: "c" }
ListElement { text: "d" }
ListElement { text: "e" }
ListElement { text: "f" }
ListElement { text: "g" }
ListElement { text: "h" }
ListElement { text: "i" }
}
onItemAdded: {
console.log(index, item, itemAt(index - 1))
item.anchors.right = Qt.binding(
function() {
if (rep.count === index + 1) return parent.right
var neighbor = rep.itemAt(index + 1)
if (neighbor.x - item.width - root.spacing < 0) return parent.right
return rep.itemAt(index + 1).left
}
)
item.anchors.bottom = Qt.binding(
function() {
if (rep.count === index + 1) return parent.bottom
var neighbor = rep.itemAt(index + 1)
if (neighbor.x - item.width - root.spacing < 0)
return neighbor.top
return neighbor.bottom
}
)
item.anchors.rightMargin = Qt.binding(
function() {
return (item.anchors.right === parent.right ? 0 : root.spacing)
}
)
item.anchors.bottomMargin = Qt.binding(
function() {
return (item.anchors.bottom === rep.itemAt(index + 1).top ? root.spacing : 0)
}
)
}
delegate: Button {
text: modelData
property int count: 0
width: (83 * (index + count + 1)) % 100 + 30
onClicked: {
count++
}
}
}
}
Rectangle {
id: debug
anchors.fill: parent
color: 'transparent'
border.color: 'red'
}
}
这背后的想法是在添加对象时初始化锚点的绑定,如果有足够的空间,我将每个Item
锚定到右侧或其后继者。
如果它紧挨着他,我还会将Item
的底部锚定在它的后继者底部,或者如果它开始新行,则锚定在它的顶部。如果它没有后继项,它将锚定到父项的底部。
后一个事实使得这个带有两个封闭Item
的奇怪结构是必要的。外部将真正包围一切,并动态增长,但这不可能是我锚定整个链条的那个,就像我锚定在底部一样。如果我根据childrenRect.height
更改height
,我将以具有奇怪效果的绑定循环结束,因为 Items 的 y 值不会保持不变。最初它会工作,但是当物品由于尺寸变化而移动时,它会破裂。