diff --git a/storybook/pages/AccountViewPage.qml b/storybook/pages/AccountViewPage.qml index 84f8a27e11..39d8516b3a 100644 --- a/storybook/pages/AccountViewPage.qml +++ b/storybook/pages/AccountViewPage.qml @@ -54,6 +54,8 @@ SplitView { sourceModel: NetworksModel.flatNetworks filters: ValueFilter { roleName: "isTest"; value: areTestNetworksEnabledCheckbox.checked } } + + property var filteredFlatModel: networks property bool areTestNetworksEnabled: areTestNetworksEnabledCheckbox.checked function toggleNetwork(chainId) { } @@ -72,6 +74,13 @@ SplitView { function processPreferredSharingNetworkToggle(preferredSharingNetworksArray, network) { console.warn("processPreferredSharingNetworkToggle :: preferredSharingNetworksArray ::", preferredSharingNetworksArray, "network :: ", network) + const chainId = network.chainId.toString() + if (preferredSharingNetworksArray.includes(chainId)) { + preferredSharingNetworksArray.splice(preferredSharingNetworksArray.indexOf(chainId), 1) + } else { + preferredSharingNetworksArray.push(chainId) + } + return [...preferredSharingNetworksArray] } } diff --git a/storybook/pages/NetworkFilterPage.qml b/storybook/pages/NetworkFilterPage.qml index 50958df554..927509a4a2 100644 --- a/storybook/pages/NetworkFilterPage.qml +++ b/storybook/pages/NetworkFilterPage.qml @@ -9,6 +9,7 @@ import SortFilterProxyModel 0.2 import AppLayouts.stores 1.0 import AppLayouts.Wallet.controls 1.0 +import AppLayouts.Wallet.views 1.0 SplitView { id: root @@ -32,10 +33,10 @@ SplitView { roles: ["chainId", "layer", "chainName", "isTest", "isEnabled", "iconUrl", "shortName", "chainColor"] rolesOverride: [{ role: "enabledState", transform: (mD) => { return simulatedNimModel.areAllEnabled(sourceModel) - ? NetworkSelectItemDelegate.UxEnabledState.AllEnabled + ? NetworkSelectionView.UxEnabledState.AllEnabled : mD.isEnabled - ? NetworkSelectItemDelegate.UxEnabledState.Enabled - : NetworkSelectItemDelegate.UxEnabledState.Disabled + ? NetworkSelectionView.UxEnabledState.Enabled + : NetworkSelectionView.UxEnabledState.Disabled } }] @@ -47,11 +48,11 @@ SplitView { let allEnabled = true for (let i = 0; i < simulatedNimModel.count; i++) { const item = simulatedNimModel.get(i) - if(item.enabledState === NetworkSelectItemDelegate.UxEnabledState.Enabled) { + if(item.enabledState === NetworkSelectionView.UxEnabledState.Enabled) { if(item.chainId !== chainId) { chainIdOnlyEnabled = false } - } else if(item.enabledState === NetworkSelectItemDelegate.UxEnabledState.Disabled) { + } else if(item.enabledState === NetworkSelectionView.UxEnabledState.Disabled) { if(item.chainId !== chainId) { chainIdOnlyDisabled = false } @@ -66,15 +67,15 @@ SplitView { for (let i = 0; i < simulatedNimModel.count; i++) { const item = simulatedNimModel.get(i) if(allEnabled) { - simulatedNimModel.setProperty(i, "enabledState", item.chainId === chainId ? NetworkSelectItemDelegate.UxEnabledState.Enabled : NetworkSelectItemDelegate.UxEnabledState.Disabled) + simulatedNimModel.setProperty(i, "enabledState", item.chainId === chainId ? NetworkSelectionView.UxEnabledState.Enabled : NetworkSelectionView.UxEnabledState.Disabled) } else if(chainIdOnlyEnabled || chainIdOnlyDisabled) { - simulatedNimModel.setProperty(i, "enabledState", NetworkSelectItemDelegate.UxEnabledState.AllEnabled) + simulatedNimModel.setProperty(i, "enabledState", NetworkSelectionView.UxEnabledState.AllEnabled) } else if(item.chainId === chainId) { - simulatedNimModel.setProperty(i, "enabledState", item.enabledState === NetworkSelectItemDelegate.UxEnabledState.Enabled - ? NetworkSelectItemDelegate.UxEnabledState.Disabled - : NetworkSelectItemDelegate.UxEnabledState.Enabled) + simulatedNimModel.setProperty(i, "enabledState", item.enabledState === NetworkSelectionView.UxEnabledState.Enabled + ? NetworkSelectionView.UxEnabledState.Disabled + : NetworkSelectionView.UxEnabledState.Enabled) } - const haveEnabled = item.enabledState !== NetworkSelectItemDelegate.UxEnabledState.Disabled + const haveEnabled = item.enabledState !== NetworkSelectionView.UxEnabledState.Disabled if(item.isEnabled !== haveEnabled) { simulatedNimModel.setProperty(i, "isEnabled", haveEnabled) } diff --git a/storybook/pages/NetworkSelectItemDelegatePage.qml b/storybook/pages/NetworkSelectItemDelegatePage.qml new file mode 100644 index 0000000000..8a10b1d0b9 --- /dev/null +++ b/storybook/pages/NetworkSelectItemDelegatePage.qml @@ -0,0 +1,97 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtQml 2.15 + +import StatusQ.Core 0.1 +import StatusQ.Core.Utils 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Components 0.1 +import StatusQ.Core.Theme 0.1 + +import AppLayouts.Wallet.controls 1.0 + +import Models 1.0 +import Storybook 1.0 + +import SortFilterProxyModel 0.2 + +import utils 1.0 + +SplitView { + id: root + + Item { + implicitWidth: delegate.width + NetworkSelectItemDelegate { + id: delegate + title: "Ethereum" + iconUrl: Style.svg("network/Network=Ethereum") + showIndicator: true + multiSelection: true + checkState: checkStateSelector.checkState + nextCheckState: checkState === Qt.Unchecked ? Qt.PartiallyChecked : + checkState === Qt.PartiallyChecked ? Qt.Checked : Qt.Unchecked + + onCheckStateChanged: { + checkStateSelector.checkState = checkState + } + } + } + + Pane { + id: pane + SplitView.fillWidth: true + ColumnLayout { + CheckBox { + text: "showIndicator" + checked: delegate.showIndicator + onCheckedChanged: { + delegate.showIndicator = checked + } + } + + CheckBox { + text: "multiSelection" + checked: delegate.multiSelection + onCheckedChanged: { + delegate.multiSelection = checked + } + } + + Label { + text: "title" + } + TextField { + text: delegate.title + onTextChanged: { + delegate.title = text + } + } + + Label { + text: "iconUrl" + } + TextField { + text: delegate.iconUrl + onTextChanged: { + delegate.iconUrl = text + } + } + + CheckBox { + id: checkStateSelector + text: "checkedState" + tristate: true + checked: true + onCheckStateChanged: { + if(delegate.checkState !== checkState) { + delegate.checkState = checkState + } + } + } + } + } +} + +// category: Controls diff --git a/storybook/pages/NetworkSelectPopupPage.qml b/storybook/pages/NetworkSelectPopupPage.qml index 9273cfe62a..01239758be 100644 --- a/storybook/pages/NetworkSelectPopupPage.qml +++ b/storybook/pages/NetworkSelectPopupPage.qml @@ -10,6 +10,7 @@ import utils 1.0 import AppLayouts.Wallet.popups 1.0 import AppLayouts.Wallet.controls 1.0 +import AppLayouts.Wallet.views 1.0 import AppLayouts.stores 1.0 import Models 1.0 @@ -219,10 +220,10 @@ SplitView { roles: ["chainId", "layer", "chainName", "isTest", "isEnabled", "iconUrl", "shortName", "chainColor"] rolesOverride: [{ role: "enabledState", transform: (mD) => { return simulatedNimModel.areAllEnabled(sourceModel) - ? NetworkSelectItemDelegate.UxEnabledState.AllEnabled + ? NetworkSelectionView.UxEnabledState.AllEnabled : mD.isEnabled - ? NetworkSelectItemDelegate.UxEnabledState.Enabled - : NetworkSelectItemDelegate.UxEnabledState.Disabled + ? NetworkSelectionView.UxEnabledState.Enabled + : NetworkSelectionView.UxEnabledState.Disabled } }] @@ -234,11 +235,11 @@ SplitView { let allEnabled = true for (let i = 0; i < simulatedNimModel.count; i++) { const item = simulatedNimModel.get(i) - if(item.enabledState === NetworkSelectItemDelegate.UxEnabledState.Enabled) { + if(item.enabledState === NetworkSelectionView.UxEnabledState.Enabled) { if(item.chainId !== chainId) { chainIdOnlyEnabled = false } - } else if(item.enabledState === NetworkSelectItemDelegate.UxEnabledState.Disabled) { + } else if(item.enabledState === NetworkSelectionView.UxEnabledState.Disabled) { if(item.chainId !== chainId) { chainIdOnlyDisabled = false } @@ -253,15 +254,15 @@ SplitView { for (let i = 0; i < simulatedNimModel.count; i++) { const item = simulatedNimModel.get(i) if(allEnabled) { - simulatedNimModel.setProperty(i, "enabledState", item.chainId === chainId ? NetworkSelectItemDelegate.UxEnabledState.Enabled : NetworkSelectItemDelegate.UxEnabledState.Disabled) + simulatedNimModel.setProperty(i, "enabledState", item.chainId === chainId ? NetworkSelectionView.UxEnabledState.Enabled : NetworkSelectionView.UxEnabledState.Disabled) } else if(chainIdOnlyEnabled || chainIdOnlyDisabled) { - simulatedNimModel.setProperty(i, "enabledState", NetworkSelectItemDelegate.UxEnabledState.AllEnabled) + simulatedNimModel.setProperty(i, "enabledState", NetworkSelectionView.UxEnabledState.AllEnabled) } else if(item.chainId === chainId) { - simulatedNimModel.setProperty(i, "enabledState", item.enabledState === NetworkSelectItemDelegate.UxEnabledState.Enabled - ? NetworkSelectItemDelegate.UxEnabledState.Disabled - :NetworkSelectItemDelegate.UxEnabledState.Enabled) + simulatedNimModel.setProperty(i, "enabledState", item.enabledState === NetworkSelectionView.UxEnabledState.Enabled + ? NetworkSelectionView.UxEnabledState.Disabled + :NetworkSelectionView.UxEnabledState.Enabled) } - const haveEnabled = item.enabledState !== NetworkSelectItemDelegate.UxEnabledState.Disabled + const haveEnabled = item.enabledState !== NetworkSelectionView.UxEnabledState.Disabled if(item.isEnabled !== haveEnabled) { simulatedNimModel.setProperty(i, "isEnabled", haveEnabled) } diff --git a/storybook/qmlTests/tests/tst_NetworkSelectItemDelegate.qml b/storybook/qmlTests/tests/tst_NetworkSelectItemDelegate.qml new file mode 100644 index 0000000000..f6e8523c60 --- /dev/null +++ b/storybook/qmlTests/tests/tst_NetworkSelectItemDelegate.qml @@ -0,0 +1,260 @@ +import QtQuick 2.15 +import QtTest 1.15 + +import AppLayouts.Wallet.controls 1.0 + +import utils 1.0 + + +Item { + id: root + width: 600 + height: 400 + + Component { + id: componentUnderTest + NetworkSelectItemDelegate { + anchors.centerIn: parent + title: "Ethereum" + iconUrl: Style.svg("network/Network=Ethereum") + onToggled: root.onToggledHandler() + } + } + + SignalSpy { + id: toggledSpy + target: controlUnderTest + signalName: "toggled" + } + + SignalSpy { + id: checkStateChangedSpy + target: controlUnderTest + signalName: "checkStateChanged" + } + + property NetworkSelectItemDelegate controlUnderTest: null + property var onToggledHandler: function(){} + property int externalCheckState: Qt.Unchecked + + TestCase { + name: "NetworkSelectItemDelegate" + when: windowShown + + function init() { + controlUnderTest = createTemporaryObject(componentUnderTest, root) + toggledSpy.clear() + checkStateChangedSpy.clear() + onToggledHandler = function() {} + } + + function test_basicGeometry() { + verify(!!controlUnderTest) + verify(controlUnderTest.width > 0) + verify(controlUnderTest.height > 0) + } + + function test_title() { + verify(!!controlUnderTest) + compare(controlUnderTest.title, "Ethereum") + controlUnderTest.title = "Polygon" + compare(controlUnderTest.title, "Polygon") + controlUnderTest.title = "" + compare(controlUnderTest.title, "") + controlUnderTest.title = "Ethereum" + } + + function test_icon() { + verify(!!controlUnderTest) + compare(controlUnderTest.iconUrl, Style.svg("network/Network=Ethereum")) + compare(findChild(controlUnderTest, "statusRoundImage").image.source, Style.svg("network/Network=Ethereum")) + controlUnderTest.iconUrl = Style.svg("network/Network=Polygon") + compare(controlUnderTest.iconUrl, Style.svg("network/Network=Polygon")) + compare(findChild(controlUnderTest, "statusRoundImage").image.source, Style.svg("network/Network=Polygon")) + } + + function test_indicatorConfig() { + verify(!!controlUnderTest) + verify(!!findChild(controlUnderTest, "networkSelectionRadioButton_Ethereum")) + verify(!findChild(controlUnderTest, "networkSelectionCheckbox_Ethereum")) + compare(controlUnderTest.showIndicator, true) + compare(controlUnderTest.multiSelection, false) + + //changing to multiselect -> indicator switches to checkbox + controlUnderTest.multiSelection = true + waitForRendering(controlUnderTest) + waitForItemPolished(controlUnderTest) + verify(!!findChild(controlUnderTest, "networkSelectionCheckbox_Ethereum")) + verify(!findChild(controlUnderTest, "networkSelectionRadioButton_Ethereum")) + + //changing removing indicator + controlUnderTest.showIndicator = false + waitForRendering(controlUnderTest) + waitForItemPolished(controlUnderTest) + verify(!findChild(controlUnderTest, "networkSelectionCheckbox_Ethereum")) + verify(!findChild(controlUnderTest, "networkSelectionRadioButton_Ethereum")) + } + + function test_toggleByClick() { + verify(!!controlUnderTest) + mouseClick(controlUnderTest) + tryCompare(toggledSpy, "count", 1) + + const image = findChild(controlUnderTest, "statusRoundImage") + mouseClick(image) + tryCompare(toggledSpy, "count", 2) + + const radioButton = findChild(controlUnderTest, "networkSelectionRadioButton_Ethereum") + mouseClick(radioButton) + tryCompare(toggledSpy, "count", 3) + + controlUnderTest.multiSelection = true + waitForItemPolished(controlUnderTest) + const checkBox = findChild(controlUnderTest, "networkSelectionCheckbox_Ethereum") + mouseClick(checkBox) + tryCompare(toggledSpy, "count", 4) + } + + function test_autoCheckStateChanges() { + verify(!!controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Checked) + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + const radioButton = findChild(controlUnderTest, "networkSelectionRadioButton_Ethereum") + mouseClick(radioButton) + compare(controlUnderTest.checkState, Qt.Checked) + mouseClick(radioButton) + compare(controlUnderTest.checkState, Qt.Unchecked) + + controlUnderTest.multiSelection = true + waitForItemPolished(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + const checkBox = findChild(controlUnderTest, "networkSelectionCheckbox_Ethereum") + mouseClick(checkBox) + waitForItemPolished(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Checked) + mouseClick(checkBox) + compare(controlUnderTest.checkState, Qt.Unchecked) + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Checked) + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + } + + function test_manualCheckStateChanges() { + verify(!!controlUnderTest) + // checkState is not bound to nextCheckState => no automatic check changes + controlUnderTest.nextCheckState = Qt.binding(() => controlUnderTest.checkState) + compare(controlUnderTest.checkState, Qt.Unchecked) + + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + let radioButton = findChild(controlUnderTest, "networkSelectionRadioButton_Ethereum") + mouseClick(radioButton) + compare(controlUnderTest.checkState, Qt.Unchecked) + + controlUnderTest.multiSelection = true + waitForItemPolished(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + let checkBox = findChild(controlUnderTest, "networkSelectionCheckbox_Ethereum") + mouseClick(checkBox) + compare(controlUnderTest.checkState, Qt.Unchecked) + + controlUnderTest.multiSelection = false + waitForItemPolished(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + + root.onToggledHandler = function() { + controlUnderTest.checkState = controlUnderTest.checkState === Qt.Checked ? Qt.Unchecked : Qt.Checked + } + + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Checked) + + radioButton = findChild(controlUnderTest, "networkSelectionRadioButton_Ethereum") + mouseClick(radioButton) + compare(controlUnderTest.checkState, Qt.Unchecked) + + controlUnderTest.multiSelection = true + root.onToggledHandler = function() { + controlUnderTest.checkState = controlUnderTest.checkState === Qt.Unchecked ? Qt.PartiallyChecked : + controlUnderTest.checkState === Qt.Checked ? Qt.Unchecked : Qt.Checked + } + + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.PartiallyChecked) + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Checked) + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + + checkBox = findChild(controlUnderTest, "networkSelectionCheckbox_Ethereum") + mouseClick(checkBox) + compare(controlUnderTest.checkState, Qt.PartiallyChecked) + mouseClick(checkBox) + compare(controlUnderTest.checkState, Qt.Checked) + mouseClick(checkBox) + compare(controlUnderTest.checkState, Qt.Unchecked) + } + + function test_checkStateBindings() { + verify(!!controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + compare(root.externalCheckState, Qt.Unchecked) + + controlUnderTest.checkState = Qt.binding(() => root.externalCheckState) + compare(controlUnderTest.checkState, root.externalCheckState) + tryCompare(checkStateChangedSpy, "count", 0) + + root.externalCheckState = Qt.Checked + compare(controlUnderTest.checkState, Qt.Checked) + tryCompare(checkStateChangedSpy, "count", 1) + + root.externalCheckState = Qt.Unchecked + compare(controlUnderTest.checkState, Qt.Unchecked) + tryCompare(checkStateChangedSpy, "count", 2) + } + + function test_interactiveConfig() { + verify(!!controlUnderTest) + compare(controlUnderTest.interactive, true) + controlUnderTest.interactive = false + compare(controlUnderTest.checkState, Qt.Unchecked) + + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + + let radioButton = findChild(controlUnderTest, "networkSelectionRadioButton_Ethereum") + mouseClick(radioButton) + compare(controlUnderTest.checkState, Qt.Unchecked) + + controlUnderTest.multiSelection = true + waitForItemPolished(controlUnderTest) + + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + + let checkBox = findChild(controlUnderTest, "networkSelectionCheckbox_Ethereum") + mouseClick(checkBox) + compare(controlUnderTest.checkState, Qt.Unchecked) + + controlUnderTest.showIndicator = false + + mouseClick(controlUnderTest) + compare(controlUnderTest.checkState, Qt.Unchecked) + + mouseMove(controlUnderTest, controlUnderTest.width / 2, controlUnderTest.height / 2) + waitForRendering(controlUnderTest) + waitForItemPolished(controlUnderTest) + compare(controlUnderTest.sensor.containsMouse, true) + + // manual selection works + controlUnderTest.checkState = Qt.Checked + compare(controlUnderTest.checkState, Qt.Checked) + controlUnderTest.checkState = Qt.Unchecked + compare(controlUnderTest.checkState, Qt.Unchecked) + } + } +} \ No newline at end of file diff --git a/ui/StatusQ/src/StatusQ/Components/StatusSmartIdenticon.qml b/ui/StatusQ/src/StatusQ/Components/StatusSmartIdenticon.qml index 298d943ea5..3d03a7a508 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusSmartIdenticon.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusSmartIdenticon.qml @@ -50,6 +50,7 @@ Loader { StatusRoundedImage { id: statusRoundImage + objectName: "statusRoundImage" width: parent.width height: parent.height image.source: root.asset.isImage ? root.asset.name : "" @@ -80,6 +81,7 @@ Loader { id: roundedIcon StatusRoundIcon { + objectName: "statusRoundIcon" asset.bgRadius: root.asset.bgRadius asset.bgWidth: root.asset.bgWidth asset.bgHeight: root.asset.bgHeight diff --git a/ui/app/AppLayouts/Wallet/controls/NetworkFilter.qml b/ui/app/AppLayouts/Wallet/controls/NetworkFilter.qml index 479c83ddc1..e9c0955cfa 100644 --- a/ui/app/AppLayouts/Wallet/controls/NetworkFilter.qml +++ b/ui/app/AppLayouts/Wallet/controls/NetworkFilter.qml @@ -59,7 +59,8 @@ StatusComboBox { root.multiSelection NetworkModelHelpers.getChainIconUrl(root.flatNetworks, d.currentIndex) } - readonly property bool allSelected: enabledFlatNetworks.count === root.flatNetworks.count + readonly property bool allSelected: root.preferredNetworksMode ? root.preferredSharingNetworks.length === root.flatNetworks.count : + enabledFlatNetworks.count === root.flatNetworks.count readonly property bool noneSelected: enabledFlatNetworks.count === 0 // Persist selection between selectPopupLoader reloads @@ -67,7 +68,14 @@ StatusComboBox { property SortFilterProxyModel enabledFlatNetworks: SortFilterProxyModel { sourceModel: root.flatNetworks - filters: ValueFilter { roleName: "isEnabled"; value: true; enabled: !root.preferredNetworksMode } + filters: [ + ValueFilter { roleName: "isEnabled"; value: true; enabled: !root.preferredNetworksMode }, + FastExpressionFilter { + expression: root.preferredSharingNetworks.includes(chainId.toString()) + expectedRoles: ["chainId"] + enabled: root.preferredNetworksMode + } + ] } } @@ -128,7 +136,7 @@ StatusComboBox { visible: (!d.allSelected || !root.showAllSelectedText) && chainRepeater.count > 0 Repeater { id: chainRepeater - model: root.preferredNetworksMode ? root.flatNetworks: root.multiSelection ? d.enabledFlatNetworks: [] + model: root.multiSelection ? d.enabledFlatNetworks: [] delegate: StatusRoundedImage { id: delegateItem width: 24 diff --git a/ui/app/AppLayouts/Wallet/controls/NetworkSelectItemDelegate.qml b/ui/app/AppLayouts/Wallet/controls/NetworkSelectItemDelegate.qml index a7f0061ad7..ab5992679a 100644 --- a/ui/app/AppLayouts/Wallet/controls/NetworkSelectItemDelegate.qml +++ b/ui/app/AppLayouts/Wallet/controls/NetworkSelectItemDelegate.qml @@ -1,96 +1,119 @@ import QtQuick 2.15 +import QtQml 2.15 import QtQuick.Controls 2.15 +import QtGraphicalEffects 1.15 import StatusQ.Components 0.1 import StatusQ.Controls 0.1 +import StatusQ.Core.Theme 0.1 import utils 1.0 -import "../stores/NetworkSelectPopup" - StatusListItem { id: root - property var networkModel: null - property var singleSelection - property var radioButtonGroup - property bool useEnabledRole: true - property bool showCheckboxes: true - property bool showRadioButtons: true + // input/output property + property int checkState: Qt.Unchecked - // Needed for preferred sharing networks - property bool preferredNetworksMode: false - property var preferredSharingNetworks: [] - property bool allChecked: true + //input property + // Defines the next state of the checkbox when clicked + // By default, it toggles between checked and unchecked + property int nextCheckState: checkState === Qt.Checked ? Qt.Unchecked : Qt.Checked - signal toggleNetwork(var network, var model, int index) + required property string iconUrl + property bool showIndicator: true + property bool multiSelection: false + property bool interactive: true - /// Mirrors Nim's UxEnabledState enum from networks/item.nim - enum UxEnabledState { - Enabled, - AllEnabled, - Disabled - } + // output signal + // Emitted when the checkbox is clicked + // This signal is useful when the check state needs to change + // only after processing the toggle event E.g backend call + signal toggled - objectName: model.chainName - title: model.chainName + objectName: root.title asset.height: 24 asset.width: 24 asset.isImage: true - asset.name: Style.svg(model.iconUrl) + asset.name: root.iconUrl onClicked: { - if(!root.singleSelection.enabled) { - checkBox.nextCheckState() - } else if(!radioButton.checked) { // Don't allow uncheck - root.toggleNetwork(({chainId: model.chainId, chainName: model.chainName, iconUrl: model.iconUrl}), root.networkModel, model.index) + d.toggled() + } + + leftPadding: 16 + rightPadding: 16 + statusListItemTitleArea.anchors.leftMargin: 12 + highlighted: (d.checkState !== Qt.Unchecked && !showIndicator) + + Binding on bgColor { + when: highlighted && !root.sensor.containsMouse + value: root.interactive ? Theme.palette.baseColor4 : Theme.palette.primaryColor3 + restoreMode: Binding.RestoreBindingOrValue + } + + onCheckStateChanged: { + if (checkState !== d.checkState) { + d.checkState = checkState } } - leftPadding: 12 - rightPadding: 0 - statusListItemTitleArea.anchors.leftMargin: 12 - components: [ - StatusCheckBox { - id: checkBox - objectName: "networkSelectionCheckbox_" + model.chainName - tristate: true - visible: !root.singleSelection.enabled && root.showCheckboxes - - checkState: { - if(root.preferredNetworksMode) { - return root.allChecked ? Qt.PartiallyChecked : preferredSharingNetworks.includes(model.chainId.toString()) ? Qt.Checked : Qt.Unchecked - } - else if(root.useEnabledRole) { - return model.isEnabled ? Qt.Checked : Qt.Unchecked - } else if (model.enabledState === NetworkSelectItemDelegate.UxEnabledState.Enabled) { - return Qt.Checked - } else { - if( model.enabledState === NetworkSelectItemDelegate.UxEnabledState.AllEnabled) { - return Qt.PartiallyChecked - } else { - return Qt.Unchecked - } - } - } - - nextCheckState: () => { - Qt.callLater(root.toggleNetwork, model, root.networkModel, model.index) - return Qt.PartiallyChecked - } - }, - StatusRadioButton { - id: radioButton - visible: root.singleSelection.enabled && root.showRadioButtons - size: StatusRadioButton.Size.Large - ButtonGroup.group: root.radioButtonGroup - checked: root.singleSelection.currentModel === root.networkModel && root.singleSelection.currentIndex === model.index - - onToggled: { - if(checked) { - root.toggleNetwork(({chainId: model.chainId, chainName: model.chainName, iconUrl: model.iconUrl}), root.networkModel, model.index) - } - } + Loader { + id: indicatorLoader + sourceComponent: root.multiSelection ? checkBoxComponent : radioButtonComponent + active: root.showIndicator } ] + + + Component { + id: checkBoxComponent + StatusCheckBox { + id: checkBox + + objectName: "networkSelectionCheckbox_" + root.title + checkState: d.checkState + tristate: true + nextCheckState: () => d.checkState + enabled: root.interactive + + onClicked: { + d.toggled() + } + } + } + + Component { + id: radioButtonComponent + StatusRadioButton { + id: radioButton + objectName: "networkSelectionRadioButton_" + root.title + size: StatusRadioButton.Size.Large + checked: d.checkState !== Qt.Unchecked + enabled: root.interactive + + onClicked: { + d.toggled() + } + } + } + + QtObject { + id: d + property int checkState: root.checkState + + function toggled() { + if (!root.interactive) { + return + } + d.checkState = root.nextCheckState + root.toggled() + } + + onCheckStateChanged: { + if (checkState !== root.checkState) { + root.checkState = checkState + } + } + } } diff --git a/ui/app/AppLayouts/Wallet/popups/NetworkSelectPopup.qml b/ui/app/AppLayouts/Wallet/popups/NetworkSelectPopup.qml index 3f7f10687b..9cf89ea49e 100644 --- a/ui/app/AppLayouts/Wallet/popups/NetworkSelectPopup.qml +++ b/ui/app/AppLayouts/Wallet/popups/NetworkSelectPopup.qml @@ -75,7 +75,7 @@ StatusDialog { preferredSharingNetworks: root.preferredSharingNetworks useEnabledRole: root.useEnabledRole singleSelection: d.singleSelection - onToggleNetwork: { + onToggleNetwork: (network, index) => { root.toggleNetwork(network, index) if(d.singleSelection.enabled) close() diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml index 7f384b6162..df012658ea 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml @@ -136,6 +136,13 @@ StatusDialog { networkFilter.setChain(root.swapInputParamsForm.selectedNetworkChainId) } } + + Connections { + target: root.swapInputParamsForm + function onSelectedNetworkChainIdChanged() { + networkFilter.setChain(root.swapInputParamsForm.selectedNetworkChainId) + } + } } } diff --git a/ui/app/AppLayouts/Wallet/views/NetworkSelectionView.qml b/ui/app/AppLayouts/Wallet/views/NetworkSelectionView.qml index 89c1136cd2..99a725b0d0 100644 --- a/ui/app/AppLayouts/Wallet/views/NetworkSelectionView.qml +++ b/ui/app/AppLayouts/Wallet/views/NetworkSelectionView.qml @@ -1,6 +1,7 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 +import StatusQ 0.1 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 @@ -24,21 +25,71 @@ StatusListView { signal toggleNetwork(var network, int index) + /// Mirrors Nim's UxEnabledState enum from networks/item.nim + enum UxEnabledState { + Enabled, + AllEnabled, + Disabled + } + model: root.flatNetworks delegate: NetworkSelectItemDelegate { + id: delegateItem + + required property var model + readonly property int multiSelectCheckState: { + if(root.preferredNetworksMode) { + return root.preferredSharingNetworks.length === root.count ? + Qt.PartiallyChecked : + root.preferredSharingNetworks.includes(model.chainId.toString()) ? Qt.Checked : Qt.Unchecked + } + else if(root.useEnabledRole) { + return model.isEnabled ? Qt.Checked : Qt.Unchecked + } else if (model.enabledState === NetworkSelectionView.UxEnabledState.Enabled) { + return Qt.Checked + } else { + if( model.enabledState === NetworkSelectionView.UxEnabledState.AllEnabled) { + return Qt.PartiallyChecked + } else { + return Qt.Unchecked + } + } + } + + readonly property int singleSelectCheckState: { + if (root.singleSelection.currentModel === root.model && root.singleSelection.currentIndex === model.index) + return Qt.Checked + return Qt.Unchecked + } + + implicitHeight: 48 implicitWidth: root.width - radioButtonGroup: radioBtnGroup - networkModel: root.model - useEnabledRole: root.useEnabledRole - singleSelection: root.singleSelection - onToggleNetwork: (network, model, index) => root.toggleNetwork(network, index) - preferredNetworksMode: root.preferredNetworksMode - preferredSharingNetworks: root.preferredSharingNetworks - allChecked: root.preferredSharingNetworks.length === root.count - showCheckboxes: root.showCheckboxes - showRadioButtons: root.showRadioButtons + title: model.chainName + iconUrl: Style.svg(model.iconUrl) + showIndicator: (multiSelection && root.showCheckboxes) || (!multiSelection && root.showRadioButtons) + multiSelection: !root.singleSelection.enabled + + Binding on checkState { + when: root.singleSelection.enabled + value: singleSelectCheckState + } + + Binding on checkState { + when: !root.singleSelection.enabled + value: multiSelectCheckState + } + + nextCheckState: checkState + onToggled: { + if(!root.singleSelection.enabled) { + Qt.callLater(root.toggleNetwork, delegateItem.model, delegateItem.model.index) + } else if(!checkState !== Qt.Checked) { // Don't allow uncheck + checkState = checkState === Qt.Checked ? Qt.Unchecked : Qt.Checked + root.toggleNetwork(delegateItem.model, model.index) + } + } } section { @@ -62,8 +113,4 @@ StatusListView { } } } - - ButtonGroup { - id: radioBtnGroup - } } diff --git a/ui/app/AppLayouts/Wallet/views/qmldir b/ui/app/AppLayouts/Wallet/views/qmldir index f779a633f3..f64560e1e0 100644 --- a/ui/app/AppLayouts/Wallet/views/qmldir +++ b/ui/app/AppLayouts/Wallet/views/qmldir @@ -1,5 +1,6 @@ AssetsDetailView 1.0 AssetsDetailView.qml CollectiblesView 1.0 CollectiblesView.qml +NetworkSelectionView 1.0 NetworkSelectionView.qml SavedAddresses 1.0 SavedAddresses.qml TokenSelectorAssetDelegate 1.0 TokenSelectorAssetDelegate.qml TokenSelectorView 1.0 TokenSelectorView.qml