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:
Michał Cieślak 2024-01-22 15:11:28 +00:00 committed by Michał
parent daecb836ac
commit 62857410e6
4 changed files with 354 additions and 0 deletions

View File

@ -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

View File

@ -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
}
}

View File

@ -1,6 +1,7 @@
module StatusQ.Core.Utils
ClippingWrapper 0.1 ClippingWrapper.qml
DoubleFlickable 0.1 DoubleFlickable.qml
EmojiJSON 1.0 emojiList.js
JSONListModel 0.1 JSONListModel.qml
ModelChangeGuard 0.1 ModelChangeGuard.qml

View File

@ -208,6 +208,7 @@
<file>StatusQ/Core/Utils/ModelsComparator.qml</file>
<file>StatusQ/Core/Utils/ModelChangeTracker.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/StatusQrCodeScanner.qml</file>
<file>StatusQ/Components/StatusOnlineBadge.qml</file>