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 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) =

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 !== ""
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)
}
}
}
}