status-desktop/ui/app/AppLayouts/Wallet/controls/SortOrderComboBox.qml
Lukáš Tinkl 7183621369 feat: Manage tokens panel UI component & model
- implements the UI component to manage regular & community tokens
(drag'n'drop or context menu to reorder, show/hide, group by community)
- implements a custom C++ QAIM model which acts as a fake proxy model
for the above QML panel (internally it does all the
sorting/grouping/hiding and preserves the custom sort order)
- adds and corrects support for cascading submenus in StatusAction, and
StatusMenu[Item]
- adds support for mirrored (horizontally flipped) StatusSwitch
- adds a new SortOrderComboBox.qml (this was being used in the first
Figma version, can be ignored now, will be used by the main wallet view
later)
- some minor fixes and cleanups in the used components

Iterates #12377
Closes #12587
2023-11-09 11:45:32 +01:00

273 lines
9.8 KiB
QML

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Popups 0.1
import utils 1.0
ComboBox {
id: root
property int sortOrder: Qt.DescendingOrder
readonly property string currentSortRoleName: d.currentSortRoleName
model: d.predefinedSortModel
textRole: "text"
valueRole: "value"
displayText: !d.isCustomSortOrder ? "%1 %2".arg(currentText).arg(sortOrder === Qt.DescendingOrder ? "↓" : "↑")
: currentText
Component.onCompleted: currentIndex = indexOfValue(SortOrderComboBox.TokenOrderCustom)
enum TokenOrder {
TokenOrderNone = 0,
TokenOrderCustom,
TokenOrderValue,
TokenOrderBalance,
TokenOrder1WChange,
TokenOrderAlpha
}
horizontalPadding: 12
verticalPadding: 8
spacing: 8
font.family: Theme.palette.baseFont.name
font.pixelSize: Style.current.additionalTextSize
QtObject {
id: d
readonly property int defaultDelegateHeight: 34
// // models
// readonly property SortFilterProxyModel tokensModel: SortFilterProxyModel {
// sourceModel: root.baseModel
// proxyRoles: [
// ExpressionRole {
// name: "currentBalance"
// expression: model.enabledNetworkBalance.amount
// },
// ExpressionRole {
// name: "currentCurrencyBalance"
// expression: model.enabledNetworkCurrencyBalance.amount
// }
// ]
// sorters: RoleSorter {
// roleName: cmbTokenOrder.currentSortRoleName
// sortOrder: cmbTokenOrder.sortOrder
// enabled: !d.isCustomSortOrder
// }
// filters: ValueFilter {
// roleName: "visibleForNetworkWithPositiveBalance"
// value: true
// }
// }
readonly property var predefinedSortModel: [
{ value: SortOrderComboBox.TokenOrderValue, text: qsTr("Token value"), icon: "token-sale", sortRoleName: "currentCurrencyBalance" }, // custom SFPM ExpressionRole
{ value: SortOrderComboBox.TokenOrderBalance, text: qsTr("Token balance"), icon: "wallet", sortRoleName: "currentBalance" }, // custom SFPM ExpressionRole
{ value: SortOrderComboBox.TokenOrder1WChange, text: qsTr("1W change"), icon: "time", sortRoleName: "changePct24hour" }, // FIXME changePct1Week role missing in backend!!!
{ value: SortOrderComboBox.TokenOrderAlpha, text: qsTr("Alphabetic"), icon: "bold", sortRoleName: "name" },
{ value: SortOrderComboBox.TokenOrderNone, text: "---", icon: "", sortRoleName: "" },
{ value: SortOrderComboBox.TokenOrderCustom, text: qsTr("Custom order"), icon: "exchange", sortRoleName: "" }
]
readonly property string currentSortRoleName: root.currentIndex !== -1 ? d.predefinedSortModel[root.currentIndex].sortRoleName : ""
readonly property bool isCustomSortOrder: root.currentValue === SortOrderComboBox.TokenOrderCustom
}
background: Rectangle {
border.width: 1
border.color: Theme.palette.directColor7
radius: 8
color: root.down ? Theme.palette.baseColor2 : "transparent"
HoverHandler {
cursorShape: root.enabled ? Qt.PointingHandCursor : undefined
}
}
contentItem: StatusBaseText {
leftPadding: root.horizontalPadding
rightPadding: root.horizontalPadding
font.pixelSize: root.font.pixelSize
font.weight: Font.Medium
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
text: root.displayText
color: Theme.palette.baseColor1
}
indicator: StatusIcon {
x: root.mirrored ? root.horizontalPadding : root.width - width - root.horizontalPadding
y: root.topPadding + (root.availableHeight - height) / 2
width: 16
height: width
icon: "chevron-down"
color: Theme.palette.baseColor1
}
popup: Popup {
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
y: root.height + 4
implicitWidth: root.width
margins: 8
padding: 1
verticalPadding: 8
background: Rectangle {
color: Theme.palette.statusSelect.menuItemBackgroundColor
radius: 8
border.color: Theme.palette.baseColor2
layer.enabled: true
layer.effect: DropShadow {
horizontalOffset: 0
verticalOffset: 4
radius: 12
samples: 25
spread: 0.2
color: Theme.palette.dropShadow
}
}
contentItem: ColumnLayout {
StatusBaseText {
Layout.fillWidth: true
Layout.preferredHeight: d.defaultDelegateHeight
text: qsTr("Sort by")
font.pixelSize: Style.current.tertiaryTextFontSize
leftPadding: Style.current.padding
verticalAlignment: Qt.AlignVCenter
color: Theme.palette.baseColor1
}
StatusListView {
Layout.fillWidth: true
implicitWidth: contentWidth
implicitHeight: contentHeight
model: root.popup.visible ? root.delegateModel : null
currentIndex: root.highlightedIndex
}
}
}
Component {
id: regularMenuComponent
RowLayout {
spacing: root.spacing
StatusIcon {
visible: !!icon
icon: iconName
color: root.enabled ? Theme.palette.primaryColor1 : Theme.palette.baseColor1
width: 16
height: 16
}
StatusBaseText {
Layout.fillWidth: true
Layout.fillHeight: true
text: menuText
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
color: root.enabled ? Theme.palette.directColor1 : Theme.palette.baseColor1
font.pixelSize: root.font.pixelSize
font.weight: root.currentIndex === menuIndex ? Font.DemiBold : Font.Normal
}
Item { Layout.fillWidth: true }
Row {
visible: !isCustomOrder
spacing: 4
StatusFlatRoundButton {
radius: 6
width: 24
height: 24
icon.name: "arrow-up"
icon.width: 18
icon.height: 18
opacity: root.highlightedIndex === menuIndex || highlighted // not "visible, we want the item to stay put
highlighted: root.currentIndex === menuIndex && root.sortOrder === Qt.AscendingOrder
onClicked: {
if (root.currentIndex !== menuIndex)
root.currentIndex = menuIndex
root.sortOrder = Qt.AscendingOrder
root.popup.close()
}
}
StatusFlatRoundButton {
radius: 6
width: 24
height: 24
icon.name: "arrow-down"
icon.width: 18
icon.height: 18
opacity: root.highlightedIndex === menuIndex || highlighted // not "visible, we want the item to stay put
highlighted: root.currentIndex === menuIndex && root.sortOrder === Qt.DescendingOrder
onClicked: {
if (root.currentIndex !== menuIndex)
root.currentIndex = menuIndex
root.sortOrder = Qt.DescendingOrder
root.popup.close()
}
}
}
}
}
Component {
id: separatorMenuComponent
StatusMenuSeparator {}
}
delegate: ItemDelegate {
required property int index
required property var modelData
readonly property bool isSeparator: text === "---"
id: menuDelegate
width: root.width
highlighted: root.highlightedIndex === index
enabled: !isSeparator
leftPadding: isSeparator ? 0 : 14
rightPadding: isSeparator ? 0 : 8
verticalPadding: isSeparator ? 2 : 5
spacing: root.spacing
font: root.font
text: root.textRole ? (Array.isArray(root.model) ? modelData[root.textRole] : model[root.textRole])
: modelData
icon.name: modelData["icon"]
icon.color: Theme.palette.primaryColor1
background: Rectangle {
implicitHeight: parent.isSeparator ? 3 : d.defaultDelegateHeight
color: {
if (menuDelegate.index === root.currentIndex)
return Theme.palette.primaryColor3
if (menuDelegate.highlighted)
return Theme.palette.statusMenu.hoverBackgroundColor
return "transparent"
}
HoverHandler {
cursorShape: root.enabled ? Qt.PointingHandCursor : undefined
}
}
contentItem: Loader {
readonly property int menuIndex: menuDelegate.index
readonly property string menuText: menuDelegate.text
readonly property string iconName: menuDelegate.icon.name
readonly property bool isCustomOrder: !menuDelegate.modelData["sortRoleName"]
sourceComponent: menuDelegate.isSeparator ? separatorMenuComponent : regularMenuComponent
}
onClicked: root.currentIndex = index
}
}