From 9374be5857a46f25e9e92be158c83605c44ea68e Mon Sep 17 00:00:00 2001 From: emizzle Date: Thu, 30 Jul 2020 15:18:54 +1000 Subject: [PATCH] feat: Send transaction component -- Account selector Fixes #670. Component spec based on https://www.notion.so/emizzle/Wallet-transaction-components-2003b78a8d0d41c4ab3d21eb2496fb20. Changes the current Select shared component to a model databound component. This means we can bind directly to `QAbstractListModel`'s coming from Nim instead of needing manipulating the data in to javascript objects. The changes to the Select component will be used for the upcoming Asset selector shared component. --- src/app/wallet/views/account_list.nim | 34 +++- .../Profile/Sections/LanguageContainer.qml | 29 ++- .../Wallet/AccountSettingsModal.qml | 22 +- ui/app/AppLayouts/Wallet/SendModal.qml | 11 - .../components/AddAccountWithPrivateKey.qml | 21 +- .../Wallet/components/AddAccountWithSeed.qml | 23 +-- .../Wallet/components/AddWatchOnlyAccount.qml | 23 +-- .../components/GenerateAccountModal.qml | 23 +-- .../Wallet/components/SendModalContent.qml | 69 +++---- ui/imports/Themes/DarkTheme.qml | 4 + ui/imports/Themes/LightTheme.qml | 4 + ui/nim-status-client.pro | 1 + ui/shared/AccountSelector.qml | 190 ++++++++++++++++++ ui/shared/ColorSelector.qml | 57 ++++++ ui/shared/Select.qml | 153 ++++++-------- 15 files changed, 429 insertions(+), 235 deletions(-) create mode 100644 ui/shared/AccountSelector.qml create mode 100644 ui/shared/ColorSelector.qml diff --git a/src/app/wallet/views/account_list.nim b/src/app/wallet/views/account_list.nim index bc5d691064..ea620633c0 100644 --- a/src/app/wallet/views/account_list.nim +++ b/src/app/wallet/views/account_list.nim @@ -1,9 +1,11 @@ -import NimQml, Tables, random +import NimQml, Tables, random, strformat, json_serialization import sequtils as sequtils -import account_item +import account_item, asset_list from ../../../status/wallet import WalletAccount const accountColors* = ["#9B832F", "#D37EF4", "#1D806F", "#FA6565", "#7CDA00", "#887af9", "#8B3131"] +type + AccountView* = tuple[account: WalletAccount, assets: AssetList] type AccountRoles {.pure.} = enum @@ -11,10 +13,12 @@ type Address = UserRole + 2, Color = UserRole + 3, Balance = UserRole + 4 + FiatBalance = UserRole + 5 + Assets = UserRole + 6 QtObject: type AccountList* = ref object of QAbstractListModel - accounts*: seq[WalletAccount] + accounts*: seq[AccountView] proc setup(self: AccountList) = self.QAbstractListModel.setup @@ -27,12 +31,14 @@ QtObject: result.accounts = @[] result.setup - proc getAccount*(self: AccountList, index: int): WalletAccount = self.accounts[index] + proc getAccount*(self: AccountList, index: int): WalletAccount = self.accounts[index].account proc rowData(self: AccountList, index: int, column: string): string {.slot.} = if (index >= self.accounts.len): return - let account = self.accounts[index] + let + accountView = self.accounts[index] + account = accountView.account case column: of "name": result = account.name of "address": result = account.address @@ -40,11 +46,12 @@ QtObject: of "balance": result = account.balance of "path": result = account.path of "walletType": result = account.walletType + of "fiatBalance": result = fmt"{account.realFiatBalance:>.2f}" proc getAccountindexByAddress*(self: AccountList, address: string): int = var i = 0 - for account in self.accounts: - if (account.address == address): + for accountView in self.accounts: + if (accountView.account.address == address): return i i = i + 1 return -1 @@ -60,26 +67,33 @@ QtObject: return if index.row < 0 or index.row >= self.accounts.len: return - let account = self.accounts[index.row] + let accountView = self.accounts[index.row] + let account = accountView.account let accountRole = role.AccountRoles case accountRole: of AccountRoles.Name: result = newQVariant(account.name) of AccountRoles.Address: result = newQVariant(account.address) of AccountRoles.Color: result = newQVariant(account.iconColor) of AccountRoles.Balance: result = newQVariant(account.balance) + of AccountRoles.FiatBalance: result = newQVariant(fmt"{account.realFiatBalance:>.2f}") + of AccountRoles.Assets: result = newQVariant(accountView.assets) method roleNames(self: AccountList): Table[int, string] = { AccountRoles.Name.int:"name", AccountRoles.Address.int:"address", AccountRoles.Color.int:"iconColor", - AccountRoles.Balance.int:"balance" }.toTable + AccountRoles.Balance.int:"balance", + AccountRoles.FiatBalance.int:"fiatBalance", + AccountRoles.Assets.int:"assets" }.toTable proc addAccountToList*(self: AccountList, account: WalletAccount) = if account.iconColor == "": randomize() account.iconColor = accountColors[rand(accountColors.len - 1)] + let assets = newAssetList() + assets.setNewData(account.assetList) self.beginInsertRows(newQModelIndex(), self.accounts.len, self.accounts.len) - self.accounts.add(account) + self.accounts.add((account: account, assets: assets)) self.endInsertRows() proc forceUpdate*(self: AccountList) = diff --git a/ui/app/AppLayouts/Profile/Sections/LanguageContainer.qml b/ui/app/AppLayouts/Profile/Sections/LanguageContainer.qml index 763489718b..43c9dafbb6 100644 --- a/ui/app/AppLayouts/Profile/Sections/LanguageContainer.qml +++ b/ui/app/AppLayouts/Profile/Sections/LanguageContainer.qml @@ -33,20 +33,37 @@ Item { text: qsTr("Language") } Select { + id: select selectedText: languageSetting.currentLocale anchors.right: undefined anchors.left: undefined width: 100 Layout.leftMargin: Style.current.padding - selectOptions: Locales_JSON.locales.map(locale => { - return { - label: locale, - onClicked: function () { + model: Locales_JSON.locales + menu.delegate: Component { + MenuItem { + id: menuItem + height: itemText.height + 4 + width: parent.width + padding: 10 + onTriggered: function () { + const locale = Locales_JSON.locales[index] profileModel.changeLocale(locale) appSettings.locale = locale } - } - }) + + StyledText { + id: itemText + text: Locales_JSON.locales[index] + anchors.left: parent.left + anchors.leftMargin: 5 + anchors.verticalCenter: parent.verticalCenter + } + background: Rectangle { + color: menuItem.highlighted ? Style.current.backgroundHover : Style.current.transparent + } + } + } } } } diff --git a/ui/app/AppLayouts/Wallet/AccountSettingsModal.qml b/ui/app/AppLayouts/Wallet/AccountSettingsModal.qml index 1ff0dabc2b..7723b96909 100644 --- a/ui/app/AppLayouts/Wallet/AccountSettingsModal.qml +++ b/ui/app/AppLayouts/Wallet/AccountSettingsModal.qml @@ -15,7 +15,6 @@ ModalPopup { height: 675 property int marginBetweenInputs: 35 - property string selectedColor: currentAccount.iconColor property string accountNameValidationError: "" function validate() { @@ -43,23 +42,14 @@ ModalPopup { validationError: popup.accountNameValidationError } - Select { + ColorSelector { id: accountColorInput + selectedColor: currentAccount.iconColor + model: Constants.accountColors anchors.top: accountNameInput.bottom anchors.topMargin: marginBetweenInputs - bgColor: selectedColor - //% "Account color" - label: qsTrId("account-color") - selectOptions: Constants.accountColors.map(color => { - return { - text: "", - bgColor: color, - height: 52, - onClicked: function () { - selectedColor = color - } - } - }) + anchors.left: parent.left + anchors.right: parent.right } TextWithLabel { @@ -181,7 +171,7 @@ ModalPopup { return } - const error = walletModel.changeAccountSettings(currentAccount.address, accountNameInput.text, selectedColor); + const error = walletModel.changeAccountSettings(currentAccount.address, accountNameInput.text, accountColorInput.selectedColor); if (error) { errorSound.play() diff --git a/ui/app/AppLayouts/Wallet/SendModal.qml b/ui/app/AppLayouts/Wallet/SendModal.qml index fe30f6b0b1..016e51badb 100644 --- a/ui/app/AppLayouts/Wallet/SendModal.qml +++ b/ui/app/AppLayouts/Wallet/SendModal.qml @@ -17,17 +17,6 @@ ModalPopup { sendModalContent.amountInput.text = "" sendModalContent.passwordInput.text = "" sendModalContent.amountInput.forceActiveFocus(Qt.MouseFocusReason) - const accounts = walletModel.accounts - const numAccounts = accounts.rowCount() - const accountsData = [] - for (let i = 0; i < numAccounts; i++) { - accountsData.push({ - name: accounts.rowData(i, 'name'), - address: accounts.rowData(i, 'address'), - iconColor: accounts.rowData(i, 'iconColor') - }) - } - sendModalContent.accounts = accountsData const assets = walletModel.assets const numAssets = assets.rowCount() diff --git a/ui/app/AppLayouts/Wallet/components/AddAccountWithPrivateKey.qml b/ui/app/AppLayouts/Wallet/components/AddAccountWithPrivateKey.qml index 0349cfb9ff..8f80e4320b 100644 --- a/ui/app/AppLayouts/Wallet/components/AddAccountWithPrivateKey.qml +++ b/ui/app/AppLayouts/Wallet/components/AddAccountWithPrivateKey.qml @@ -1,4 +1,5 @@ import QtQuick 2.13 +import QtQuick.Controls 2.13 import QtQuick.Dialogs 1.3 import "../../../../imports" import "../../../../shared" @@ -10,7 +11,6 @@ ModalPopup { height: 600 property int marginBetweenInputs: 38 - property string selectedColor: Constants.accountColors[0] property string passwordValidationError: "" property string privateKeyValidationError: "" property string accountNameValidationError: "" @@ -86,23 +86,14 @@ ModalPopup { validationError: popup.accountNameValidationError } - Select { + ColorSelector { id: accountColorInput + selectedColor: Constants.accountColors[0] + model: Constants.accountColors anchors.top: accountNameInput.bottom anchors.topMargin: marginBetweenInputs - bgColor: selectedColor - //% "Account color" - label: qsTrId("account-color") - selectOptions: Constants.accountColors.map(color => { - return { - text: "", - bgColor: color, - height: 52, - onClicked: function () { - selectedColor = color - } - } - }) + anchors.left: parent.left + anchors.right: parent.right } footer: StyledButton { diff --git a/ui/app/AppLayouts/Wallet/components/AddAccountWithSeed.qml b/ui/app/AppLayouts/Wallet/components/AddAccountWithSeed.qml index f6d81b2c27..9ada865437 100644 --- a/ui/app/AppLayouts/Wallet/components/AddAccountWithSeed.qml +++ b/ui/app/AppLayouts/Wallet/components/AddAccountWithSeed.qml @@ -1,4 +1,5 @@ import QtQuick 2.13 +import QtQuick.Controls 2.13 import QtQuick.Dialogs 1.3 import "../../../../imports" import "../../../../shared" @@ -8,7 +9,6 @@ ModalPopup { height: 600 property int marginBetweenInputs: 38 - property string selectedColor: Constants.accountColors[0] property string passwordValidationError: "" property string seedValidationError: "" property string accountNameValidationError: "" @@ -87,23 +87,14 @@ ModalPopup { validationError: popup.accountNameValidationError } - Select { + ColorSelector { id: accountColorInput + selectedColor: Constants.accountColors[0] + model: Constants.accountColors anchors.top: accountNameInput.bottom anchors.topMargin: marginBetweenInputs - bgColor: selectedColor - //% "Account color" - label: qsTrId("account-color") - selectOptions: Constants.accountColors.map(color => { - return { - text: "", - bgColor: color, - height: 52, - onClicked: function () { - selectedColor = color - } - } - }) + anchors.left: parent.left + anchors.right: parent.right } footer: StyledButton { @@ -133,7 +124,7 @@ ModalPopup { return loading = false } - const error = walletModel.addAccountsFromSeed(accountSeedInput.text, passwordInput.text, accountNameInput.text, selectedColor) + const error = walletModel.addAccountsFromSeed(accountSeedInput.text, passwordInput.text, accountNameInput.text, accountColorInput.selectedColor) loading = false if (error) { errorSound.play() diff --git a/ui/app/AppLayouts/Wallet/components/AddWatchOnlyAccount.qml b/ui/app/AppLayouts/Wallet/components/AddWatchOnlyAccount.qml index 0ffb994510..5acbe21c42 100644 --- a/ui/app/AppLayouts/Wallet/components/AddWatchOnlyAccount.qml +++ b/ui/app/AppLayouts/Wallet/components/AddWatchOnlyAccount.qml @@ -1,4 +1,5 @@ import QtQuick 2.13 +import QtQuick.Controls 2.13 import QtQuick.Dialogs 1.3 import "../../../../imports" import "../../../../shared" @@ -9,7 +10,6 @@ ModalPopup { title: qsTrId("add-watch-account") property int marginBetweenInputs: 38 - property string selectedColor: Constants.accountColors[0] property string addressError: "" property string accountNameValidationError: "" property bool loading: false @@ -61,23 +61,14 @@ ModalPopup { validationError: popup.accountNameValidationError } - Select { + ColorSelector { id: accountColorInput + selectedColor: Constants.accountColors[0] + model: Constants.accountColors anchors.top: accountNameInput.bottom anchors.topMargin: marginBetweenInputs - bgColor: selectedColor - //% "Account color" - label: qsTrId("account-color") - selectOptions: Constants.accountColors.map(color => { - return { - text: "", - bgColor: color, - height: 52, - onClicked: function () { - selectedColor = color - } - } - }) + anchors.left: parent.left + anchors.right: parent.right } footer: StyledButton { @@ -107,7 +98,7 @@ ModalPopup { return loading = false } - const error = walletModel.addWatchOnlyAccount(addressInput.text, accountNameInput.text, selectedColor); + const error = walletModel.addWatchOnlyAccount(addressInput.text, accountNameInput.text, accountColorInput.selectedColor); loading = false if (error) { errorSound.play() diff --git a/ui/app/AppLayouts/Wallet/components/GenerateAccountModal.qml b/ui/app/AppLayouts/Wallet/components/GenerateAccountModal.qml index dc0cc1d00c..f991045199 100644 --- a/ui/app/AppLayouts/Wallet/components/GenerateAccountModal.qml +++ b/ui/app/AppLayouts/Wallet/components/GenerateAccountModal.qml @@ -1,4 +1,5 @@ import QtQuick 2.13 +import QtQuick.Controls 2.13 import QtQuick.Dialogs 1.3 import "../../../../imports" import "../../../../shared" @@ -9,7 +10,6 @@ ModalPopup { title: qsTrId("generate-a-new-account") property int marginBetweenInputs: 38 - property string selectedColor: Constants.accountColors[0] property string passwordValidationError: "" property string accountNameValidationError: "" property bool loading: false @@ -61,23 +61,14 @@ ModalPopup { validationError: popup.accountNameValidationError } - Select { + ColorSelector { id: accountColorInput + selectedColor: Constants.accountColors[0] + model: Constants.accountColors anchors.top: accountNameInput.bottom anchors.topMargin: marginBetweenInputs - bgColor: selectedColor - //% "Account color" - label: qsTrId("account-color") - selectOptions: Constants.accountColors.map(color => { - return { - text: "", - bgColor: color, - height: 52, - onClicked: function () { - selectedColor = color - } - } - }) + anchors.left: parent.left + anchors.right: parent.right } footer: StyledButton { @@ -107,7 +98,7 @@ ModalPopup { return loading = false } - const error = walletModel.generateNewAccount(passwordInput.text, accountNameInput.text, selectedColor) + const error = walletModel.generateNewAccount(passwordInput.text, accountNameInput.text, accountColorInput.selectedColor) loading = false if (error) { errorSound.play() diff --git a/ui/app/AppLayouts/Wallet/components/SendModalContent.qml b/ui/app/AppLayouts/Wallet/components/SendModalContent.qml index eb11edb903..f7853a0aaf 100644 --- a/ui/app/AppLayouts/Wallet/components/SendModalContent.qml +++ b/ui/app/AppLayouts/Wallet/components/SendModalContent.qml @@ -1,4 +1,5 @@ import QtQuick 2.13 +import QtQuick.Controls 2.13 import QtQuick.Dialogs 1.3 import "../../../../imports" import "../../../../shared" @@ -8,14 +9,8 @@ Item { property var closePopup: function(){} property alias amountInput: txtAmount property alias passwordInput: txtPassword - property var accounts: [] property var assets: [] - property int selectedAccountIndex: 0 - property string selectedAccountAddress: accounts && accounts.length ? accounts[selectedAccountIndex].address : "" - property string selectedAccountName: accounts && accounts.length ? accounts[selectedAccountIndex].name : "" - property string selectedAccountIconColor: accounts && accounts.length ? accounts[selectedAccountIndex].iconColor : "" - property int selectedAssetIndex: 0 property string selectedAssetName: assets && assets.length ? assets[selectedAssetIndex].name : "" property string selectedAssetAddress: assets && assets.length ? assets[selectedAssetIndex].address : "" @@ -30,8 +25,7 @@ Item { if (!validate()) { return; } - - let result = walletModel.onSendTransaction(selectedAccountAddress, + let result = walletModel.onSendTransaction(selectFromAccount.selectedAccount.address, txtTo.text, selectedAssetAddress, txtAmount.text, @@ -125,14 +119,27 @@ Item { anchors.top: txtAmount.bottom anchors.topMargin: Style.current.padding selectedText: selectedAssetName - selectOptions: sendModalContent.assets.map(function (asset, index) { - return { - text: asset.name, - onClicked: function () { + model: sendModalContent.assets + menu.delegate: Component { + MenuItem { + + height: itemText.height + 4 + width: parent ? parent.width : selectMenu.width + padding: 10 + + StyledText { + id: itemText + text: sendModalContent.assets[index].name + anchors.left: parent.left + anchors.leftMargin: 5 + anchors.verticalCenter: parent.verticalCenter + } + + onTriggered: function () { selectedAssetIndex = index } } - }) + } } StyledText { @@ -147,38 +154,14 @@ Item { anchors.rightMargin: 0 } - Select { - id: txtFrom - iconHeight: 12 - iconWidth: 12 - icon: "../../../img/walletIcon.svg" - iconColor: selectedAccountIconColor - //% "From account" - label: qsTrId("from-account") + + AccountSelector { + id: selectFromAccount + accounts: walletModel.accounts anchors.top: assetTypeSelect.bottom anchors.topMargin: Style.current.padding - selectedText: selectedAccountName - selectOptions: sendModalContent.accounts.map(function (account, index) { - return { - text: account.name, - onClicked: function () { - selectedAccountIndex = index - } - } - }) - } - - StyledText { - id: textSelectAccountAddress - text: selectedAccountAddress - font.family: Style.current.fontHexRegular.name - anchors.right: parent.right anchors.left: parent.left - anchors.leftMargin: 2 - elide: Text.ElideMiddle - anchors.top: txtFrom.bottom - font.pixelSize: 12 - color: Style.current.darkGrey + anchors.right: parent.right } Input { @@ -187,7 +170,7 @@ Item { label: qsTrId("recipient") //% "Send to" placeholderText: qsTrId("send-to") - anchors.top: textSelectAccountAddress.bottom + anchors.top: selectFromAccount.bottom anchors.topMargin: Style.current.padding validationError: toValidationError } diff --git a/ui/imports/Themes/DarkTheme.qml b/ui/imports/Themes/DarkTheme.qml index 6fefc776b2..11bd0b5c78 100644 --- a/ui/imports/Themes/DarkTheme.qml +++ b/ui/imports/Themes/DarkTheme.qml @@ -25,4 +25,8 @@ Theme { property color inputBackground: secondaryBackground property color inputColor: darkGrey property color modalBackground: background + property color backgroundHover: "#252528" + property color secondaryText: darkGrey + property color secondaryHover: Qt.rgba(255, 255, 255, 0.1) + property color danger: red } diff --git a/ui/imports/Themes/LightTheme.qml b/ui/imports/Themes/LightTheme.qml index 9b67a272e0..df566c56f3 100644 --- a/ui/imports/Themes/LightTheme.qml +++ b/ui/imports/Themes/LightTheme.qml @@ -25,4 +25,8 @@ Theme { property color inputBackground: grey property color inputColor: black property color modalBackground: white2 + property color backgroundHover: grey + property color secondaryText: darkGrey + property color secondaryHover: Qt.rgba(0, 0, 0, 0.1) + property color danger: red } diff --git a/ui/nim-status-client.pro b/ui/nim-status-client.pro index fb40e51e3c..96361947dc 100644 --- a/ui/nim-status-client.pro +++ b/ui/nim-status-client.pro @@ -309,6 +309,7 @@ DISTFILES += \ onboarding/img/wallet@2x.jpg \ onboarding/img/wallet@3x.jpg \ onboarding/qmldir \ + shared/AccountSelector.qml \ shared/AddButton.qml \ shared/IconButton.qml \ shared/Input.qml \ diff --git a/ui/shared/AccountSelector.qml b/ui/shared/AccountSelector.qml new file mode 100644 index 0000000000..0f8f4ad4c1 --- /dev/null +++ b/ui/shared/AccountSelector.qml @@ -0,0 +1,190 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.13 +import QtGraphicalEffects 1.13 +import "../imports" + +Item { + id: root + property string label: qsTr("Choose account") + property var accounts + property var selectedAccount: { + "address": "", "name": "", "iconColor": "", "fiatBalance": "" + } + height: select.height + selectedAccountDetails.height + // set to asset symbol to display asset's balance top right + // NOTE: if this asset is not selected as a wallet token in the UI, then + // nothing will be displayed + property string showAssetBalance: "" + + Repeater { + visible: showAssetBalance !== "" + model: selectedAccount.assets + delegate: StyledText { + visible: symbol === root.showAssetBalance.toUpperCase() + anchors.bottom: select.top + anchors.bottomMargin: -18 + anchors.right: parent.right + text: "Balance: " + (parseFloat(value) === 0.0 ? "0" : value) + " " + symbol + color: parseFloat(value) === 0.0 ? Style.current.danger : Style.current.secondaryText + font.pixelSize: 13 + height: 18 + } + } + Select { + id: select + icon: "../app/img/walletIcon.svg" + iconColor: selectedAccount.iconColor || Style.current.blue + label: root.label + selectedText: selectedAccount.name + model: root.accounts + + menu.delegate: menuItem + menu.onOpened: { + selectedAccountDetails.visible = false + } + menu.onClosed: { + selectedAccountDetails.visible = true + } + } + + Row { + id: selectedAccountDetails + anchors.top: select.bottom + anchors.topMargin: 8 + anchors.left: parent.left + anchors.leftMargin: 2 + + StyledText { + id: textSelectedAddress + text: selectedAccount.address + font.pixelSize: 12 + elide: Text.ElideMiddle + height: 16 + width: 85 + color: Style.current.secondaryText + } + StyledText { + id: separator + text: "• " + font.pixelSize: 12 + height: 16 + color: Style.current.secondaryText + } + StyledText { + text: selectedAccount.fiatBalance + " " + walletModel.defaultCurrency.toUpperCase() + font.pixelSize: 12 + height: 16 + color: Style.current.secondaryText + } + } + + Component { + id: menuItem + MenuItem { + id: itemContainer + property bool isFirstItem: index === 0 + property bool isLastItem: index === accounts.rowCount() - 1 + + Component.onCompleted: { + if (root.selectedAccount.address === "") { + root.selectedAccount = { address, name, iconColor, assets, fiatBalance } + } + } + + height: accountName.height + 14 + accountAddress.height + 14 + SVGImage { + id: iconImg + anchors.left: parent.left + anchors.leftMargin: Style.current.padding + anchors.verticalCenter: parent.verticalCenter + width: select.iconWidth + height: select.iconHeight + sourceSize.height: select.iconHeight + sourceSize.width: select.iconWidth + fillMode: Image.PreserveAspectFit + source: select.icon + } + ColorOverlay { + anchors.fill: iconImg + source: iconImg + color: iconColor + } + Column { + anchors.left: iconImg.right + anchors.leftMargin: 14 + anchors.verticalCenter: parent.verticalCenter + + StyledText { + id: accountName + text: name + font.pixelSize: 15 + height: 22 + } + + StyledText { + id: accountAddress + text: address + elide: Text.ElideMiddle + width: 80 + color: Style.current.secondaryText + font.pixelSize: 12 + height: 16 + } + } + StyledText { + anchors.right: fiatCurrencySymbol.left + anchors.rightMargin: 4 + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: 15 + height: 22 + text: fiatBalance + } + StyledText { + id: fiatCurrencySymbol + anchors.right: parent.right + anchors.rightMargin: Style.current.padding + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: 15 + height: 22 + color: Style.current.secondaryText + text: walletModel.defaultCurrency.toUpperCase() + } + background: Rectangle { + color: itemContainer.highlighted ? Style.current.backgroundHover : Style.current.background + radius: Style.current.radius + + // cover bottom left/right corners with square corners + Rectangle { + visible: !isLastItem + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: parent.radius + color: parent.color + } + + // cover top left/right corners with square corners + Rectangle { + visible: !isFirstItem + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + height: parent.radius + color: parent.color + } + } + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: itemContainer + onClicked: { + root.selectedAccount = { address, name, iconColor, assets, fiatBalance } + select.menu.close() + } + } + } + } +} + + + diff --git a/ui/shared/ColorSelector.qml b/ui/shared/ColorSelector.qml new file mode 100644 index 0000000000..ac5020279a --- /dev/null +++ b/ui/shared/ColorSelector.qml @@ -0,0 +1,57 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import "../imports" + +Item { + id: root + property string selectedColor + //% "Account color" + property string label: qsTrId("account-color") + property var model + height: accountColorInput.height + + Select { + id: accountColorInput + bgColor: selectedColor + label: root.label + model: root.model + + menu.delegate: Component { + MenuItem { + property bool isFirstItem: index === 0 + property bool isLastItem: index === root.model.length - 1 + height: 52 + width: parent.width + padding: 10 + onTriggered: function () { + const selectedColor = root.model[index] + root.selectedColor = selectedColor + } + background: Rectangle { + color: root.model[index] || Style.current.transparent + radius: Style.current.radius + + // cover bottom left/right corners with square corners + Rectangle { + visible: !isLastItem + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: parent.radius + color: parent.color + } + + // cover top left/right corners with square corners + Rectangle { + visible: !isFirstItem + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + height: parent.radius + color: parent.color + } + } + } + } + } +} \ No newline at end of file diff --git a/ui/shared/Select.qml b/ui/shared/Select.qml index 31fae36105..d66df1d262 100644 --- a/ui/shared/Select.qml +++ b/ui/shared/Select.qml @@ -9,34 +9,32 @@ Item { readonly property bool hasLabel: label !== "" property color bgColor: Style.current.inputBackground readonly property int labelMargin: 7 - property var selectOptions - property int customHeight: 44 + property var model + property int customHeight: 56 property string selectedText: "" property url icon: "" - property int iconHeight: 24 - property int iconWidth: 24 + property int iconHeight: 12 + property int iconWidth: 12 property color iconColor: Style.current.transparent + property alias menu: selectMenu readonly property bool hasIcon: icon.toString() !== "" - id: inputBox + id: root height: inputRectangle.height + (hasLabel ? inputLabel.height + labelMargin : 0) anchors.right: parent.right anchors.left: parent.left - onSelectOptionsChanged: { - selectMenu.setupMenuItems() - } - StyledText { id: inputLabel - text: inputBox.label + text: root.label font.weight: Font.Medium anchors.left: parent.left anchors.leftMargin: 0 anchors.top: parent.top anchors.topMargin: 0 font.pixelSize: 13 + height: 18 } Rectangle { @@ -44,20 +42,21 @@ Item { height: customHeight color: bgColor radius: Style.current.radius - anchors.top: inputBox.hasLabel ? inputLabel.bottom : parent.top - anchors.topMargin: inputBox.hasLabel ? inputBox.labelMargin : 0 + anchors.top: root.hasLabel ? inputLabel.bottom : parent.top + anchors.topMargin: root.hasLabel ? root.labelMargin : 0 anchors.right: parent.right anchors.left: parent.left SVGImage { id: iconImg + visible: root.hasIcon sourceSize.height: iconHeight sourceSize.width: iconWidth anchors.left: parent.left - anchors.leftMargin: Style.current.smallPadding + anchors.leftMargin: Style.current.padding anchors.verticalCenter: parent.verticalCenter fillMode: Image.PreserveAspectFit - source: inputBox.icon + source: root.icon } ColorOverlay { anchors.fill: iconImg @@ -67,12 +66,13 @@ Item { StyledText { id: selectedTextField - visible: inputBox.selectedText !== "" - text: inputBox.selectedText - anchors.left: parent.left - anchors.leftMargin: inputBox.hasIcon ? iconWidth + 20 : Style.current.padding + visible: root.selectedText !== "" + text: root.selectedText + anchors.left: iconImg.right + anchors.leftMargin: hasIcon ? 8 : 0 anchors.verticalCenter: parent.verticalCenter font.pixelSize: 15 + height: 22 } SVGImage { @@ -88,80 +88,61 @@ Item { ColorOverlay { anchors.fill: caret source: caret - color: Style.current.darkGrey - } - - Menu { - property var items: [] - - id: selectMenu - width: parent.width - padding: 10 - background: Rectangle { - width: parent.width - height: parent.height - color: Style.current.inputBackground - radius: Style.current.radius - } - - function setupMenuItems() { - if (selectMenu.items.length) { - // Remove old items - selectMenu.items.forEach(function (item) { - selectMenu.removeItem(item) - }) - selectMenu.items = [] - } - if (!selectOptions) { - return - } - - selectOptions.forEach(function (element) { - var item = menuItem.createObject(undefined, element) - selectMenu.items.push(item) - selectMenu.addItem(item) - }) - } - - Component.onCompleted: { - setupMenuItems() - } - - Component { - id: menuItem - MenuItem { - id: itemContainer - property var onClicked: function () {} - property string label: "" - property color bgColor: Style.current.transparent - - height: itemText.height + 4 - width: parent ? parent.width : selectMenu.width - - StyledText { - id: itemText - text: itemContainer.label - anchors.left: parent ? parent.left : undefined - anchors.leftMargin: 5 - anchors.verticalCenter: parent ? parent.verticalCenter : undefined - } - - onTriggered: function () { - onClicked() - } - background: Rectangle { - color: bgColor - } - } - } + color: Style.current.secondaryText } } + // create a drop shadow externally so that it is not clipped by the + // rounded corners of the menu background + Rectangle { + width: selectMenu.width + height: selectMenu.height + x: selectMenu.x + y: selectMenu.y + visible: selectMenu.opened + color: Style.current.background + radius: Style.current.radius + border.color: Style.current.border + layer.enabled: true + layer.effect: DropShadow { + verticalOffset: 3 + radius: Style.current.radius + samples: 15 + fast: true + cached: true + color: "#22000000" + } + } + + Menu { + id: selectMenu + property var items: [] + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + width: parent.width + background: Rectangle { + // do not add a drop shadow effect here or it will be clipped + radius: Style.current.radius + color: Style.current.background + } + clip: true + delegate: menuItem + + Repeater { + id: menuItems + model: root.model + delegate: selectMenu.delegate + } + } MouseArea { id: mouseArea - anchors.fill: parent + anchors.fill: inputRectangle + cursorShape: Qt.PointingHandCursor onClicked: { - selectMenu.open() + if (selectMenu.opened) { + selectMenu.close() + } else { + selectMenu.popup(inputRectangle.x, inputRectangle.y + inputRectangle.height + 8) + } } } }