feat(StatusQ): DoubleFlickable utility making two Flickables operating line one
The wrapper doesn't "unroll" the managed Flickables. There is no instantiation of delegats for all model entries up front.
This commit is contained in:
parent
daecb836ac
commit
62857410e6
|
@ -0,0 +1,238 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
|
|
||||||
|
import Storybook 1.0
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
|
||||||
|
readonly property int headerSize: 40
|
||||||
|
|
||||||
|
function fillModel(model, count) {
|
||||||
|
const content = []
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++)
|
||||||
|
content.push({})
|
||||||
|
|
||||||
|
model.clear()
|
||||||
|
model.append(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustModel(model, newCount) {
|
||||||
|
const countDiff = newCount - model.count
|
||||||
|
const randPos = () => Math.floor(Math.random() * model.count)
|
||||||
|
|
||||||
|
if (countDiff > 0) {
|
||||||
|
for (let i = 0; i < countDiff; i++)
|
||||||
|
model.insert(randPos(), {})
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < -countDiff; i++)
|
||||||
|
model.remove(randPos())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: firstModel
|
||||||
|
|
||||||
|
Component.onCompleted: fillModel(this, firstSlider.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: secondModel
|
||||||
|
|
||||||
|
Component.onCompleted: fillModel(this, secondSlider.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
SplitView.fillWidth: true
|
||||||
|
SplitView.fillHeight: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: frame
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
width: Math.round(parent.width / 2)
|
||||||
|
height: Math.round(parent.height / 2)
|
||||||
|
border.width: 1
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
DoubleFlickable {
|
||||||
|
id: doubleFlickable
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: clipCheckBox.checked
|
||||||
|
z: -1
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar { policy: ScrollBar.AlwaysOn }
|
||||||
|
|
||||||
|
flickable1: GridView {
|
||||||
|
width: frame.width
|
||||||
|
interactive: false
|
||||||
|
model: firstModel
|
||||||
|
|
||||||
|
cellWidth: 120
|
||||||
|
cellHeight: 30
|
||||||
|
|
||||||
|
header: Rectangle {
|
||||||
|
height: root.headerSize
|
||||||
|
width: GridView.view.width
|
||||||
|
|
||||||
|
color: "orange"
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
font.bold: true
|
||||||
|
text: "Community"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
width: GridView.view.cellWidth
|
||||||
|
height: GridView.view.cellHeight
|
||||||
|
|
||||||
|
border.color: "black"
|
||||||
|
color: "lightblue"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
border.color: "green"
|
||||||
|
border.width: 5
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flickable2: GridView {
|
||||||
|
width: frame.width
|
||||||
|
interactive: false
|
||||||
|
model: secondModel
|
||||||
|
|
||||||
|
cellWidth: 100
|
||||||
|
cellHeight: 100
|
||||||
|
|
||||||
|
header: Rectangle {
|
||||||
|
height: root.headerSize
|
||||||
|
width: GridView.view.width
|
||||||
|
|
||||||
|
color: "red"
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
font.bold: true
|
||||||
|
text: "Others"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
width: GridView.view.cellWidth
|
||||||
|
height: GridView.view.cellHeight
|
||||||
|
|
||||||
|
border.color: "black"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
border.color: "blue"
|
||||||
|
border.width: 5
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogsAndControlsPanel {
|
||||||
|
SplitView.minimumHeight: 100
|
||||||
|
SplitView.preferredHeight: 200
|
||||||
|
SplitView.fillWidth: true
|
||||||
|
|
||||||
|
Column {
|
||||||
|
CheckBox {
|
||||||
|
id: clipCheckBox
|
||||||
|
text: "clip"
|
||||||
|
checked: true
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Label {
|
||||||
|
text: "first model:"
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: firstSlider
|
||||||
|
from: 0
|
||||||
|
to: 200
|
||||||
|
stepSize: 1
|
||||||
|
|
||||||
|
value: 160
|
||||||
|
|
||||||
|
onValueChanged: adjustModel(firstModel, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
RoundButton {
|
||||||
|
text: "-"
|
||||||
|
onClicked: firstSlider.decrease()
|
||||||
|
}
|
||||||
|
|
||||||
|
RoundButton {
|
||||||
|
text: "+"
|
||||||
|
onClicked: firstSlider.increase()
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: firstSlider.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Label {
|
||||||
|
text: "second model:"
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: secondSlider
|
||||||
|
from: 0
|
||||||
|
to: 100
|
||||||
|
stepSize: 1
|
||||||
|
|
||||||
|
value: 90
|
||||||
|
|
||||||
|
onValueChanged: adjustModel(secondModel, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
RoundButton {
|
||||||
|
text: "-"
|
||||||
|
onClicked: secondSlider.decrease()
|
||||||
|
}
|
||||||
|
|
||||||
|
RoundButton {
|
||||||
|
text: "+"
|
||||||
|
onClicked: secondSlider.increase()
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: secondSlider.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// category: Components
|
|
@ -0,0 +1,114 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
maximumFlickVelocity: 2000
|
||||||
|
synchronousDrag: true
|
||||||
|
|
||||||
|
property Flickable flickable1: Flickable {}
|
||||||
|
property Flickable flickable2: Flickable {}
|
||||||
|
|
||||||
|
readonly property real flickable1ContentHeight: flickable1.contentHeight
|
||||||
|
readonly property real flickable2ContentHeight: flickable2.contentHeight
|
||||||
|
|
||||||
|
onWidthChanged: returnToBounds()
|
||||||
|
onHeightChanged: returnToBounds()
|
||||||
|
|
||||||
|
contentWidth: root.width
|
||||||
|
contentHeight: flickable1ContentHeight + flickable2ContentHeight
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
property real offsetY1
|
||||||
|
property real offsetY2
|
||||||
|
|
||||||
|
Binding on offsetY1 {
|
||||||
|
value: flickable1.originY
|
||||||
|
delayed: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding on offsetY2 {
|
||||||
|
value: flickable2.originY
|
||||||
|
delayed: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First flickable
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: flickable1
|
||||||
|
property: "parent"
|
||||||
|
value: contentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: flickable1
|
||||||
|
property: "interactive"
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: flickable1
|
||||||
|
property: "height"
|
||||||
|
value: Math.min(root.height, flickable1ContentHeight)
|
||||||
|
delayed: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: flickable1
|
||||||
|
property: "y"
|
||||||
|
value: Math.min(Math.max(0, root.contentY),
|
||||||
|
flickable1ContentHeight - flickable1.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: flickable1
|
||||||
|
property: "contentY"
|
||||||
|
value: Math.min(Math.max(root.contentY, 0),
|
||||||
|
flickable1ContentHeight - flickable1.height) + d.offsetY1
|
||||||
|
|
||||||
|
delayed: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second flickable
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: flickable2
|
||||||
|
property: "parent"
|
||||||
|
value: contentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: flickable2
|
||||||
|
property: "interactive"
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: flickable2
|
||||||
|
property: "height"
|
||||||
|
value: Math.min(root.height, flickable2ContentHeight)
|
||||||
|
|
||||||
|
delayed: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: flickable2
|
||||||
|
property: "y"
|
||||||
|
value: Math.min(Math.max(flickable1ContentHeight, root.contentY),
|
||||||
|
root.contentHeight - flickable2.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: flickable2
|
||||||
|
property: "contentY"
|
||||||
|
value: Math.min(Math.max(0, root.contentY - flickable1ContentHeight),
|
||||||
|
flickable2ContentHeight - flickable2.height) + d.offsetY2
|
||||||
|
|
||||||
|
delayed: true
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
module StatusQ.Core.Utils
|
module StatusQ.Core.Utils
|
||||||
|
|
||||||
ClippingWrapper 0.1 ClippingWrapper.qml
|
ClippingWrapper 0.1 ClippingWrapper.qml
|
||||||
|
DoubleFlickable 0.1 DoubleFlickable.qml
|
||||||
EmojiJSON 1.0 emojiList.js
|
EmojiJSON 1.0 emojiList.js
|
||||||
JSONListModel 0.1 JSONListModel.qml
|
JSONListModel 0.1 JSONListModel.qml
|
||||||
ModelChangeGuard 0.1 ModelChangeGuard.qml
|
ModelChangeGuard 0.1 ModelChangeGuard.qml
|
||||||
|
|
|
@ -208,6 +208,7 @@
|
||||||
<file>StatusQ/Core/Utils/ModelsComparator.qml</file>
|
<file>StatusQ/Core/Utils/ModelsComparator.qml</file>
|
||||||
<file>StatusQ/Core/Utils/ModelChangeTracker.qml</file>
|
<file>StatusQ/Core/Utils/ModelChangeTracker.qml</file>
|
||||||
<file>StatusQ/Core/Utils/StringUtils.qml</file>
|
<file>StatusQ/Core/Utils/StringUtils.qml</file>
|
||||||
|
<file>StatusQ/Core/Utils/DoubleFlickable.qml</file>
|
||||||
<file>StatusQ/Components/StatusPageIndicator.qml</file>
|
<file>StatusQ/Components/StatusPageIndicator.qml</file>
|
||||||
<file>StatusQ/Components/StatusQrCodeScanner.qml</file>
|
<file>StatusQ/Components/StatusQrCodeScanner.qml</file>
|
||||||
<file>StatusQ/Components/StatusOnlineBadge.qml</file>
|
<file>StatusQ/Components/StatusOnlineBadge.qml</file>
|
||||||
|
|
Loading…
Reference in New Issue