status-desktop/storybook/pages/SubmodelProxyModelPage.qml
Michał Cieślak d4d72038bd feat(StatusQ): Generic proxy model allowing setting proxies like SFPM or LeftJoinModel for submodels
It allows transforming model, including submodels outside of the view
component consuming the model. Thanks to that now there is no need
to create proxies for submodels in view's delegate. It can be done
earlies, on the level of the main view.

Closes: #12630
2023-11-06 12:26:04 +01:00

272 lines
7.0 KiB
QML

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import StatusQ 0.1
import Storybook 1.0
import SortFilterProxyModel 0.2
Item {
id: root
readonly property int numberOfTokens: 2000
readonly property var colors: [
"purple", "lightgreen", "red", "blue", "darkgreen"
]
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
ListModel {
id: networksModel
ListElement {
chainId: "1"
name: "Mainnet"
color: "purple"
}
ListElement {
chainId: "2"
name: "Optimism"
color: "lightgreen"
}
ListElement {
chainId: "3"
name: "Status"
color: "red"
}
ListElement {
chainId: "4"
name: "Abitrum"
color: "blue"
}
ListElement {
chainId: "5"
name: "Sepolia"
color: "darkgreen"
}
}
ListModel {
id: tokensModel
Component.onCompleted: {
// Populate model with given number of tokens containing random
// balances
const numberOfTokens = root.numberOfTokens
const tokens = []
const chainIds = []
for (let n = 0; n < networksModel.count; n++)
chainIds.push(networksModel.get(n).chainId)
for (let i = 0; i < numberOfTokens; i++) {
const balances = []
const numberOfBalances = 1 + getRandomInt(networksModel.count)
const chainIdsCpy = [...chainIds]
for (let i = 0; i < numberOfBalances; i++) {
const chainId = chainIdsCpy.splice(
getRandomInt(chainIdsCpy.length), 1)[0]
balances.push({
chainId: chainId,
balance: 1 + getRandomInt(200)
})
}
tokens.push({ name: `Token ${i + 1}`, balances })
}
append(tokens)
}
}
// Proxy model joining networksModel to submodels under "balances" role.
// Additionally submodel is filtered and sorted via SFPM. Submodel is
// accessible via "submodel" context property.
SubmodelProxyModel {
id: submodelProxyModel
sourceModel: tokensModel
delegateModel: SortFilterProxyModel {
readonly property LeftJoinModel joinModel: LeftJoinModel {
leftModel: submodel
rightModel: networksModel
joinRole: "chainId"
}
sourceModel: joinModel
filters: ExpressionFilter {
expression: balance >= thresholdSlider.value
}
sorters: RoleSorter {
roleName: "name"
enabled: sortCheckBox.checked
}
}
submodelRoleName: "balances"
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
// ListView consuming model don't have to do any transformation
// of the submodels internally because it's handled externally via
// SubmodelProxyModel.
ListView {
id: listView
Layout.fillWidth: true
Layout.fillHeight: true
reuseItems: true
ScrollBar.vertical: ScrollBar {}
clip: true
spacing: 18
model: submodelProxyModel
delegate: ColumnLayout {
id: delegateRoot
width: ListView.view.width
height: 46
spacing: 0
readonly property var balances: model.balances
Label {
Layout.fillWidth: true
text: model.name
font.bold: true
}
RowLayout {
spacing: 14
Layout.fillWidth: true
Repeater {
model: delegateRoot.balances
Rectangle {
width: label.implicitWidth * 1.5
height: label.implicitHeight * 2
color: "transparent"
border.width: 2
border.color: model.color
Label {
id: label
anchors.centerIn: parent
text: `${model.name} (${model.balance})`
font.pixelSize: 10
}
}
}
}
}
}
Rectangle {
Layout.preferredWidth: 1
Layout.fillHeight: true
Layout.rightMargin: 20
color: "lightgray"
}
ListView {
Layout.preferredWidth: 150
Layout.fillHeight: true
spacing: 20
model: networksModel
delegate: ColumnLayout {
width: ListView.view.width
Label {
Layout.fillWidth: true
text: model.name
font.bold: true
}
Rectangle {
Layout.preferredWidth: changeColorButton.width
Layout.preferredHeight: 10
color: model.color
}
Button {
id: changeColorButton
text: "Change color"
onClicked: {
const currentIdx = root.colors.indexOf(model.color)
const numberOfColors = root.colors.length
const nextIdx = (currentIdx + 1) % numberOfColors
networksModel.setProperty(model.index, "color",
root.colors[nextIdx])
}
}
}
}
}
MenuSeparator {
Layout.fillWidth: true
}
RowLayout {
Label {
text: `Number of tokens: ${listView.count}, minimum balance:`
}
Slider {
id: thresholdSlider
from: 0
to: 201
stepSize: 1
}
Label {
text: thresholdSlider.value
}
CheckBox {
id: sortCheckBox
text: "sort networks by name"
}
}
}
}
// category: Models