272 lines
7.0 KiB
QML
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
|