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.
This commit is contained in:
emizzle 2020-07-30 15:18:54 +10:00
parent cb92c1dc14
commit 9374be5857
No known key found for this signature in database
GPG Key ID: 1FD4BAB3C37EE9BA
15 changed files with 429 additions and 235 deletions

View File

@ -1,9 +1,11 @@
import NimQml, Tables, random import NimQml, Tables, random, strformat, json_serialization
import sequtils as sequtils import sequtils as sequtils
import account_item import account_item, asset_list
from ../../../status/wallet import WalletAccount from ../../../status/wallet import WalletAccount
const accountColors* = ["#9B832F", "#D37EF4", "#1D806F", "#FA6565", "#7CDA00", "#887af9", "#8B3131"] const accountColors* = ["#9B832F", "#D37EF4", "#1D806F", "#FA6565", "#7CDA00", "#887af9", "#8B3131"]
type
AccountView* = tuple[account: WalletAccount, assets: AssetList]
type type
AccountRoles {.pure.} = enum AccountRoles {.pure.} = enum
@ -11,10 +13,12 @@ type
Address = UserRole + 2, Address = UserRole + 2,
Color = UserRole + 3, Color = UserRole + 3,
Balance = UserRole + 4 Balance = UserRole + 4
FiatBalance = UserRole + 5
Assets = UserRole + 6
QtObject: QtObject:
type AccountList* = ref object of QAbstractListModel type AccountList* = ref object of QAbstractListModel
accounts*: seq[WalletAccount] accounts*: seq[AccountView]
proc setup(self: AccountList) = self.QAbstractListModel.setup proc setup(self: AccountList) = self.QAbstractListModel.setup
@ -27,12 +31,14 @@ QtObject:
result.accounts = @[] result.accounts = @[]
result.setup 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.} = proc rowData(self: AccountList, index: int, column: string): string {.slot.} =
if (index >= self.accounts.len): if (index >= self.accounts.len):
return return
let account = self.accounts[index] let
accountView = self.accounts[index]
account = accountView.account
case column: case column:
of "name": result = account.name of "name": result = account.name
of "address": result = account.address of "address": result = account.address
@ -40,11 +46,12 @@ QtObject:
of "balance": result = account.balance of "balance": result = account.balance
of "path": result = account.path of "path": result = account.path
of "walletType": result = account.walletType of "walletType": result = account.walletType
of "fiatBalance": result = fmt"{account.realFiatBalance:>.2f}"
proc getAccountindexByAddress*(self: AccountList, address: string): int = proc getAccountindexByAddress*(self: AccountList, address: string): int =
var i = 0 var i = 0
for account in self.accounts: for accountView in self.accounts:
if (account.address == address): if (accountView.account.address == address):
return i return i
i = i + 1 i = i + 1
return -1 return -1
@ -60,26 +67,33 @@ QtObject:
return return
if index.row < 0 or index.row >= self.accounts.len: if index.row < 0 or index.row >= self.accounts.len:
return return
let account = self.accounts[index.row] let accountView = self.accounts[index.row]
let account = accountView.account
let accountRole = role.AccountRoles let accountRole = role.AccountRoles
case accountRole: case accountRole:
of AccountRoles.Name: result = newQVariant(account.name) of AccountRoles.Name: result = newQVariant(account.name)
of AccountRoles.Address: result = newQVariant(account.address) of AccountRoles.Address: result = newQVariant(account.address)
of AccountRoles.Color: result = newQVariant(account.iconColor) of AccountRoles.Color: result = newQVariant(account.iconColor)
of AccountRoles.Balance: result = newQVariant(account.balance) 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] = method roleNames(self: AccountList): Table[int, string] =
{ AccountRoles.Name.int:"name", { AccountRoles.Name.int:"name",
AccountRoles.Address.int:"address", AccountRoles.Address.int:"address",
AccountRoles.Color.int:"iconColor", 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) = proc addAccountToList*(self: AccountList, account: WalletAccount) =
if account.iconColor == "": if account.iconColor == "":
randomize() randomize()
account.iconColor = accountColors[rand(accountColors.len - 1)] account.iconColor = accountColors[rand(accountColors.len - 1)]
let assets = newAssetList()
assets.setNewData(account.assetList)
self.beginInsertRows(newQModelIndex(), self.accounts.len, self.accounts.len) self.beginInsertRows(newQModelIndex(), self.accounts.len, self.accounts.len)
self.accounts.add(account) self.accounts.add((account: account, assets: assets))
self.endInsertRows() self.endInsertRows()
proc forceUpdate*(self: AccountList) = proc forceUpdate*(self: AccountList) =

View File

@ -33,20 +33,37 @@ Item {
text: qsTr("Language") text: qsTr("Language")
} }
Select { Select {
id: select
selectedText: languageSetting.currentLocale selectedText: languageSetting.currentLocale
anchors.right: undefined anchors.right: undefined
anchors.left: undefined anchors.left: undefined
width: 100 width: 100
Layout.leftMargin: Style.current.padding Layout.leftMargin: Style.current.padding
selectOptions: Locales_JSON.locales.map(locale => { model: Locales_JSON.locales
return { menu.delegate: Component {
label: locale, MenuItem {
onClicked: function () { id: menuItem
height: itemText.height + 4
width: parent.width
padding: 10
onTriggered: function () {
const locale = Locales_JSON.locales[index]
profileModel.changeLocale(locale) profileModel.changeLocale(locale)
appSettings.locale = 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
}
}
} }
})
} }
} }
} }

View File

@ -15,7 +15,6 @@ ModalPopup {
height: 675 height: 675
property int marginBetweenInputs: 35 property int marginBetweenInputs: 35
property string selectedColor: currentAccount.iconColor
property string accountNameValidationError: "" property string accountNameValidationError: ""
function validate() { function validate() {
@ -43,23 +42,14 @@ ModalPopup {
validationError: popup.accountNameValidationError validationError: popup.accountNameValidationError
} }
Select { ColorSelector {
id: accountColorInput id: accountColorInput
selectedColor: currentAccount.iconColor
model: Constants.accountColors
anchors.top: accountNameInput.bottom anchors.top: accountNameInput.bottom
anchors.topMargin: marginBetweenInputs anchors.topMargin: marginBetweenInputs
bgColor: selectedColor anchors.left: parent.left
//% "Account color" anchors.right: parent.right
label: qsTrId("account-color")
selectOptions: Constants.accountColors.map(color => {
return {
text: "",
bgColor: color,
height: 52,
onClicked: function () {
selectedColor = color
}
}
})
} }
TextWithLabel { TextWithLabel {
@ -181,7 +171,7 @@ ModalPopup {
return return
} }
const error = walletModel.changeAccountSettings(currentAccount.address, accountNameInput.text, selectedColor); const error = walletModel.changeAccountSettings(currentAccount.address, accountNameInput.text, accountColorInput.selectedColor);
if (error) { if (error) {
errorSound.play() errorSound.play()

View File

@ -17,17 +17,6 @@ ModalPopup {
sendModalContent.amountInput.text = "" sendModalContent.amountInput.text = ""
sendModalContent.passwordInput.text = "" sendModalContent.passwordInput.text = ""
sendModalContent.amountInput.forceActiveFocus(Qt.MouseFocusReason) 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 assets = walletModel.assets
const numAssets = assets.rowCount() const numAssets = assets.rowCount()

View File

@ -1,4 +1,5 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.3 import QtQuick.Dialogs 1.3
import "../../../../imports" import "../../../../imports"
import "../../../../shared" import "../../../../shared"
@ -10,7 +11,6 @@ ModalPopup {
height: 600 height: 600
property int marginBetweenInputs: 38 property int marginBetweenInputs: 38
property string selectedColor: Constants.accountColors[0]
property string passwordValidationError: "" property string passwordValidationError: ""
property string privateKeyValidationError: "" property string privateKeyValidationError: ""
property string accountNameValidationError: "" property string accountNameValidationError: ""
@ -86,23 +86,14 @@ ModalPopup {
validationError: popup.accountNameValidationError validationError: popup.accountNameValidationError
} }
Select { ColorSelector {
id: accountColorInput id: accountColorInput
selectedColor: Constants.accountColors[0]
model: Constants.accountColors
anchors.top: accountNameInput.bottom anchors.top: accountNameInput.bottom
anchors.topMargin: marginBetweenInputs anchors.topMargin: marginBetweenInputs
bgColor: selectedColor anchors.left: parent.left
//% "Account color" anchors.right: parent.right
label: qsTrId("account-color")
selectOptions: Constants.accountColors.map(color => {
return {
text: "",
bgColor: color,
height: 52,
onClicked: function () {
selectedColor = color
}
}
})
} }
footer: StyledButton { footer: StyledButton {

View File

@ -1,4 +1,5 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.3 import QtQuick.Dialogs 1.3
import "../../../../imports" import "../../../../imports"
import "../../../../shared" import "../../../../shared"
@ -8,7 +9,6 @@ ModalPopup {
height: 600 height: 600
property int marginBetweenInputs: 38 property int marginBetweenInputs: 38
property string selectedColor: Constants.accountColors[0]
property string passwordValidationError: "" property string passwordValidationError: ""
property string seedValidationError: "" property string seedValidationError: ""
property string accountNameValidationError: "" property string accountNameValidationError: ""
@ -87,23 +87,14 @@ ModalPopup {
validationError: popup.accountNameValidationError validationError: popup.accountNameValidationError
} }
Select { ColorSelector {
id: accountColorInput id: accountColorInput
selectedColor: Constants.accountColors[0]
model: Constants.accountColors
anchors.top: accountNameInput.bottom anchors.top: accountNameInput.bottom
anchors.topMargin: marginBetweenInputs anchors.topMargin: marginBetweenInputs
bgColor: selectedColor anchors.left: parent.left
//% "Account color" anchors.right: parent.right
label: qsTrId("account-color")
selectOptions: Constants.accountColors.map(color => {
return {
text: "",
bgColor: color,
height: 52,
onClicked: function () {
selectedColor = color
}
}
})
} }
footer: StyledButton { footer: StyledButton {
@ -133,7 +124,7 @@ ModalPopup {
return loading = false 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 loading = false
if (error) { if (error) {
errorSound.play() errorSound.play()

View File

@ -1,4 +1,5 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.3 import QtQuick.Dialogs 1.3
import "../../../../imports" import "../../../../imports"
import "../../../../shared" import "../../../../shared"
@ -9,7 +10,6 @@ ModalPopup {
title: qsTrId("add-watch-account") title: qsTrId("add-watch-account")
property int marginBetweenInputs: 38 property int marginBetweenInputs: 38
property string selectedColor: Constants.accountColors[0]
property string addressError: "" property string addressError: ""
property string accountNameValidationError: "" property string accountNameValidationError: ""
property bool loading: false property bool loading: false
@ -61,23 +61,14 @@ ModalPopup {
validationError: popup.accountNameValidationError validationError: popup.accountNameValidationError
} }
Select { ColorSelector {
id: accountColorInput id: accountColorInput
selectedColor: Constants.accountColors[0]
model: Constants.accountColors
anchors.top: accountNameInput.bottom anchors.top: accountNameInput.bottom
anchors.topMargin: marginBetweenInputs anchors.topMargin: marginBetweenInputs
bgColor: selectedColor anchors.left: parent.left
//% "Account color" anchors.right: parent.right
label: qsTrId("account-color")
selectOptions: Constants.accountColors.map(color => {
return {
text: "",
bgColor: color,
height: 52,
onClicked: function () {
selectedColor = color
}
}
})
} }
footer: StyledButton { footer: StyledButton {
@ -107,7 +98,7 @@ ModalPopup {
return loading = false return loading = false
} }
const error = walletModel.addWatchOnlyAccount(addressInput.text, accountNameInput.text, selectedColor); const error = walletModel.addWatchOnlyAccount(addressInput.text, accountNameInput.text, accountColorInput.selectedColor);
loading = false loading = false
if (error) { if (error) {
errorSound.play() errorSound.play()

View File

@ -1,4 +1,5 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.3 import QtQuick.Dialogs 1.3
import "../../../../imports" import "../../../../imports"
import "../../../../shared" import "../../../../shared"
@ -9,7 +10,6 @@ ModalPopup {
title: qsTrId("generate-a-new-account") title: qsTrId("generate-a-new-account")
property int marginBetweenInputs: 38 property int marginBetweenInputs: 38
property string selectedColor: Constants.accountColors[0]
property string passwordValidationError: "" property string passwordValidationError: ""
property string accountNameValidationError: "" property string accountNameValidationError: ""
property bool loading: false property bool loading: false
@ -61,23 +61,14 @@ ModalPopup {
validationError: popup.accountNameValidationError validationError: popup.accountNameValidationError
} }
Select { ColorSelector {
id: accountColorInput id: accountColorInput
selectedColor: Constants.accountColors[0]
model: Constants.accountColors
anchors.top: accountNameInput.bottom anchors.top: accountNameInput.bottom
anchors.topMargin: marginBetweenInputs anchors.topMargin: marginBetweenInputs
bgColor: selectedColor anchors.left: parent.left
//% "Account color" anchors.right: parent.right
label: qsTrId("account-color")
selectOptions: Constants.accountColors.map(color => {
return {
text: "",
bgColor: color,
height: 52,
onClicked: function () {
selectedColor = color
}
}
})
} }
footer: StyledButton { footer: StyledButton {
@ -107,7 +98,7 @@ ModalPopup {
return loading = false return loading = false
} }
const error = walletModel.generateNewAccount(passwordInput.text, accountNameInput.text, selectedColor) const error = walletModel.generateNewAccount(passwordInput.text, accountNameInput.text, accountColorInput.selectedColor)
loading = false loading = false
if (error) { if (error) {
errorSound.play() errorSound.play()

View File

@ -1,4 +1,5 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.3 import QtQuick.Dialogs 1.3
import "../../../../imports" import "../../../../imports"
import "../../../../shared" import "../../../../shared"
@ -8,14 +9,8 @@ Item {
property var closePopup: function(){} property var closePopup: function(){}
property alias amountInput: txtAmount property alias amountInput: txtAmount
property alias passwordInput: txtPassword property alias passwordInput: txtPassword
property var accounts: []
property var assets: [] 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 int selectedAssetIndex: 0
property string selectedAssetName: assets && assets.length ? assets[selectedAssetIndex].name : "" property string selectedAssetName: assets && assets.length ? assets[selectedAssetIndex].name : ""
property string selectedAssetAddress: assets && assets.length ? assets[selectedAssetIndex].address : "" property string selectedAssetAddress: assets && assets.length ? assets[selectedAssetIndex].address : ""
@ -30,8 +25,7 @@ Item {
if (!validate()) { if (!validate()) {
return; return;
} }
let result = walletModel.onSendTransaction(selectFromAccount.selectedAccount.address,
let result = walletModel.onSendTransaction(selectedAccountAddress,
txtTo.text, txtTo.text,
selectedAssetAddress, selectedAssetAddress,
txtAmount.text, txtAmount.text,
@ -125,14 +119,27 @@ Item {
anchors.top: txtAmount.bottom anchors.top: txtAmount.bottom
anchors.topMargin: Style.current.padding anchors.topMargin: Style.current.padding
selectedText: selectedAssetName selectedText: selectedAssetName
selectOptions: sendModalContent.assets.map(function (asset, index) { model: sendModalContent.assets
return { menu.delegate: Component {
text: asset.name, MenuItem {
onClicked: function () {
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 selectedAssetIndex = index
} }
} }
}) }
} }
StyledText { StyledText {
@ -147,38 +154,14 @@ Item {
anchors.rightMargin: 0 anchors.rightMargin: 0
} }
Select {
id: txtFrom AccountSelector {
iconHeight: 12 id: selectFromAccount
iconWidth: 12 accounts: walletModel.accounts
icon: "../../../img/walletIcon.svg"
iconColor: selectedAccountIconColor
//% "From account"
label: qsTrId("from-account")
anchors.top: assetTypeSelect.bottom anchors.top: assetTypeSelect.bottom
anchors.topMargin: Style.current.padding 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.left: parent.left
anchors.leftMargin: 2 anchors.right: parent.right
elide: Text.ElideMiddle
anchors.top: txtFrom.bottom
font.pixelSize: 12
color: Style.current.darkGrey
} }
Input { Input {
@ -187,7 +170,7 @@ Item {
label: qsTrId("recipient") label: qsTrId("recipient")
//% "Send to" //% "Send to"
placeholderText: qsTrId("send-to") placeholderText: qsTrId("send-to")
anchors.top: textSelectAccountAddress.bottom anchors.top: selectFromAccount.bottom
anchors.topMargin: Style.current.padding anchors.topMargin: Style.current.padding
validationError: toValidationError validationError: toValidationError
} }

View File

@ -25,4 +25,8 @@ Theme {
property color inputBackground: secondaryBackground property color inputBackground: secondaryBackground
property color inputColor: darkGrey property color inputColor: darkGrey
property color modalBackground: background 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
} }

View File

@ -25,4 +25,8 @@ Theme {
property color inputBackground: grey property color inputBackground: grey
property color inputColor: black property color inputColor: black
property color modalBackground: white2 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
} }

View File

@ -309,6 +309,7 @@ DISTFILES += \
onboarding/img/wallet@2x.jpg \ onboarding/img/wallet@2x.jpg \
onboarding/img/wallet@3x.jpg \ onboarding/img/wallet@3x.jpg \
onboarding/qmldir \ onboarding/qmldir \
shared/AccountSelector.qml \
shared/AddButton.qml \ shared/AddButton.qml \
shared/IconButton.qml \ shared/IconButton.qml \
shared/Input.qml \ shared/Input.qml \

View File

@ -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()
}
}
}
}
}

View File

@ -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
}
}
}
}
}
}

View File

@ -9,34 +9,32 @@ Item {
readonly property bool hasLabel: label !== "" readonly property bool hasLabel: label !== ""
property color bgColor: Style.current.inputBackground property color bgColor: Style.current.inputBackground
readonly property int labelMargin: 7 readonly property int labelMargin: 7
property var selectOptions property var model
property int customHeight: 44 property int customHeight: 56
property string selectedText: "" property string selectedText: ""
property url icon: "" property url icon: ""
property int iconHeight: 24 property int iconHeight: 12
property int iconWidth: 24 property int iconWidth: 12
property color iconColor: Style.current.transparent property color iconColor: Style.current.transparent
property alias menu: selectMenu
readonly property bool hasIcon: icon.toString() !== "" readonly property bool hasIcon: icon.toString() !== ""
id: inputBox id: root
height: inputRectangle.height + (hasLabel ? inputLabel.height + labelMargin : 0) height: inputRectangle.height + (hasLabel ? inputLabel.height + labelMargin : 0)
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
onSelectOptionsChanged: {
selectMenu.setupMenuItems()
}
StyledText { StyledText {
id: inputLabel id: inputLabel
text: inputBox.label text: root.label
font.weight: Font.Medium font.weight: Font.Medium
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 0 anchors.leftMargin: 0
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 0 anchors.topMargin: 0
font.pixelSize: 13 font.pixelSize: 13
height: 18
} }
Rectangle { Rectangle {
@ -44,20 +42,21 @@ Item {
height: customHeight height: customHeight
color: bgColor color: bgColor
radius: Style.current.radius radius: Style.current.radius
anchors.top: inputBox.hasLabel ? inputLabel.bottom : parent.top anchors.top: root.hasLabel ? inputLabel.bottom : parent.top
anchors.topMargin: inputBox.hasLabel ? inputBox.labelMargin : 0 anchors.topMargin: root.hasLabel ? root.labelMargin : 0
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
SVGImage { SVGImage {
id: iconImg id: iconImg
visible: root.hasIcon
sourceSize.height: iconHeight sourceSize.height: iconHeight
sourceSize.width: iconWidth sourceSize.width: iconWidth
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Style.current.smallPadding anchors.leftMargin: Style.current.padding
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: inputBox.icon source: root.icon
} }
ColorOverlay { ColorOverlay {
anchors.fill: iconImg anchors.fill: iconImg
@ -67,12 +66,13 @@ Item {
StyledText { StyledText {
id: selectedTextField id: selectedTextField
visible: inputBox.selectedText !== "" visible: root.selectedText !== ""
text: inputBox.selectedText text: root.selectedText
anchors.left: parent.left anchors.left: iconImg.right
anchors.leftMargin: inputBox.hasIcon ? iconWidth + 20 : Style.current.padding anchors.leftMargin: hasIcon ? 8 : 0
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 15 font.pixelSize: 15
height: 22
} }
SVGImage { SVGImage {
@ -88,80 +88,61 @@ Item {
ColorOverlay { ColorOverlay {
anchors.fill: caret anchors.fill: caret
source: caret source: caret
color: Style.current.darkGrey 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 { Menu {
property var items: []
id: selectMenu id: selectMenu
property var items: []
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
width: parent.width width: parent.width
padding: 10
background: Rectangle { background: Rectangle {
width: parent.width // do not add a drop shadow effect here or it will be clipped
height: parent.height
color: Style.current.inputBackground
radius: Style.current.radius radius: Style.current.radius
color: Style.current.background
} }
clip: true
delegate: menuItem
function setupMenuItems() { Repeater {
if (selectMenu.items.length) { id: menuItems
// Remove old items model: root.model
selectMenu.items.forEach(function (item) { delegate: selectMenu.delegate
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
} }
} }
}
}
}
MouseArea { MouseArea {
id: mouseArea id: mouseArea
anchors.fill: parent anchors.fill: inputRectangle
cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
selectMenu.open() if (selectMenu.opened) {
selectMenu.close()
} else {
selectMenu.popup(inputRectangle.x, inputRectangle.y + inputRectangle.height + 8)
}
} }
} }
} }