diff --git a/MiddlePanel.qml b/MiddlePanel.qml index 6101b28d..1b1a4a83 100644 --- a/MiddlePanel.qml +++ b/MiddlePanel.qml @@ -37,6 +37,7 @@ import QtGraphicalEffects 1.0 import moneroComponents.Wallet 1.0 import "./pages" +import "./pages/settings" Rectangle { id: root @@ -141,7 +142,7 @@ Rectangle { }, State { name: "Settings" PropertyChanges { target: root; currentView: settingsView } - PropertyChanges { target: mainFlickable; contentHeight: settingsView.settingsHeight + 100 } + PropertyChanges { target: mainFlickable; contentHeight: settingsView.settingsHeight } }, State { name: "Mining" PropertyChanges { target: root; currentView: miningView } diff --git a/components/HistoryTableInnerColumn.qml b/components/HistoryTableInnerColumn.qml index 8a8acbe7..566c2466 100644 --- a/components/HistoryTableInnerColumn.qml +++ b/components/HistoryTableInnerColumn.qml @@ -53,7 +53,7 @@ Rectangle{ font.family: MoneroComponents.Style.fontRegular.name font.pixelSize: 14 * scaleRatio text: labelHeader - color: MoneroComponents.Style.greyFontColor + color: MoneroComponents.Style.dimmedFontColor } Text { @@ -77,7 +77,7 @@ Rectangle{ label2.color = MoneroComponents.Style.defaultFontColor; } onExited: { - label1.color = MoneroComponents.Style.greyFontColor; + label1.color = MoneroComponents.Style.dimmedFontColor label2.color = MoneroComponents.Style.dimmedFontColor; } onClicked: { diff --git a/components/InputDialog.qml b/components/InputDialog.qml index e51a16db..ac56a56b 100644 --- a/components/InputDialog.qml +++ b/components/InputDialog.qml @@ -103,6 +103,8 @@ Item { leftPadding: 10 topPadding: 10 color: MoneroComponents.Style.defaultFontColor + selectionColor: MoneroComponents.Style.dimmedFontColor + selectedTextColor: MoneroComponents.Style.defaultFontColor background: Rectangle { radius: 2 diff --git a/components/InputMulti.qml b/components/InputMulti.qml index b8005354..f7706d75 100644 --- a/components/InputMulti.qml +++ b/components/InputMulti.qml @@ -32,31 +32,25 @@ import QtQuick 2.7 import "../js/TxUtils.js" as TxUtils import "../components" as MoneroComponents - TextArea { - property bool error: false - property bool addressValidation: false - property bool wrapAnywhere: true property int fontSize: 18 * scaleRatio property bool fontBold: false + property string fontColor: MoneroComponents.Style.defaultFontColor + + property bool mouseSelection: true + property bool error: false + property bool addressValidation: false id: textArea font.family: MoneroComponents.Style.fontRegular.name + color: fontColor font.pixelSize: fontSize font.bold: fontBold horizontalAlignment: TextInput.AlignLeft - selectByMouse: true - color: MoneroComponents.Style.defaultFontColor + selectByMouse: mouseSelection selectionColor: MoneroComponents.Style.dimmedFontColor selectedTextColor: MoneroComponents.Style.defaultFontColor - wrapMode: { - if(wrapAnywhere){ - return Text.WrapAnywhere; - } else { - return Text.WordWrap; - } - } onTextChanged: { if(addressValidation){ // js replacement for `RegExpValidator { regExp: /[0-9A-Fa-f]{95}/g }` diff --git a/components/LineEdit.qml b/components/LineEdit.qml index 8a194c12..9eb50b04 100644 --- a/components/LineEdit.qml +++ b/components/LineEdit.qml @@ -50,6 +50,8 @@ Item { property alias inlineButtonText: inlineButtonId.text property alias inlineIcon: inlineIcon.visible property bool copyButton: false + + property bool borderDisabled: false property string borderColor: { if(input.activeFocus){ return MoneroComponents.Style.inputBorderColorActive; @@ -57,9 +59,8 @@ Item { return MoneroComponents.Style.inputBorderColorInActive; } } - property bool borderDisabled: false + property int fontSize: 18 * scaleRatio - property bool showBorder: true property bool fontBold: false property alias fontColor: input.color property bool error: false @@ -140,6 +141,7 @@ Item { anchors.top: showingHeader ? inputLabel.bottom : parent.top anchors.topMargin: showingHeader ? 12 * scaleRatio : 2 * scaleRatio width: parent.width + clip: true Text { id: placeholderLabel @@ -200,6 +202,8 @@ Item { onEditingFinished: item.editingFinished() onAccepted: item.accepted(); onTextChanged: item.textUpdated() + topPadding: 10 * scaleRatio + bottomPadding: 10 * scaleRatio } MoneroComponents.InlineButton { diff --git a/components/LineEditMulti.qml b/components/LineEditMulti.qml index 8243a7ee..87cee6d4 100644 --- a/components/LineEditMulti.qml +++ b/components/LineEditMulti.qml @@ -32,25 +32,56 @@ import QtQuick.Layouts 1.1 import "../components" as MoneroComponents ColumnLayout { - id: lineditmulti - property alias text: multiLine.text - property alias placeholderText: placeholderLabel.text + id: item + + Layout.fillWidth: true + Layout.preferredHeight: childrenRect.height + + property alias text: input.text property alias labelText: inputLabel.text - property alias error: multiLine.error - property alias readOnly: multiLine.readOnly - property alias addressValidation: multiLine.addressValidation property alias labelButtonText: labelButton.text + property alias placeholderText: placeholderLabel.text + + property bool placeholderCenter: false + property string placeholderFontFamily: MoneroComponents.Style.fontRegular.name + property bool placeholderFontBold: false + property int placeholderFontSize: 18 * scaleRatio + property string placeholderColor: MoneroComponents.Style.defaultFontColor + property real placeholderOpacity: 0.35 + + property bool borderDisabled: false + property string borderColor: { + if(input.error && input.text !== ""){ + return MoneroComponents.Style.inputBorderColorInvalid; + } else if(input.activeFocus){ + return MoneroComponents.Style.inputBorderColorActive; + } else { + return MoneroComponents.Style.inputBorderColorInActive; + } + } + + property bool error: false + + property string labelFontColor: MoneroComponents.Style.defaultFontColor property bool labelFontBold: false + property int labelFontSize: 16 * scaleRatio property bool labelButtonVisible: false - property bool copyButton: false - property bool wrapAnywhere: true - property bool showingHeader: true - property bool showBorder: true + + property string fontColor: "white" property bool fontBold: false property int fontSize: 16 * scaleRatio + property bool mouseSelection: true + property alias readOnly: input.readOnly + property bool copyButton: false + property bool showingHeader: true + property var wrapMode: Text.NoWrap + property alias addressValidation: input.addressValidation + property string backgroundColor: "" // mock + signal labelButtonClicked(); signal inputLabelLinkActivated(); + signal editingFinished(); spacing: 0 Rectangle { @@ -65,11 +96,17 @@ ColumnLayout { anchors.top: parent.top anchors.left: parent.left font.family: MoneroComponents.Style.fontRegular.name - font.pixelSize: 16 * scaleRatio + font.pixelSize: item.labelFontSize font.bold: labelFontBold textFormat: Text.RichText - color: MoneroComponents.Style.defaultFontColor + color: item.labelFontColor onLinkActivated: inputLabelLinkActivated() + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } } MoneroComponents.LabelButton { @@ -80,14 +117,14 @@ ColumnLayout { MoneroComponents.LabelButton { id: copyButtonId - visible: copyButton && multiLine.text !== "" + visible: copyButton && input.text !== "" text: qsTr("Copy") anchors.right: labelButton.visible ? inputLabel.right : parent.right anchors.rightMargin: labelButton.visible? 4 : 0 onClicked: { - if (multiLine.text.length > 0) { + if (input.text.length > 0) { console.log("Copied to clipboard"); - clipboard.setText(multiLine.text); + clipboard.setText(input.text); appWindow.showStatusMessage(qsTr("Copied to clipboard"), 3); } } @@ -95,27 +132,32 @@ ColumnLayout { } MoneroComponents.InputMulti { - id: multiLine + id: input readOnly: false - addressValidation: true - anchors.top: parent.showingHeader ? inputLabelRect.bottom : parent.top + addressValidation: false + anchors.top: item.showingHeader ? inputLabelRect.bottom : item.top Layout.fillWidth: true - topPadding: parent.showingHeader ? 10 * scaleRatio : 0 + topPadding: item.showingHeader ? 10 * scaleRatio : 0 bottomPadding: 10 * scaleRatio - wrapAnywhere: parent.wrapAnywhere - fontSize: parent.fontSize - fontBold: parent.fontBold + wrapMode: item.wrapMode + fontSize: item.fontSize + fontBold: item.fontBold + fontColor: item.fontColor + mouseSelection: item.mouseSelection + onEditingFinished: item.editingFinished() + error: item.error Text { id: placeholderLabel - visible: multiLine.text ? false : true + visible: input.text ? false : true anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 10 * scaleRatio - opacity: 0.35 - color: MoneroComponents.Style.defaultFontColor - font.family: MoneroComponents.Style.fontRegular.name - font.pixelSize: 18 * scaleRatio + opacity: item.placeholderOpacity + color: item.placeholderColor + font.family: item.placeholderFontFamily + font.bold: item.placeholderFontBold + font.pixelSize: item.placeholderFontSize text: "" z: 3 } @@ -123,18 +165,10 @@ ColumnLayout { Rectangle { color: "transparent" border.width: 1 - border.color: { - if(multiLine.error && multiLine.text !== ""){ - return MoneroComponents.Style.inputBorderColorInvalid; - } else if(multiLine.activeFocus){ - return MoneroComponents.Style.inputBorderColorActive; - } else { - return MoneroComponents.Style.inputBorderColorInActive; - } - } + border.color: item.borderColor radius: 4 anchors.fill: parent - visible: lineditmulti.showBorder + visible: !item.borderDisabled } } } diff --git a/components/PasswordDialog.qml b/components/PasswordDialog.qml index 8e75193d..1da76a6d 100644 --- a/components/PasswordDialog.qml +++ b/components/PasswordDialog.qml @@ -108,6 +108,8 @@ Item { leftPadding: 10 topPadding: 10 color: MoneroComponents.Style.defaultFontColor + selectionColor: MoneroComponents.Style.dimmedFontColor + selectedTextColor: MoneroComponents.Style.defaultFontColor background: Rectangle { radius: 2 diff --git a/components/RemoteNodeEdit.qml b/components/RemoteNodeEdit.qml index ecc8b496..36d2ee94 100644 --- a/components/RemoteNodeEdit.qml +++ b/components/RemoteNodeEdit.qml @@ -53,6 +53,8 @@ GridLayout { property string lineEditBorderColor: Qt.rgba(0, 0, 0, 0.15) property string lineEditBackgroundColor: "white" property string lineEditFontColor: "black" + property int lineEditFontSize: 18 * scaleRatio + property int labelFontSize: 16 * scaleRatio property bool lineEditFontBold: true signal editingFinished() @@ -61,7 +63,7 @@ GridLayout { return daemonAddr.text.trim() + ":" + daemonPort.text.trim() } - LineEdit { + LineEditMulti { id: daemonAddr Layout.fillWidth: true placeholderText: qsTr("Remote Node Hostname / IP") + translationManager.emptyString @@ -70,14 +72,16 @@ GridLayout { placeholderFontSize: root.placeholderFontSize placeholderColor: root.placeholderColor placeholderOpacity: root.placeholderOpacity - onEditingFinished: root.editingFinished() + labelFontSize: root.labelFontSize borderColor: lineEditBorderColor backgroundColor: lineEditBackgroundColor fontColor: lineEditFontColor fontBold: lineEditFontBold + fontSize: lineEditFontSize + onEditingFinished: root.editingFinished() } - LineEdit { + LineEditMulti { id: daemonPort Layout.fillWidth: true placeholderText: qsTr("Port") + translationManager.emptyString @@ -86,10 +90,13 @@ GridLayout { placeholderFontSize: root.placeholderFontSize placeholderColor: root.placeholderColor placeholderOpacity: root.placeholderOpacity - onEditingFinished: root.editingFinished() + labelFontSize: root.labelFontSize borderColor: lineEditBorderColor backgroundColor: lineEditBackgroundColor fontColor: lineEditFontColor fontBold: lineEditFontBold + fontSize: lineEditFontSize + + onEditingFinished: root.editingFinished() } } diff --git a/components/StandardDropdown.qml b/components/StandardDropdown.qml index 7ff021ed..82f9406a 100644 --- a/components/StandardDropdown.qml +++ b/components/StandardDropdown.qml @@ -32,6 +32,7 @@ import "../components" as MoneroComponents Item { id: dropdown + property int itemTopMargin: 0 property alias dataModel: repeater.model property string shadowPressedColor property string shadowReleasedColor @@ -76,6 +77,7 @@ Item { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top + anchors.topMargin: parent.itemTopMargin height: dropdown.dropdownHeight Rectangle { diff --git a/components/Style.qml b/components/Style.qml index 21a27f7c..bf0a5084 100644 --- a/components/Style.qml +++ b/components/Style.qml @@ -11,7 +11,6 @@ QtObject { property string grey: "#404040" property string defaultFontColor: "white" - property string greyFontColor: "#808080" property string dimmedFontColor: "#BBBBBB" property string inputBoxBackground: "black" property string inputBoxBackgroundError: "#FFDDDD" diff --git a/components/WarningBox.qml b/components/WarningBox.qml new file mode 100644 index 00000000..9ea868eb --- /dev/null +++ b/components/WarningBox.qml @@ -0,0 +1,63 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 + +import "." as MoneroComponents + +Rectangle { + id: root + property alias text: content.text + property int fontSize: 15 * scaleRatio + + Layout.fillWidth: true + Layout.preferredHeight: warningLayout.height + + color: "#09FFFFFF" + radius: 4 + border.color: MoneroComponents.Style.inputBorderColorInActive + border.width: 1 + + signal linkActivated; + + RowLayout { + id: warningLayout + spacing: 0 + anchors.left: parent.left + anchors.right: parent.right + + Image { + Layout.alignment: Qt.AlignVCenter + Layout.preferredHeight: 33 + Layout.preferredWidth: 33 + Layout.rightMargin: 14 + Layout.leftMargin: 14 + Layout.topMargin: 12 + Layout.bottomMargin: 12 + source: "../images/warning.png" + } + + TextArea { + id: content + Layout.fillWidth: true + color: MoneroComponents.Style.defaultFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: root.fontSize + horizontalAlignment: TextInput.AlignLeft + selectByMouse: false + textFormat: Text.RichText + wrapMode: Text.WordWrap + textMargin: 0 + leftPadding: 0 + topPadding: 6 + readOnly: true + onLinkActivated: root.linkActivated(); + + // @TODO: Legacy. Remove after Qt 5.8. + // https://stackoverflow.com/questions/41990013 + MouseArea { + anchors.fill: parent + enabled: false + } + } + } +} diff --git a/images/settings_local.png b/images/settings_local.png new file mode 100644 index 00000000..6d25a9e3 Binary files /dev/null and b/images/settings_local.png differ diff --git a/images/settings_navbar_side.png b/images/settings_navbar_side.png new file mode 100755 index 00000000..b57695fb Binary files /dev/null and b/images/settings_navbar_side.png differ diff --git a/images/settings_navbar_side_active.png b/images/settings_navbar_side_active.png new file mode 100755 index 00000000..ec711852 Binary files /dev/null and b/images/settings_navbar_side_active.png differ diff --git a/images/settings_remote.png b/images/settings_remote.png new file mode 100644 index 00000000..ed7087de Binary files /dev/null and b/images/settings_remote.png differ diff --git a/js/Utils.js b/js/Utils.js index fdf24c6b..56953f50 100644 --- a/js/Utils.js +++ b/js/Utils.js @@ -26,4 +26,31 @@ function formatDate( date, params ) { function isNumeric(n) { return !isNaN(parseFloat(n)) && isFinite(n); +} + +function showSeedPage() { + // Shows `Settings->Seed & keys`. Prompts a password dialog. + passwordDialog.onAcceptedCallback = function() { + if(walletPassword === passwordDialog.password){ + if(currentWallet.seedLanguage == "") { + console.log("No seed language set. Using English as default"); + currentWallet.setSeedLanguage("English"); + } + // Load keys page + middlePanel.state = "Keys" + } else { + informationPopup.title = qsTr("Error") + translationManager.emptyString; + informationPopup.text = qsTr("Wrong password"); + informationPopup.open() + informationPopup.onCloseCallback = function() { + passwordDialog.open() + } + } + } + passwordDialog.onRejectedCallback = function() { + appWindow.showPageRequest("Settings"); + } + passwordDialog.open(); + if(isMobile) hideMenu(); + updateBalance(); } \ No newline at end of file diff --git a/js/Windows.js b/js/Windows.js index be2f503e..435a91de 100644 --- a/js/Windows.js +++ b/js/Windows.js @@ -11,12 +11,11 @@ function setCustomWindowDecorations(custom) { var y = appWindow.y if (x < 0) x = 0 if (y < 0) y = 0 - + // Update persistentSettings persistentSettings.customDecorations = custom; titleBar.visible = custom; - daemonConsolePopup.titleBar.visible = custom; if (custom) { appWindow.flags = flagsCustomDecorations; @@ -25,7 +24,7 @@ function setCustomWindowDecorations(custom) { appWindow.flags = flags; daemonConsolePopup.flags = flags; } - + // Reset window appWindow.hide() appWindow.x = x diff --git a/main.qml b/main.qml index c41616df..07035d53 100644 --- a/main.qml +++ b/main.qml @@ -40,6 +40,7 @@ import moneroComponents.NetworkType 1.0 import "components" import "wizard" +import "../js/Utils.js" as Utils import "js/Windows.js" as Windows ApplicationWindow { @@ -1414,31 +1415,7 @@ ApplicationWindow { updateBalance(); } - onKeysClicked: { - passwordDialog.onAcceptedCallback = function() { - if(walletPassword === passwordDialog.password){ - if(currentWallet.seedLanguage == "") { - console.log("No seed language set. Using English as default"); - currentWallet.setSeedLanguage("English"); - } - // Load keys page - middlePanel.state = "Keys" - } else { - informationPopup.title = qsTr("Error") + translationManager.emptyString; - informationPopup.text = qsTr("Wrong password"); - informationPopup.open() - informationPopup.onCloseCallback = function() { - passwordDialog.open() - } - } - } - passwordDialog.onRejectedCallback = function() { - appWindow.showPageRequest("Settings"); - } - passwordDialog.open(); - if(isMobile) hideMenu(); - updateBalance(); - } + onKeysClicked: Utils.showSeedPage(); } RightPanel { diff --git a/pages/Keys.qml b/pages/Keys.qml index 8bba91f9..b792a8e2 100644 --- a/pages/Keys.qml +++ b/pages/Keys.qml @@ -128,7 +128,8 @@ Rectangle { copyButton: true addressValidation: false readOnly: true - wrapAnywhere: false + wrapMode: Text.WordWrap + fontColor: "white" } } diff --git a/pages/Settings.qml b/pages/Settings.qml deleted file mode 100644 index 4abd62f7..00000000 --- a/pages/Settings.qml +++ /dev/null @@ -1,818 +0,0 @@ -// Copyright (c) 2014-2018, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import QtQuick 2.0 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 -import "../version.js" as Version -import "../js/Windows.js" as Windows -import "../js/Utils.js" as Utils - - -import "../components" -import moneroComponents.Clipboard 1.0 - -Rectangle { - property bool viewOnly: false - property alias settingsHeight: mainLayout.height - id: page - - color: "transparent" - - // fires on every page load - function onPageCompleted() { - console.log("Settings page loaded"); - - if(typeof daemonManager != "undefined"){ - daemonRunning = persistentSettings.useRemoteNode ? false : appWindow.daemonRunning; - } - - logLevelDropdown.update() - } - - Clipboard { id: clipboard } - - ColumnLayout { - id: mainLayout - anchors.margins: (isMobile)? 17 : 40 - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - spacing: 26 * scaleRatio - - //! Manage wallet - RowLayout { - Layout.fillWidth: true - Label { - id: manageWalletLabel - fontSize: 22 * scaleRatio - Layout.fillWidth: true - text: qsTr("Manage wallet") + translationManager.emptyString - Layout.topMargin: 10 * scaleRatio - } - - Rectangle { - anchors.top: manageWalletLabel.bottom - anchors.topMargin: 4 - anchors.left: parent.left - anchors.right: parent.right - Layout.fillWidth: true - height: 2 - color: Style.dividerColor - opacity: Style.dividerOpacity - } - } - - GridLayout { - columns: (isMobile)? 1 : 4 - StandardButton { - id: closeWalletButton - small: true - text: qsTr("Close wallet") + translationManager.emptyString - visible: true - onClicked: { - console.log("closing wallet button clicked") - appWindow.showWizard(); - } - } - - StandardButton { - id: createViewOnlyWalletButton - enabled: !viewOnly - small: true - text: qsTr("Create view only wallet") + translationManager.emptyString - visible: true - onClicked: { - wizard.openCreateViewOnlyWalletPage(); - } - } - -/* Rescan cache - Disabled until we know it's needed - - StandardButton { - id: rescanWalletbutton - shadowReleasedColor: "#FF4304" - shadowPressedColor: "#B32D00" - releasedColor: "#FF6C3C" - pressedColor: "#FF4304" - text: qsTr("Rescan wallet cache") + translationManager.emptyString - onClicked: { - // Show confirmation dialog - confirmationDialog.title = qsTr("Rescan wallet cache") + translationManager.emptyString; - confirmationDialog.text = qsTr("Are you sure you want to rebuild the wallet cache?\n" - + "The following information will be deleted\n" - + "- Recipient addresses\n" - + "- Tx keys\n" - + "- Tx descriptions\n\n" - + "The old wallet cache file will be renamed and can be restored later.\n" - ); - confirmationDialog.icon = StandardIcon.Question - confirmationDialog.cancelText = qsTr("Cancel") - confirmationDialog.onAcceptedCallback = function() { - walletManager.closeWallet(); - walletManager.clearWalletCache(persistentSettings.wallet_path); - walletManager.openWalletAsync(persistentSettings.wallet_path, appWindow.walletPassword, - persistentSettings.nettype); - } - - confirmationDialog.onRejectedCallback = null; - - confirmationDialog.open() - - } - } -*/ - StandardButton { - id: rescanSpentButton - small: true - enabled: !persistentSettings.useRemoteNode - text: qsTr("Rescan wallet balance") + translationManager.emptyString - onClicked: { - if (!currentWallet.rescanSpent()) { - console.error("Error: ", currentWallet.errorString); - informationPopup.title = qsTr("Error") + translationManager.emptyString; - informationPopup.text = qsTr("Error: ") + currentWallet.errorString - informationPopup.icon = StandardIcon.Critical - informationPopup.onCloseCallback = null - informationPopup.open(); - } else { - informationPopup.title = qsTr("Information") + translationManager.emptyString - informationPopup.text = qsTr("Successfully rescanned spent outputs.") + translationManager.emptyString - informationPopup.icon = StandardIcon.Information - informationPopup.onCloseCallback = null - informationPopup.open(); - } - } - } - } - - RowLayout{ - Layout.fillWidth: true - - StandardButton { - id: changePasswordButton - small: true - text: qsTr("Change password") + translationManager.emptyString - onClicked: { - passwordDialog.onAcceptedCallback = function() { - if(appWindow.walletPassword === passwordDialog.password){ - newPasswordDialog.open() - } else { - informationPopup.title = qsTr("Error") + translationManager.emptyString; - informationPopup.text = qsTr("Wrong password"); - informationPopup.open() - informationPopup.onCloseCallback = function() { - changePasswordDialog.open() - } - passwordDialog.open() - } - } - passwordDialog.onRejectedCallback = null; - passwordDialog.open() - } - } - } - - RowLayout { - Layout.fillWidth: true - - LabelSubheader { - text: qsTr("Daemon mode") + translationManager.emptyString - } - } - - ColumnLayout { - RadioButton { - id: remoteDisconnect - checked: !persistentSettings.useRemoteNode - text: qsTr("Local Node") + translationManager.emptyString - onClicked: { - persistentSettings.useRemoteNode = false; - remoteConnect.checked = false; - appWindow.disconnectRemoteNode(); - } - } - - RadioButton { - id: remoteConnect - checked: persistentSettings.useRemoteNode - text: qsTr("Remote Node") + translationManager.emptyString - onClicked: { - persistentSettings.useRemoteNode = true; - remoteDisconnect.checked = false; - appWindow.connectRemoteNode(); - } - } - } - - RowLayout { - visible: !isMobile && !persistentSettings.useRemoteNode - Layout.fillWidth: true - - LabelSubheader { - text: qsTr("Bootstrap node") + translationManager.emptyString - } - } - - RowLayout { - visible: !isMobile && !persistentSettings.useRemoteNode - - ColumnLayout { - Layout.fillWidth: true - - RemoteNodeEdit { - id: bootstrapNodeEdit - Layout.minimumWidth: 100 * scaleRatio - Layout.bottomMargin: 20 * scaleRatio - - lineEditBackgroundColor: "transparent" - lineEditFontColor: "white" - lineEditBorderColor: Style.inputBorderColorActive - - daemonAddrLabelText: qsTr("Address") + translationManager.emptyString - daemonPortLabelText: qsTr("Port") + translationManager.emptyString - daemonAddrText: persistentSettings.bootstrapNodeAddress.split(":")[0].trim() - daemonPortText: { - var node_split = persistentSettings.bootstrapNodeAddress.split(":"); - if(node_split.length == 2){ - (node_split[1].trim() == "") ? "18081" : node_split[1]; - } else { - return "" - } - } - onEditingFinished: { - persistentSettings.bootstrapNodeAddress = daemonAddrText ? bootstrapNodeEdit.getAddress() : ""; - console.log("setting bootstrap node to " + persistentSettings.bootstrapNodeAddress) - } - } - } - } - - RowLayout { - visible: persistentSettings.useRemoteNode - ColumnLayout { - Layout.fillWidth: true - - RemoteNodeEdit { - id: remoteNodeEdit - Layout.minimumWidth: 100 * scaleRatio - - lineEditBackgroundColor: "transparent" - lineEditFontColor: "white" - lineEditBorderColor: Qt.rgba(255, 255, 255, 0.35) - - daemonAddrLabelText: qsTr("Address") - daemonPortLabelText: qsTr("Port") - - property var rna: persistentSettings.remoteNodeAddress - daemonAddrText: rna.search(":") != -1 ? rna.split(":")[0].trim() : "" - daemonPortText: rna.search(":") != -1 ? (rna.split(":")[1].trim() == "") ? "18081" : rna.split(":")[1] : "" - onEditingFinished: { - persistentSettings.remoteNodeAddress = remoteNodeEdit.getAddress(); - console.log("setting remote node to " + persistentSettings.remoteNodeAddress) - } - } - } - } - - RowLayout{ - visible: persistentSettings.useRemoteNode - Layout.fillWidth: true - - StandardButton { - id: remoteNodeSave - small: true - text: qsTr("Connect") + translationManager.emptyString - onClicked: { - // Update daemon login - persistentSettings.remoteNodeAddress = remoteNodeEdit.getAddress(); - persistentSettings.daemonUsername = daemonUsername.text; - persistentSettings.daemonPassword = daemonPassword.text; - persistentSettings.useRemoteNode = true - - currentWallet.setDaemonLogin(persistentSettings.daemonUsername, persistentSettings.daemonPassword); - - appWindow.connectRemoteNode() - } - } - } - - //! Manage daemon - RowLayout { - visible: !isMobile - - Label { - id: manageDaemonLabel - fontSize: 22 * scaleRatio - text: qsTr("Manage Daemon") + translationManager.emptyString - } - - Rectangle { - anchors.top: manageDaemonLabel.bottom - anchors.topMargin: 4 - anchors.left: parent.left - anchors.right: parent.right - Layout.fillWidth: true - height: 2 - color: Style.dividerColor - opacity: Style.dividerOpacity - } - } - - GridLayout { - visible: !isMobile && !persistentSettings.useRemoteNode - id: daemonStatusRow - columns: (isMobile) ? 2 : 4 - StandardButton { - id: startDaemonButton - small: true - visible: !daemonRunning - text: qsTr("Start Local Node") + translationManager.emptyString - onClicked: { - // Update bootstrap daemon address - persistentSettings.bootstrapNodeAddress = bootstrapNodeEdit.daemonAddrText ? bootstrapNodeEdit.getAddress() : ""; - - // Set current daemon address to local - appWindow.currentDaemonAddress = appWindow.localDaemonAddress; - appWindow.startDaemon(daemonFlags.text); - } - } - - StandardButton { - id: stopDaemonButton - small: true - visible: daemonRunning - text: qsTr("Stop Local Node") + translationManager.emptyString - onClicked: { - appWindow.stopDaemon() - } - } - - StandardButton { - id: daemonStatusButton - small: true - visible: true - text: qsTr("Show status") + translationManager.emptyString - onClicked: { - daemonManager.sendCommand("status",currentWallet.nettype); - daemonConsolePopup.open(); - } - } - } - - ColumnLayout { - id: blockchainFolderRow - visible: !isMobile && !persistentSettings.useRemoteNode - - RowLayout { - Layout.fillWidth: true - Layout.bottomMargin: 14 * scaleRatio - - LabelSubheader { - text: qsTr("Blockchain location") + translationManager.emptyString - } - } - - RowLayout { - visible: persistentSettings.blockchainDataDir.length > 0 - - LineEdit { - id: blockchainFolder - Layout.preferredWidth: 200 - - Layout.fillWidth: true - text: persistentSettings.blockchainDataDir; - placeholderText: qsTr("(optional)") + translationManager.emptyString - } - } - - RowLayout { - Layout.fillWidth: true - Layout.topMargin: 8 - StandardButton { - id: blockchainFolderButton - small: true - visible: true - text: qsTr("Change location") + translationManager.emptyString - onClicked: { - //mouse.accepted = false - if(persistentSettings.blockchainDataDir != "") - blockchainFileDialog.folder = "file://" + persistentSettings.blockchainDataDir - blockchainFileDialog.open() - blockchainFolder.focus = true - } - } - } - } - - RowLayout{ - CheckBox { - id: daemonAdvanced - text: qsTr("Show advanced") + translationManager.emptyString - } - } - - RowLayout { - visible: daemonAdvanced.checked && !isMobile && !persistentSettings.useRemoteNode - id: daemonFlagsRow - - LineEdit { - id: daemonFlags - Layout.preferredWidth: 200 - Layout.fillWidth: true - labelText: qsTr("Local daemon startup flags") + translationManager.emptyString - text: appWindow.persistentSettings.daemonFlags; - placeholderText: qsTr("(optional)") + translationManager.emptyString - } - } - - ColumnLayout { - visible: (daemonAdvanced.checked || isMobile) && persistentSettings.useRemoteNode - GridLayout { - columns: (isMobile) ? 1 : 2 - columnSpacing: 32 - - LineEdit { - id: daemonUsername - Layout.fillWidth: true - labelText: "Daemon username" - text: persistentSettings.daemonUsername - placeholderText: qsTr("Username") + translationManager.emptyString - } - - LineEdit { - id: daemonPassword - Layout.fillWidth: true - labelText: "Daemon password" - text: persistentSettings.daemonPassword - placeholderText: qsTr("Password") + translationManager.emptyString - echoMode: TextInput.Password - } - } - } - - RowLayout { - visible: !isMobile - Label { - id: layoutSettingsLabel - fontSize: 22 * scaleRatio - text: qsTr("Layout settings") + translationManager.emptyString - } - - Rectangle { - anchors.top: layoutSettingsLabel.bottom - anchors.topMargin: 4 - anchors.left: parent.left - anchors.right: parent.right - Layout.fillWidth: true - height: 2 - color: Style.dividerColor - opacity: Style.dividerOpacity - } - } - - RowLayout { - CheckBox { - visible: !isMobile - id: customDecorationsCheckBox - checked: persistentSettings.customDecorations - onClicked: Windows.setCustomWindowDecorations(checked) - text: qsTr("Custom decorations") + translationManager.emptyString - } - } - - // Log level - - RowLayout { - Label { - id: logLevelLabel - fontSize: 22 * scaleRatio - text: qsTr("Log level") + translationManager.emptyString - } - - Rectangle { - anchors.top: logLevelLabel.bottom - anchors.topMargin: 4 - anchors.left: parent.left - anchors.right: parent.right - Layout.fillWidth: true - height: 2 - color: Style.dividerColor - opacity: Style.dividerOpacity - } - } - - GridLayout { - columns: (isMobile)? 1 : 3 - Layout.fillWidth: true - columnSpacing: 32 - - ColumnLayout { - spacing: 0 - Layout.fillWidth: true - - ListModel { - id: logLevel - ListElement { name: "none"; column1: "0"; } - ListElement { column1: "1"; } - ListElement { column1: "2"; } - ListElement { column1: "3"; } - ListElement { column1: "4"; } - ListElement { column1: "custom"; } - } - - StandardDropdown { - id: logLevelDropdown - dataModel: logLevel - currentIndex: appWindow.persistentSettings.logLevel; - onChanged: { - if (currentIndex == 5) { - console.log("log categories changed: ", logCategories.text); - walletManager.setLogCategories(logCategories.text); - } - else { - console.log("log level changed: ",currentIndex); - walletManager.setLogLevel(currentIndex); - } - appWindow.persistentSettings.logLevel = currentIndex; - } - Layout.fillWidth: true - shadowReleasedColor: "#FF4304" - shadowPressedColor: "#B32D00" - releasedColor: "#363636" - pressedColor: "#202020" - } - // Make sure dropdown is on top - } - - ColumnLayout { - Layout.fillWidth: true - } - - ColumnLayout { - Layout.fillWidth: true - } - - z: parent.z + 1 - } - - ColumnLayout { - LineEdit { - id: logCategories - Layout.fillWidth: true - text: appWindow.persistentSettings.logCategories - labelText: "Log Categories" - placeholderText: "(e.g. *:WARNING,net.p2p:DEBUG)" - enabled: logLevelDropdown.currentIndex === 5 - onEditingFinished: { - if(enabled) { - console.log("log categories changed: ", text); - walletManager.setLogCategories(text); - appWindow.persistentSettings.logCategories = text; - } - } - } - } - - // Version - RowLayout { - Label { - id: debugLabel - text: qsTr("Debug info") + translationManager.emptyString - fontSize: 22 - anchors.topMargin: 30 * scaleRatio - Layout.topMargin: 30 * scaleRatio - } - - Rectangle { - anchors.top: debugLabel.bottom - anchors.topMargin: 4 - anchors.left: parent.left - anchors.right: parent.right - Layout.fillWidth: true - height: 2 - color: Style.dividerColor - opacity: Style.dividerOpacity - } - } - - GridLayout { - id: grid - columns: 2 - columnSpacing: 20 * scaleRatio - - TextBlock { - font.pixelSize: 14 - text: qsTr("GUI version: ") + translationManager.emptyString - } - - TextBlock { - font.pixelSize: 14 - font.bold: true - text: Version.GUI_VERSION + " (Qt " + qtRuntimeVersion + ")" + translationManager.emptyString - } - - TextBlock { - id: guiMoneroVersion - font.pixelSize: 14 - text: qsTr("Embedded Monero version: ") + translationManager.emptyString - } - - TextBlock { - font.pixelSize: 14 - font.bold: true - text: Version.GUI_MONERO_VERSION + translationManager.emptyString - } - - TextBlock { - Layout.fillWidth: true - font.pixelSize: 14 - text: qsTr("Wallet name: ") + translationManager.emptyString - } - - TextBlock { - Layout.fillWidth: true - font.pixelSize: 14 - font.bold: true - text: walletName + translationManager.emptyString - } - - TextBlock { - id: restoreHeight - font.pixelSize: 14 - textFormat: Text.RichText - text: (typeof currentWallet == "undefined") ? "" : qsTr("Wallet creation height: ") + translationManager.emptyString - } - - TextBlock { - id: restoreHeightText - textFormat: Text.RichText - font.pixelSize: 14 - font.bold: true - property var style: "" - text: (currentWallet ? currentWallet.walletCreationHeight : "") + style + qsTr(" (Click to change)") + translationManager.emptyString - onLinkActivated: { - inputDialog.labelText = qsTr("Set a new restore height:") + translationManager.emptyString; - inputDialog.inputText = currentWallet ? currentWallet.walletCreationHeight : "0"; - inputDialog.onAcceptedCallback = function() { - var _restoreHeight = inputDialog.inputText; - if(Utils.isNumeric(_restoreHeight)){ - _restoreHeight = parseInt(_restoreHeight); - if(_restoreHeight >= 0) { - currentWallet.walletCreationHeight = _restoreHeight - // Restore height is saved in .keys file. Set password to trigger rewrite. - currentWallet.setPassword(appWindow.walletPassword) - - // Show confirmation dialog - confirmationDialog.title = qsTr("Rescan wallet cache") + translationManager.emptyString; - confirmationDialog.text = qsTr("Are you sure you want to rebuild the wallet cache?\n" - + "The following information will be deleted\n" - + "- Recipient addresses\n" - + "- Tx keys\n" - + "- Tx descriptions\n\n" - + "The old wallet cache file will be renamed and can be restored later.\n" - ); - confirmationDialog.icon = StandardIcon.Question - confirmationDialog.cancelText = qsTr("Cancel") - confirmationDialog.onAcceptedCallback = function() { - walletManager.closeWallet(); - walletManager.clearWalletCache(persistentSettings.wallet_path); - walletManager.openWalletAsync(persistentSettings.wallet_path, appWindow.walletPassword, - persistentSettings.nettype); - } - - confirmationDialog.onRejectedCallback = null; - confirmationDialog.open() - return; - } - } - - appWindow.showStatusMessage(qsTr("Invalid restore height specified. Must be a number."),3); - } - inputDialog.onRejectedCallback = null; - inputDialog.open() - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.NoButton - cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor - } - } - - TextBlock { - Layout.fillWidth: true - font.pixelSize: 14 - text: qsTr("Wallet log path: ") + translationManager.emptyString - } - - TextBlock { - Layout.fillWidth: true - font.pixelSize: 14 - text: walletLogPath - } - } - } - - // Choose blockchain folder - FileDialog { - id: blockchainFileDialog - title: qsTr("Please choose a folder") + translationManager.emptyString; - selectFolder: true - folder: "file://" + persistentSettings.blockchainDataDir - - onAccepted: { - var dataDir = walletManager.urlToLocalPath(blockchainFileDialog.fileUrl); - console.log(dataDir); - var validator = daemonManager.validateDataDir(dataDir); - if(!validator.valid) { - - confirmationDialog.title = qsTr("Warning") + translationManager.emptyString; - confirmationDialog.text = ""; - if(validator.readOnly) { - confirmationDialog.text += qsTr("Error: Filesystem is read only") + "\n\n" - } - - if(validator.storageAvailable < estimatedBlockchainSize) { - confirmationDialog.text += qsTr("Warning: There's only %1 GB available on the device. Blockchain requires ~%2 GB of data.").arg(validator.storageAvailable).arg(estimatedBlockchainSize) + "\n\n" - } else { - confirmationDialog.text += qsTr("Note: There's %1 GB available on the device. Blockchain requires ~%2 GB of data.").arg(validator.storageAvailable).arg(estimatedBlockchainSize) + "\n\n" - } - - if(!validator.lmdbExists) { - confirmationDialog.text += qsTr("Note: lmdb folder not found. A new folder will be created.") + "\n\n" - } - - confirmationDialog.icon = StandardIcon.Question - confirmationDialog.cancelText = qsTr("Cancel") - - // Continue - confirmationDialog.onAcceptedCallback = function() { - persistentSettings.blockchainDataDir = dataDir - } - - // Cancel - confirmationDialog.onRejectedCallback = function() { - }; - - confirmationDialog.open() - } else { - persistentSettings.blockchainDataDir = dataDir - } - - delete validator; - - - } - onRejected: { - console.log("data dir selection canceled") - } - - } - - // fires only once - Component.onCompleted: { - if(typeof daemonManager != "undefined") - daemonManager.daemonConsoleUpdated.connect(onDaemonConsoleUpdated) - } - - function onDaemonConsoleUpdated(message){ - // Update daemon console - daemonConsolePopup.textArea.logMessage(message) - } - - - - -} - - - - diff --git a/pages/Transfer.qml b/pages/Transfer.qml index fe3c6c21..c398eaf8 100644 --- a/pages/Transfer.qml +++ b/pages/Transfer.qml @@ -33,6 +33,7 @@ import moneroComponents.Clipboard 1.0 import moneroComponents.PendingTransaction 1.0 import moneroComponents.Wallet 1.0 import "../components" +import "../components" as MoneroComponents import "." 1.0 @@ -43,6 +44,7 @@ Rectangle { signal sweepUnmixableClicked() color: "transparent" + property string warningContent: "" property string startLinkText: qsTr(" (Start daemon)") + translationManager.emptyString property bool showAdvanced: false @@ -122,47 +124,13 @@ Rectangle { spacing: 30 * scaleRatio - RowLayout{ - visible: warningText.text !== "" + RowLayout { + visible: root.warningContent !== "" - Rectangle { - id: statusRect - Layout.preferredHeight: warningText.height + 40 - Layout.fillWidth: true - - radius: 2 - border.color: Style.inputBorderColorInActive - border.width: 1 - color: "transparent" - - GridLayout{ - Layout.fillWidth: true - Layout.preferredHeight: warningText.height + 40 - - Image { - Layout.alignment: Qt.AlignVCenter - Layout.preferredHeight: 33 - Layout.preferredWidth: 33 - Layout.leftMargin: 10 - Layout.topMargin: 10 - source: "../images/warning.png" - } - - Text { - id: warningText - Layout.topMargin: 12 * scaleRatio - Layout.preferredWidth: statusRect.width - 80 - Layout.leftMargin: 6 - text: qsTr("This page lets you sign/verify a message (or file contents) with your address.") + translationManager.emptyString - wrapMode: Text.Wrap - font.family: Style.fontRegular.name - font.pixelSize: 14 * scaleRatio - color: Style.defaultFontColor - textFormat: Text.RichText - onLinkActivated: { - appWindow.startDaemon(appWindow.persistentSettings.daemonFlags); - } - } + MoneroComponents.WarningBox { + text: warningContent + onLinkActivated: { + appWindow.startDaemon(appWindow.persistentSettings.daemonFlags); } } } @@ -255,6 +223,8 @@ Rectangle { + translationManager.emptyString labelButtonText: qsTr("Resolve") + translationManager.emptyString placeholderText: "4.. / 8.." + wrapMode: Text.WrapAnywhere + addressValidation: true onInputLabelLinkActivated: { appWindow.showPageRequest("AddressBook") } } @@ -319,17 +289,19 @@ Rectangle { RowLayout { // payment id input - LineEdit { + LineEditMulti { id: paymentIdLine fontBold: true labelText: qsTr("Payment ID ( Optional )") + translationManager.emptyString placeholderText: qsTr("16 or 64 hexadecimal characters") + translationManager.emptyString Layout.fillWidth: true + wrapMode: Text.WrapAnywhere + addressValidation: false } } RowLayout { - LineEdit { + LineEditMulti { id: descriptionLine labelText: qsTr("Description ( Optional )") + translationManager.emptyString placeholderText: qsTr("Saved to local wallet history") + translationManager.emptyString @@ -352,7 +324,7 @@ Rectangle { } // There is no warning box displayed - if(warningText.text !== ''){ + if(root.warningContent !== ''){ return false; } @@ -707,7 +679,7 @@ Rectangle { function updateStatus() { pageRoot.enabled = true; if(typeof currentWallet === "undefined") { - warningText.text = qsTr("Wallet is not connected to daemon.") + root.startLinkText + root.warningContent = qsTr("Wallet is not connected to daemon.") + root.startLinkText return; } @@ -719,20 +691,20 @@ Rectangle { switch (currentWallet.connected()) { case Wallet.ConnectionStatus_Disconnected: - warningText.text = qsTr("Wallet is not connected to daemon.") + root.startLinkText + root.warningContent = qsTr("Wallet is not connected to daemon.") + root.startLinkText break case Wallet.ConnectionStatus_WrongVersion: - warningText.text = qsTr("Connected daemon is not compatible with GUI. \n" + + root.warningContent = qsTr("Connected daemon is not compatible with GUI. \n" + "Please upgrade or connect to another daemon") break default: if(!appWindow.daemonSynced){ - warningText.text = qsTr("Waiting on daemon synchronization to finish") + root.warningContent = qsTr("Waiting on daemon synchronization to finish") } else { // everything OK, enable transfer page // Light wallet is always ready pageRoot.enabled = true; - warningText.text = ""; + root.warningContent = ""; } } } diff --git a/pages/settings/Navbar.qml b/pages/settings/Navbar.qml new file mode 100644 index 00000000..4c1c5d2a --- /dev/null +++ b/pages/settings/Navbar.qml @@ -0,0 +1,346 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick 2.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.2 +import "../../js/Windows.js" as Windows +import "../../js/Utils.js" as Utils +import "../../components" as MoneroComponents +import "../../pages" +import "." +import moneroComponents.Clipboard 1.0 + +Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 96 + color: "transparent" + + ColumnLayout { + spacing: 0 + Layout.preferredHeight: 32 + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + GridLayout { + id: grid + anchors.horizontalCenter: parent.horizontalCenter + columnSpacing: 0 + property string fontColor: "white" + property int fontSize: 13 * scaleRatio + property bool fontBold: true + property var fontFamily: MoneroComponents.Style.fontRegular.name + property string borderColor: "#808080" + property int textMargin: { + // left-right margins for a given cell + if(isMobile){ + return 10; + } else if(appWindow.width < 890){ + return 32; + } else { + return 64; + } + } + Image { + Layout.preferredWidth: 2 + Layout.preferredHeight: 32 + source: { + if(settingsStateView.state === "Wallet"){ + return "../../images/settings_navbar_side_active.png" + } else { + return "../../images/settings_navbar_side.png" + } + } + } + ColumnLayout { + // WALLET + id: navWallet + Layout.preferredWidth: navWalletText.width + grid.textMargin + Layout.minimumWidth: 72 * scaleRatio + Layout.preferredHeight: 32 + spacing: 0 + + Rectangle { + color: grid.borderColor + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + + Rectangle { + color: settingsStateView.state === "Wallet" ? grid.borderColor : "transparent" + height: 30 * scaleRatio + Layout.fillWidth: true + + Text { + id: navWalletText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.family: grid.fontFamily + font.pixelSize: grid.fontSize + font.bold: grid.fontBold + text: qsTr("Wallet") + translationManager.emptyString + color: grid.fontColor + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onClicked: { settingsStateView.state = "Wallet" } + } + } + + Rectangle { + color: grid.borderColor + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + } + Rectangle{ + Layout.preferredWidth: 1 + Layout.preferredHeight: 32 + color: grid.borderColor + } + ColumnLayout { + // UI + id: navUI + Layout.preferredWidth: navUIText.width + grid.textMargin + Layout.preferredHeight: 32 + Layout.minimumWidth: 72 * scaleRatio + spacing: 0 + + Rectangle { + color: grid.borderColor + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + + Rectangle { + color: settingsStateView.state === "UI" ? grid.borderColor : "transparent" + height: 30 * scaleRatio + Layout.fillWidth: true + + Text { + id: navUIText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.family: grid.fontFamily + font.pixelSize: grid.fontSize + font.bold: grid.fontBold + text: qsTr("Layout") + translationManager.emptyString + color: grid.fontColor + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onClicked: { settingsStateView.state = "UI" } + } + } + + Rectangle { + color: grid.borderColor + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + } + Rectangle{ + Layout.preferredWidth: 1 + Layout.preferredHeight: 32 + color: grid.borderColor + } + ColumnLayout { + // NODE + id: navNode + Layout.preferredWidth: navNodeText.width + grid.textMargin + Layout.preferredHeight: 32 + Layout.minimumWidth: 72 * scaleRatio + spacing: 0 + + Rectangle { + color: grid.borderColor + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + + Rectangle { + color: settingsStateView.state === "Node" ? grid.borderColor : "transparent" + height: 30 * scaleRatio + Layout.fillWidth: true + + Text { + id: navNodeText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.family: grid.fontFamily + font.pixelSize: grid.fontSize + font.bold: grid.fontBold + text: qsTr("Node") + translationManager.emptyString + color: grid.fontColor + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onClicked: { settingsStateView.state = "Node" } + } + } + + Rectangle { + color: grid.borderColor + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + } + Rectangle{ + Layout.preferredWidth: 1 + Layout.preferredHeight: 32 + color: grid.borderColor + } + ColumnLayout { + // LOG + id: navLog + Layout.preferredWidth: navLogText.width + grid.textMargin + Layout.preferredHeight: 32 + Layout.minimumWidth: 72 * scaleRatio + spacing: 0 + + Rectangle { + color: grid.borderColor + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + + Rectangle { + color: settingsStateView.state === "Log" ? grid.borderColor : "transparent" + height: 30 * scaleRatio + Layout.fillWidth: true + + Text { + id: navLogText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.family: grid.fontFamily + font.pixelSize: grid.fontSize + font.bold: grid.fontBold + text: qsTr("Log") + translationManager.emptyString + color: grid.fontColor + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onClicked: { settingsStateView.state = "Log" } + } + } + + Rectangle { + color: grid.borderColor + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + } + Rectangle{ + Layout.preferredWidth: 1 + Layout.preferredHeight: 32 + color: grid.borderColor + } + ColumnLayout { + // INFO + id: navInfo + Layout.preferredWidth: navInfoText.width + grid.textMargin + Layout.preferredHeight: 32 + Layout.minimumWidth: 72 * scaleRatio + spacing: 0 + + Rectangle { + color: grid.borderColor + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + + Rectangle { + color: settingsStateView.state === "Info" ? grid.borderColor : "transparent" + height: 30 * scaleRatio + Layout.fillWidth: true + + Text { + id: navInfoText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.family: grid.fontFamily + font.pixelSize: grid.fontSize + font.bold: grid.fontBold + text: qsTr("Info") + translationManager.emptyString + color: grid.fontColor + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onClicked: { settingsStateView.state = "Info" } + } + } + + Rectangle { + color: grid.borderColor + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + } + Image { + Layout.preferredWidth: 2 + Layout.preferredHeight: 32 + source: { + if(settingsStateView.state === "Info"){ + return "../../images/settings_navbar_side_active.png" + } else { + return "../../images/settings_navbar_side.png" + } + + } + rotation: 180 + } + Rectangle { + color: "transparent" + Layout.fillWidth: true + } + } + } +} diff --git a/pages/settings/Settings.qml b/pages/settings/Settings.qml new file mode 100644 index 00000000..71005168 --- /dev/null +++ b/pages/settings/Settings.qml @@ -0,0 +1,139 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick 2.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.2 +import "../../js/Windows.js" as Windows +import "../../js/Utils.js" as Utils +import "../../components" as MoneroComponents +import "../../pages" +import "." +import moneroComponents.Clipboard 1.0 + +ColumnLayout { + id: settingsPage + Layout.fillWidth: true + Layout.preferredHeight: 900 + spacing: 0 + Clipboard { id: clipboard } + property bool viewOnly: false + property int settingsHeight: 900 + + Navbar{} + + Rectangle{ + id: settingsStateView + property Item currentView + property Item previousView + property SettingsWallet settingsWalletView: SettingsWallet { } + property SettingsLayout settingsLayoutView: SettingsLayout { } + property SettingsNode settingsNodeView: SettingsNode { } + property SettingsLog settingsLogView: SettingsLog { } + property SettingsInfo settingsInfoView: SettingsInfo { } + Layout.fillWidth: true + Layout.preferredHeight: parent.height + color: "transparent" + state: "Wallet" + + onCurrentViewChanged: { + if (previousView) { +// if (typeof previousView.onPageClosed === "function") { +// previousView.onPageClosed(); +// } + } + previousView = currentView + if (currentView) { + stackView.replace(currentView) + // Component.onCompleted is called before wallet is initilized +// if (typeof currentView.onPageCompleted === "function") { +// currentView.onPageCompleted(); +// } + } + } + + states: [ + State { + name: "Wallet" + PropertyChanges { target: settingsStateView; currentView: settingsStateView.settingsWalletView } + }, State { + name: "UI" + PropertyChanges { target: settingsStateView; currentView: settingsStateView.settingsLayoutView } + }, State { + name: "Node" + PropertyChanges { target: settingsStateView; currentView: settingsStateView.settingsNodeView } + }, State { + name: "Log" + PropertyChanges { target: settingsStateView; currentView: settingsStateView.settingsLogView } + }, State { + name: "Info" + PropertyChanges { target: settingsStateView; currentView: settingsStateView.settingsInfoView } + } + ] + + StackView { + id: stackView + initialItem: settingsStateView.settingsWalletView + anchors.fill: parent + clip: false // otherwise animation will affect left panel + + delegate: StackViewDelegate { + pushTransition: StackViewTransition { + PropertyAnimation { + target: enterItem + property: "x" + from: 0 - target.width + to: 0 + duration: 300 + easing.type: Easing.OutCubic + } + PropertyAnimation { + target: exitItem + property: "x" + from: 0 + to: target.width + duration: 300 + easing.type: Easing.OutCubic + } + } + } + } + } + + function onDaemonConsoleUpdated(message){ + // Update daemon console + settingsStateView.settingsLogView.consoleArea.logMessage(message) + } + + // fires on every page load + function onPageCompleted() { + console.log("Settings page loaded"); + } +} diff --git a/pages/settings/SettingsInfo.qml b/pages/settings/SettingsInfo.qml new file mode 100644 index 00000000..bd8847e0 --- /dev/null +++ b/pages/settings/SettingsInfo.qml @@ -0,0 +1,302 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick 2.7 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 +import QtQuick.Dialogs 1.2 + +import "../../js/Utils.js" as Utils +import "../../version.js" as Version +import "../../components" as MoneroComponents + + +Rectangle { + color: "transparent" + height: 1400 + Layout.fillWidth: true + + ColumnLayout { + id: infoLayout + Layout.fillWidth: true + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: (isMobile)? 17 : 20 + anchors.topMargin: 0 + spacing: 30 + + GridLayout { + columns: 2 + columnSpacing: 0 + + MoneroComponents.TextBlock { + font.pixelSize: 14 + text: qsTr("GUI version: ") + translationManager.emptyString + } + + MoneroComponents.TextBlock { + font.pixelSize: 14 + text: Version.GUI_VERSION + " (Qt " + qtRuntimeVersion + ")" + translationManager.emptyString + } + + Rectangle { + height: 1 + Layout.topMargin: 2 * scaleRatio + Layout.bottomMargin: 2 * scaleRatio + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + Rectangle { + height: 1 + Layout.topMargin: 2 * scaleRatio + Layout.bottomMargin: 2 * scaleRatio + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + MoneroComponents.TextBlock { + id: guiMoneroVersion + font.pixelSize: 14 + text: qsTr("Embedded Monero version: ") + translationManager.emptyString + } + + MoneroComponents.TextBlock { + font.pixelSize: 14 + text: Version.GUI_MONERO_VERSION + translationManager.emptyString + } + + Rectangle { + height: 1 + Layout.topMargin: 2 * scaleRatio + Layout.bottomMargin: 2 * scaleRatio + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + Rectangle { + height: 1 + Layout.topMargin: 2 * scaleRatio + Layout.bottomMargin: 2 * scaleRatio + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + MoneroComponents.TextBlock { + Layout.fillWidth: true + font.pixelSize: 14 + text: qsTr("Wallet path: ") + translationManager.emptyString + } + + MoneroComponents.TextBlock { + Layout.fillWidth: true + Layout.maximumWidth: 360 + font.pixelSize: 14 + text: { + var wallet_path = walletPath(); + if(isIOS) + wallet_path = moneroAccountsDir + wallet_path; + return wallet_path; + } + } + + Rectangle { + height: 1 + Layout.topMargin: 2 * scaleRatio + Layout.bottomMargin: 2 * scaleRatio + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + Rectangle { + height: 1 + Layout.topMargin: 2 * scaleRatio + Layout.bottomMargin: 2 * scaleRatio + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + MoneroComponents.TextBlock { + id: restoreHeight + font.pixelSize: 14 + textFormat: Text.RichText + text: (typeof currentWallet == "undefined") ? "" : qsTr("Wallet creation height: ") + translationManager.emptyString + } + + MoneroComponents.TextBlock { + id: restoreHeightText + Layout.fillWidth: true + textFormat: Text.RichText + font.pixelSize: 14 + font.bold: true + property var style: "" + text: (currentWallet ? currentWallet.walletCreationHeight : "") + style + qsTr(" (Click to change)") + translationManager.emptyString + onLinkActivated: { + inputDialog.labelText = qsTr("Set a new restore height:") + translationManager.emptyString; + inputDialog.inputText = currentWallet ? currentWallet.walletCreationHeight : "0"; + inputDialog.onAcceptedCallback = function() { + var _restoreHeight = inputDialog.inputText; + if(Utils.isNumeric(_restoreHeight)){ + _restoreHeight = parseInt(_restoreHeight); + if(_restoreHeight >= 0) { + currentWallet.walletCreationHeight = _restoreHeight + // Restore height is saved in .keys file. Set password to trigger rewrite. + currentWallet.setPassword(appWindow.walletPassword) + + // Show confirmation dialog + confirmationDialog.title = qsTr("Rescan wallet cache") + translationManager.emptyString; + confirmationDialog.text = qsTr("Are you sure you want to rebuild the wallet cache?\n" + + "The following information will be deleted\n" + + "- Recipient addresses\n" + + "- Tx keys\n" + + "- Tx descriptions\n\n" + + "The old wallet cache file will be renamed and can be restored later.\n" + ); + confirmationDialog.icon = StandardIcon.Question + confirmationDialog.cancelText = qsTr("Cancel") + confirmationDialog.onAcceptedCallback = function() { + walletManager.closeWallet(); + walletManager.clearWalletCache(persistentSettings.wallet_path); + walletManager.openWalletAsync(persistentSettings.wallet_path, appWindow.walletPassword, + persistentSettings.nettype); + } + + confirmationDialog.onRejectedCallback = null; + confirmationDialog.open() + return; + } + } + + appWindow.showStatusMessage(qsTr("Invalid restore height specified. Must be a number."),3); + } + inputDialog.onRejectedCallback = null; + inputDialog.open() + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } + } + + Rectangle { + height: 1 + Layout.topMargin: 2 * scaleRatio + Layout.bottomMargin: 2 * scaleRatio + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + Rectangle { + height: 1 + Layout.topMargin: 2 * scaleRatio + Layout.bottomMargin: 2 * scaleRatio + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + MoneroComponents.TextBlock { + Layout.fillWidth: true + font.pixelSize: 14 + text: qsTr("Wallet log path: ") + translationManager.emptyString + } + + MoneroComponents.TextBlock { + Layout.fillWidth: true + font.pixelSize: 14 + text: walletLogPath + } + } + + // Copy info to clipboard + Rectangle { + color: "transparent" + Layout.preferredHeight: 24 * scaleRatio + Layout.fillWidth: true + + Rectangle { + id: rectCopy + color: MoneroComponents.Style.buttonBackgroundColorDisabled + width: btnCopy.width + 40 + height: 24 + radius: 2 + + Text { + id: btnCopy + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: MoneroComponents.Style.defaultFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 * scaleRatio + font.bold: true + text: qsTr("Copy to clipboard") + translationManager.emptyString + } + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: { + var data = ""; + data += "GUI version: " + Version.GUI_VERSION + " (Qt " + qtRuntimeVersion + ")"; + data += "\nEmbedded Monero version: " + Version.GUI_MONERO_VERSION; + data += "\nWallet path: "; + + var wallet_path = walletPath(); + if(isIOS) + wallet_path = moneroAccountsDir + wallet_path; + data += wallet_path; + + data += "\nWallet creation height: "; + if(currentWallet) + data += currentWallet.walletCreationHeight; + + data += "\nWallet log path: " + walletLogPath; + + console.log("Copied to clipboard"); + clipboard.setText(data); + appWindow.showStatusMessage(qsTr("Copied to clipboard"), 3); + } + } + } + } + } + + Component.onCompleted: { + + } +} diff --git a/pages/settings/SettingsLayout.qml b/pages/settings/SettingsLayout.qml new file mode 100644 index 00000000..4bad9433 --- /dev/null +++ b/pages/settings/SettingsLayout.qml @@ -0,0 +1,75 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick 2.7 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 +import QtQuick.Dialogs 1.2 + +import "../../js/Utils.js" as Utils +import "../../js/Windows.js" as Windows +import "../../components" as MoneroComponents + +Rectangle { + color: "transparent" + height: 1400 + Layout.fillWidth: true + + ColumnLayout { + id: settingsUI + property int itemHeight: 60 * scaleRatio + Layout.fillWidth: true + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: (isMobile)? 17 : 20 + anchors.topMargin: 0 + spacing: 0 + + MoneroComponents.CheckBox { + visible: !isMobile + id: customDecorationsCheckBox + checked: persistentSettings.customDecorations + onClicked: Windows.setCustomWindowDecorations(checked) + text: qsTr("Custom decorations") + translationManager.emptyString + } + + MoneroComponents.TextBlock { + visible: isMobile + font.pixelSize: 14 + textFormat: Text.RichText + Layout.fillWidth: true + text: qsTr("No Layout options exist yet in mobile mode.") + translationManager.emptyString; + } + } + + Component.onCompleted: { + console.log('SettingsLayout loaded'); + } +} + diff --git a/pages/settings/SettingsLog.qml b/pages/settings/SettingsLog.qml new file mode 100644 index 00000000..d43c1dd1 --- /dev/null +++ b/pages/settings/SettingsLog.qml @@ -0,0 +1,245 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick 2.7 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 + +import "../../js/Utils.js" as Utils +import "../../components" as MoneroComponents + + +Rectangle { + property alias consoleArea: consoleArea + color: "transparent" + height: 1400 + Layout.fillWidth: true + + ColumnLayout { + id: settingsLog + property int itemHeight: 60 * scaleRatio + Layout.fillWidth: true + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: (isMobile)? 17 : 20 + anchors.topMargin: 0 + spacing: 10 + +// Rectangle { +// // divider +// Layout.preferredHeight: 1 * scaleRatio +// Layout.fillWidth: true +// Layout.bottomMargin: 8 * scaleRatio +// color: MoneroComponents.Style.dividerColor +// opacity: MoneroComponents.Style.dividerOpacity +// } + + Text { + Layout.bottomMargin: 2 * scaleRatio + color: MoneroComponents.Style.defaultFontColor + font.pixelSize: 18 * scaleRatio + font.family: MoneroComponents.Style.fontRegular.name + text: qsTr("Log level") + translationManager.emptyString + } + + GridLayout { + id: logGrid + columns: appWindow.persistentSettings.logLevel === 5 ? 2 : 1 + Layout.fillWidth: true + columnSpacing: 32 * scaleRatio + z: parent.z + 1 + + ColumnLayout { + spacing: 0 + Layout.fillWidth: true + + ListModel { + id: logLevel + ListElement { column1: "0"; name: "none"; } + ListElement { column1: "1"; } + ListElement { column1: "2"; } + ListElement { column1: "3"; } + ListElement { column1: "4"; } + ListElement { column1: "custom"; } + } + + MoneroComponents.StandardDropdown { + id: logLevelDropdown + dataModel: logLevel + itemTopMargin: 2 * scaleRatio + currentIndex: appWindow.persistentSettings.logLevel; + onChanged: { + if (currentIndex == 5) { + console.log("log categories changed: ", logCategories.text); + walletManager.setLogCategories(logCategories.text); + } + else { + console.log("log level changed: ",currentIndex); + walletManager.setLogLevel(currentIndex); + } + appWindow.persistentSettings.logLevel = currentIndex; + } + Layout.fillWidth: true + Layout.preferredWidth: logGrid.width / 2 + shadowReleasedColor: "#FF4304" + shadowPressedColor: "#B32D00" + releasedColor: "#363636" + pressedColor: "#202020" + } + } + + MoneroComponents.LineEdit { + id: logCategories + visible: persistentSettings.logLevel === 5 + Layout.fillWidth: true + Layout.preferredWidth: logGrid.width / 2 + text: appWindow.persistentSettings.logCategories + placeholderText: "(e.g. *:WARNING,net.p2p:DEBUG)" + placeholderFontSize: 14 * scaleRatio + fontSize: 14 * scaleRatio + enabled: logLevelDropdown.currentIndex === 5 + onEditingFinished: { + if(enabled) { + console.log("log categories changed: ", text); + walletManager.setLogCategories(text); + appWindow.persistentSettings.logCategories = text; + } + } + } + } + + Text { + Layout.topMargin: 10 * scaleRatio + Layout.bottomMargin: 2 * scaleRatio + color: MoneroComponents.Style.defaultFontColor + font.pixelSize: 18 * scaleRatio + font.family: MoneroComponents.Style.fontRegular.name + text: qsTr("Daemon log") + translationManager.emptyString + } + + Flickable { + id: flickable + Layout.fillWidth: true + Layout.preferredHeight: 240 * scaleRatio + + TextArea.flickable: TextArea { + id : consoleArea + anchors.fill: parent + color: MoneroComponents.Style.defaultFontColor + selectionColor: MoneroComponents.Style.dimmedFontColor + textFormat: TextEdit.RichText + selectByMouse: true + selectByKeyboard: true + font.family: "Ariel" + font.pixelSize: 14 * scaleRatio + wrapMode: TextEdit.Wrap + readOnly: true + background: Rectangle { + color: "transparent" + anchors.fill: parent + border.color: Qt.rgba(255, 255, 255, 0.25); + border.width: 1 + radius: 4 + } + function logCommand(msg){ + msg = log_color(msg, "lime"); + consoleArea.append(msg); + } + function logMessage(msg){ + msg = msg.trim(); + var color = "white"; + if(msg.toLowerCase().indexOf('error') >= 0){ + color = "red"; + } else if (msg.toLowerCase().indexOf('warning') >= 0){ + color = "yellow"; + } + + // format multi-lines + if(msg.split("\n").length >= 2){ + msg = msg.split("\n").join('
'); + } + + log(msg, color); + } + function log_color(msg, color){ + return "" + msg + ""; + } + function log(msg, color){ + var timestamp = Utils.formatDate(new Date(), { + weekday: undefined, + month: "numeric", + timeZoneName: undefined + }); + + var _timestamp = log_color("[" + timestamp + "]", "#FFFFFF"); + var _msg = log_color(msg, color); + consoleArea.append(_timestamp + " " + _msg); + + // scroll to bottom + //if(flickable.contentHeight > content.height){ + // flickable.contentY = flickable.contentHeight; + //} + } + } + + ScrollBar.vertical: ScrollBar { + // TODO: scrollbar always visible is buggy. + // QT 5.9 introduces `policy: ScrollBar.AlwaysOn` + contentItem.opacity: 1 + anchors.top: flickable.top + anchors.left: flickable.right + anchors.leftMargin: 10 * scaleRatio + anchors.bottom: flickable.bottom + } + } + + MoneroComponents.LineEdit { + id: sendCommandText + Layout.fillWidth: true + fontBold: false + placeholderText: qsTr("command + enter (e.g 'help' or 'status')") + translationManager.emptyString + placeholderFontSize: 16 * scaleRatio + onAccepted: { + if(text.length > 0) { + consoleArea.logCommand(">>> " + text) + daemonManager.sendCommand(text, currentWallet.nettype); + } + text = "" + } + } + } + + Component.onCompleted: { + logLevelDropdown.currentIndex = persistentSettings.logLevel; + logLevelDropdown.update(); + + if(typeof daemonManager != "undefined") + daemonManager.daemonConsoleUpdated.connect(onDaemonConsoleUpdated) + } +} diff --git a/pages/settings/SettingsNode.qml b/pages/settings/SettingsNode.qml new file mode 100644 index 00000000..0e9bccfb --- /dev/null +++ b/pages/settings/SettingsNode.qml @@ -0,0 +1,497 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick 2.7 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 +import "../../components" as MoneroComponents + +Rectangle{ + color: "transparent" + height: 1400 + Layout.fillWidth: true + + /* main layout */ + ColumnLayout { + id: root + anchors.margins: (isMobile)? 17 : 20 + anchors.topMargin: 0 + + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + + spacing: 0 * scaleRatio + property int labelWidth: 120 + property int editWidth: 400 + property int lineEditFontSize: 14 * scaleRatio + property int buttonWidth: 110 + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 90 + color: "transparent" + + Rectangle { + id: localNodeDivider + Layout.fillWidth: true + anchors.topMargin: 0 * scaleRatio + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + Rectangle { + visible: !persistentSettings.useRemoteNode + Layout.fillHeight: true + anchors.top: parent.top + anchors.bottom: parent.bottom + color: "white" + width: 2 + } + + Rectangle { + width: parent.width + height: localNodeHeader.height + localNodeArea.contentHeight + color: "transparent"; + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + + Rectangle { + id: localNodeIcon + color: "transparent" + height: 32 + width: 32 + anchors.left: parent.left + anchors.leftMargin: 16 * scaleRatio + anchors.verticalCenter: parent.verticalCenter + + Image{ + height: 27 + width: 27 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + source: "../../images/settings_local.png" + } + } + + Text { + id: localNodeHeader + anchors.left: localNodeIcon.right + anchors.leftMargin: 14 * scaleRatio + anchors.top: parent.top + color: "white" + font.bold: true + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 16 * scaleRatio + text: qsTr("Local node") + translationManager.emptyString + } + + TextArea { + id: localNodeArea + anchors.top: localNodeHeader.bottom + anchors.topMargin: 4 * scaleRatio + anchors.left: localNodeIcon.right + anchors.leftMargin: 14 * scaleRatio + color: MoneroComponents.Style.dimmedFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 15 * scaleRatio + horizontalAlignment: TextInput.AlignLeft + selectByMouse: false + wrapMode: Text.WordWrap; + textMargin: 0 + leftPadding: 0 + topPadding: 0 + text: qsTr("The blockchain is downloaded to your computer. Provides higher security and requires more local storage.") + translationManager.emptyString + width: parent.width - (localNodeIcon.width + localNodeIcon.anchors.leftMargin + anchors.leftMargin) + + // @TODO: Legacy. Remove after Qt 5.8. + // https://stackoverflow.com/questions/41990013 + MouseArea { + anchors.fill: parent + enabled: false + } + } + } + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: { + persistentSettings.useRemoteNode = false; + appWindow.disconnectRemoteNode(); + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 90 + color: "transparent" + + Rectangle { + id: remoteNodeDivider + Layout.fillWidth: true + anchors.topMargin: 0 * scaleRatio + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + Rectangle { + visible: persistentSettings.useRemoteNode + Layout.fillHeight: true + anchors.top: parent.top + anchors.bottom: parent.bottom + color: "white" + width: 2 + } + + Rectangle { + width: parent.width + height: remoteNodeHeader.height + remoteNodeArea.contentHeight + color: "transparent"; + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + + Rectangle { + id: remoteNodeIcon + color: "transparent" + height: 32 + width: 32 + anchors.left: parent.left + anchors.leftMargin: 16 * scaleRatio + anchors.verticalCenter: parent.verticalCenter + + Image{ + height: 27 + width: 22 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + source: "../../images/settings_remote.png" + } + } + + Text { + id: remoteNodeHeader + anchors.left: remoteNodeIcon.right + anchors.leftMargin: 14 * scaleRatio + anchors.top: parent.top + color: "white" + font.bold: true + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 16 * scaleRatio + text: qsTr("Remote node") + translationManager.emptyString + } + + TextArea { + id: remoteNodeArea + anchors.top: remoteNodeHeader.bottom + anchors.topMargin: 4 * scaleRatio + anchors.left: remoteNodeIcon.right + anchors.leftMargin: 14 * scaleRatio + color: MoneroComponents.Style.dimmedFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 15 * scaleRatio + activeFocusOnPress: false + horizontalAlignment: TextInput.AlignLeft + selectByMouse: false + wrapMode: Text.WordWrap; + textMargin: 0 + leftPadding: 0 + topPadding: 0 + text: qsTr("Uses a third-party server to connect to the Monero network. Less secure, but easier on your computer.") + translationManager.emptyString + width: parent.width - (remoteNodeIcon.width + remoteNodeIcon.anchors.leftMargin + anchors.leftMargin) + + // @TODO: Legacy. Remove after Qt 5.8. + // https://stackoverflow.com/questions/41990013 + MouseArea { + anchors.fill: parent + enabled: false + } + } + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: { + persistentSettings.useRemoteNode = true; + appWindow.connectRemoteNode(); + } + } + } + + Rectangle { + id: localNodeBottomDivider + Layout.fillWidth: true + anchors.topMargin: 0 * scaleRatio + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: 1 + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + } + + ColumnLayout { + id: remoteNodeLayout + anchors.margins: 0 + spacing: 20 * scaleRatio + Layout.fillWidth: true + Layout.topMargin: 20 + visible: !isMobile && persistentSettings.useRemoteNode + + MoneroComponents.WarningBox { + Layout.topMargin: 26 * scaleRatio + Layout.bottomMargin: 6 * scaleRatio + text: qsTr("To find a remote node, type 'Monero remote node' into your favorite search engine. Please ensure the node is run by a trusted third-party.") + translationManager.emptyString + } + + MoneroComponents.RemoteNodeEdit { + id: remoteNodeEdit + Layout.minimumWidth: 100 * scaleRatio + + lineEditBackgroundColor: "transparent" + lineEditFontColor: "white" + lineEditFontBold: false + lineEditBorderColor: Qt.rgba(255, 255, 255, 0.35) + labelFontSize: 14 * scaleRatio + placeholderFontSize: 15 * scaleRatio + + daemonAddrLabelText: qsTr("Address") + daemonPortLabelText: qsTr("Port") + + property var rna: persistentSettings.remoteNodeAddress + daemonAddrText: rna.search(":") != -1 ? rna.split(":")[0].trim() : "" + daemonPortText: rna.search(":") != -1 ? (rna.split(":")[1].trim() == "") ? "18081" : rna.split(":")[1] : "" + onEditingFinished: { + persistentSettings.remoteNodeAddress = remoteNodeEdit.getAddress(); + console.log("setting remote node to " + persistentSettings.remoteNodeAddress) + } + } + + GridLayout { + columns: (isMobile) ? 1 : 2 + columnSpacing: 32 + + MoneroComponents.LineEdit { + id: daemonUsername + Layout.fillWidth: true + labelText: "Daemon username" + text: persistentSettings.daemonUsername + placeholderText: qsTr("(optional)") + translationManager.emptyString + placeholderFontSize: 15 * scaleRatio + labelFontSize: 14 * scaleRatio + fontSize: 15 * scaleRatio + } + + MoneroComponents.LineEdit { + id: daemonPassword + Layout.fillWidth: true + labelText: "Daemon password" + text: persistentSettings.daemonPassword + placeholderText: qsTr("Password") + translationManager.emptyString + echoMode: TextInput.Password + placeholderFontSize: 15 * scaleRatio + labelFontSize: 14 * scaleRatio + fontSize: 15 * scaleRatio + } + } + + Rectangle { + id: rectConnectRemote + Layout.topMargin: 12 * scaleRatio + color: MoneroComponents.Style.buttonBackgroundColorDisabled + width: btnConnectRemote.width + 40 + height: 26 + radius: 2 + + Text { + id: btnConnectRemote + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: MoneroComponents.Style.defaultFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 * scaleRatio + font.bold: true + text: qsTr("Connect") + translationManager.emptyString + } + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: { + // Update daemon login + persistentSettings.remoteNodeAddress = remoteNodeEdit.getAddress(); + persistentSettings.daemonUsername = daemonUsername.text; + persistentSettings.daemonPassword = daemonPassword.text; + persistentSettings.useRemoteNode = true + + currentWallet.setDaemonLogin(persistentSettings.daemonUsername, persistentSettings.daemonPassword); + + appWindow.connectRemoteNode() + } + } + } + } + + ColumnLayout { + id: localNodeLayout + anchors.margins: 0 + spacing: 20 * scaleRatio + Layout.topMargin: 40 + anchors.left: parent.left + anchors.right: parent.right + visible: !isMobile && !persistentSettings.useRemoteNode + + Rectangle { + color: "transparent" + Layout.topMargin: 0 * scaleRatio + Layout.bottomMargin: 8 * scaleRatio + Layout.preferredHeight: 24 * scaleRatio + Layout.preferredWidth: parent.width + + Rectangle { + id: rectStopNode + color: MoneroComponents.Style.buttonBackgroundColorDisabled + width: btnStopNode.width + 40 + height: 24 + radius: 2 + + Text { + id: btnStopNode + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: MoneroComponents.Style.defaultFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 * scaleRatio + font.bold: true + text: qsTr("Stop local node") + translationManager.emptyString + } + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: { + appWindow.stopDaemon(); + } + } + } + } + + RowLayout { + MoneroComponents.LineEditMulti { + id: blockchainFolder + Layout.preferredWidth: 200 + Layout.fillWidth: true + fontSize: 15 * scaleRatio + labelFontSize: 14 * scaleRatio + property string style: "" + labelText: qsTr("Blockchain location") + style + qsTr(" (change)") + translationManager.emptyString + placeholderText: qsTr("(default)") + translationManager.emptyString + placeholderFontSize: 15 * scaleRatio + text: { + if(persistentSettings.blockchainDataDir.length > 0){ + return persistentSettings.blockchainDataDir; + } else { return "" } + } + addressValidation: false + onInputLabelLinkActivated: { + //mouse.accepted = false + if(persistentSettings.blockchainDataDir !== ""){ + blockchainFileDialog.folder = "file://" + persistentSettings.blockchainDataDir; + } + blockchainFileDialog.open(); + blockchainFolder.focus = true; + } + } + } + + RowLayout { + id: daemonFlagsRow + + MoneroComponents.LineEditMulti { + id: daemonFlags + Layout.preferredWidth: 200 + Layout.fillWidth: true + labelFontSize: 14 * scaleRatio + fontSize: 15 * scaleRatio + labelText: qsTr("Daemon startup flags") + translationManager.emptyString + placeholderText: qsTr("(optional)") + translationManager.emptyString + placeholderFontSize: 15 * scaleRatio + text: appWindow.persistentSettings.daemonFlags + addressValidation: false + } + } + + RowLayout { + visible: !isMobile && !persistentSettings.useRemoteNode + + ColumnLayout { + Layout.fillWidth: true + + MoneroComponents.RemoteNodeEdit { + id: bootstrapNodeEdit + Layout.minimumWidth: 100 * scaleRatio + Layout.bottomMargin: 20 * scaleRatio + + lineEditBackgroundColor: "transparent" + lineEditFontColor: "white" + lineEditBorderColor: MoneroComponents.Style.inputBorderColorActive + placeholderFontSize: 15 * scaleRatio + labelFontSize: 14 * scaleRatio + lineEditFontBold: false + lineEditFontSize: 15 * scaleRatio + + daemonAddrLabelText: qsTr("Bootstrap Address") + daemonPortLabelText: qsTr("Bootstrap Port") + daemonAddrText: persistentSettings.bootstrapNodeAddress.split(":")[0].trim() + daemonPortText: { + var node_split = persistentSettings.bootstrapNodeAddress.split(":"); + if(node_split.length == 2){ + (node_split[1].trim() == "") ? "18081" : node_split[1]; + } else { + return "" + } + } + onEditingFinished: { + persistentSettings.bootstrapNodeAddress = daemonAddrText ? bootstrapNodeEdit.getAddress() : ""; + console.log("setting bootstrap node to " + persistentSettings.bootstrapNodeAddress) + } + } + } + } + } + } +} + diff --git a/pages/settings/SettingsWallet.qml b/pages/settings/SettingsWallet.qml new file mode 100644 index 00000000..37cce2e4 --- /dev/null +++ b/pages/settings/SettingsWallet.qml @@ -0,0 +1,433 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick 2.7 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 +import QtQuick.Dialogs 1.2 + +import "../../js/Utils.js" as Utils +import "../../components" as MoneroComponents + +Rectangle { + color: "transparent" + height: 1400 + Layout.fillWidth: true + + ColumnLayout { + id: settingsWallet + property int itemHeight: 60 * scaleRatio + Layout.fillWidth: true + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: (isMobile)? 17 : 20 + anchors.topMargin: 0 + spacing: 0 + + Rectangle { + // divider + Layout.preferredHeight: 1 * scaleRatio + Layout.fillWidth: true + Layout.bottomMargin: 8 * scaleRatio + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + GridLayout { + Layout.fillWidth: true + Layout.preferredHeight: settingsWallet.itemHeight + columnSpacing: 0 + + ColumnLayout { + Layout.fillWidth: true + anchors.verticalCenter: parent.verticalCenter + spacing: 0 + + Text { + Layout.fillWidth: true + Layout.preferredHeight: 20 * scaleRatio + Layout.topMargin: 8 * scaleRatio + color: "white" + font.bold: true + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 16 * scaleRatio + text: qsTr("Close this wallet") + translationManager.emptyString + } + + TextArea { + Layout.fillWidth: true + color: MoneroComponents.Style.dimmedFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 * scaleRatio + horizontalAlignment: TextInput.AlignLeft + selectByMouse: false + wrapMode: Text.WordWrap; + textMargin: 0 + leftPadding: 0 + topPadding: 0 + text: qsTr("Logs out of this wallet.") + translationManager.emptyString + width: parent.width + readOnly: true + + // @TODO: Legacy. Remove after Qt 5.8. + // https://stackoverflow.com/questions/41990013 + MouseArea { + anchors.fill: parent + enabled: false + } + } + } + + Rectangle { + Layout.minimumWidth: 120 * scaleRatio + Layout.preferredWidth: closeWalletText.width + (20 * scaleRatio) + Layout.preferredHeight: parent.height + color: "transparent" + + Rectangle{ + width: parent.width + height: 24 * scaleRatio + radius: 2 * scaleRatio + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: MoneroComponents.Style.buttonBackgroundColorDisabled + + Text { + id: closeWalletText + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: MoneroComponents.Style.defaultFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 * scaleRatio + font.bold: true + text: qsTr("Close wallet") + translationManager.emptyString + } + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: { + appWindow.showWizard(); + } + } + } + } + } + + Rectangle { + // divider + Layout.preferredHeight: 1 * scaleRatio + Layout.fillWidth: true + Layout.topMargin: 8 * scaleRatio + Layout.bottomMargin: 8 * scaleRatio + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + GridLayout { + Layout.fillWidth: true + Layout.preferredHeight: settingsWallet.itemHeight + columnSpacing: 0 + + ColumnLayout { + Layout.fillWidth: true + anchors.verticalCenter: parent.verticalCenter + spacing: 0 + + Text { + Layout.fillWidth: true + Layout.preferredHeight: 20 * scaleRatio + Layout.topMargin: 8 * scaleRatio + color: "white" + font.bold: true + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 16 * scaleRatio + text: qsTr("Create a view-only wallet") + translationManager.emptyString + } + + TextArea { + Layout.fillWidth: true + color: MoneroComponents.Style.dimmedFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 * scaleRatio + horizontalAlignment: TextInput.AlignLeft + selectByMouse: false + wrapMode: Text.WordWrap; + textMargin: 0 + leftPadding: 0 + topPadding: 0 + text: qsTr("Creates a new wallet that can only view transactions, cannot initialize transactions.") + translationManager.emptyString + width: parent.width + readOnly: true + + // @TODO: Legacy. Remove after Qt 5.8. + // https://stackoverflow.com/questions/41990013 + MouseArea { + anchors.fill: parent + enabled: false + } + } + } + + Rectangle { + Layout.minimumWidth: 120 * scaleRatio + Layout.preferredWidth: createViewOnlyText.width + (20 * scaleRatio) + Layout.preferredHeight: parent.height + color: "transparent" + + Rectangle{ + width: parent.width + height: 24 * scaleRatio + radius: 2 * scaleRatio + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: MoneroComponents.Style.buttonBackgroundColorDisabled + + Text { + id: createViewOnlyText + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: MoneroComponents.Style.defaultFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 * scaleRatio + font.bold: true + text: qsTr("Create wallet") + translationManager.emptyString + } + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: { + wizard.openCreateViewOnlyWalletPage(); + } + } + } + } + } + + Rectangle { + // divider + Layout.preferredHeight: 1 * scaleRatio + Layout.fillWidth: true + Layout.topMargin: 8 * scaleRatio + Layout.bottomMargin: 8 * scaleRatio + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + GridLayout { + Layout.fillWidth: true + Layout.preferredHeight: settingsWallet.itemHeight + columnSpacing: 0 + + ColumnLayout { + Layout.fillWidth: true + anchors.verticalCenter: parent.verticalCenter + spacing: 0 + + Text { + Layout.fillWidth: true + Layout.preferredHeight: 20 * scaleRatio + Layout.topMargin: 8 * scaleRatio + color: "white" + font.bold: true + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 16 * scaleRatio + text: qsTr("Show seed & keys") + translationManager.emptyString + } + + TextArea { + Layout.fillWidth: true + color: MoneroComponents.Style.dimmedFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 * scaleRatio + horizontalAlignment: TextInput.AlignLeft + selectByMouse: false + wrapMode: Text.WordWrap; + textMargin: 0 * scaleRatio + leftPadding: 0 + topPadding: 0 + text: qsTr("Store this information safely to recover your wallet in the future.") + translationManager.emptyString + width: parent.width + readOnly: true + + // @TODO: Legacy. Remove after Qt 5.8. + // https://stackoverflow.com/questions/41990013 + MouseArea { + anchors.fill: parent + enabled: false + } + } + } + + Rectangle { + Layout.minimumWidth: 120 * scaleRatio + Layout.preferredWidth: showSeedText.width + (20 * scaleRatio) + Layout.preferredHeight: parent.height + color: "transparent" + + Rectangle{ + width: parent.width + height: 24 * scaleRatio + radius: 2 * scaleRatio + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: MoneroComponents.Style.buttonBackgroundColorDisabled + + Text { + id: showSeedText + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: MoneroComponents.Style.defaultFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 * scaleRatio + font.bold: true + text: qsTr("Show seed") + translationManager.emptyString + } + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: Utils.showSeedPage(); + } + } + } + } + + Rectangle { + // divider + Layout.preferredHeight: 1 * scaleRatio + Layout.fillWidth: true + Layout.topMargin: 8 * scaleRatio + Layout.bottomMargin: 8 * scaleRatio + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + GridLayout { + Layout.fillWidth: true + Layout.preferredHeight: settingsWallet.itemHeight + columnSpacing: 0 + + ColumnLayout { + Layout.fillWidth: true + anchors.verticalCenter: parent.verticalCenter + spacing: 0 + + Text { + Layout.fillWidth: true + Layout.preferredHeight: 20 * scaleRatio + Layout.topMargin: 8 * scaleRatio + color: "white" + font.bold: true + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 16 * scaleRatio + text: qsTr("Rescan wallet balance") + translationManager.emptyString + } + + TextArea { + Layout.fillWidth: true + color: MoneroComponents.Style.dimmedFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 * scaleRatio + horizontalAlignment: TextInput.AlignLeft + selectByMouse: false + wrapMode: Text.WordWrap; + textMargin: 0 + leftPadding: 0 + topPadding: 0 + text: qsTr("Use this feature if you think the shown balance is not accurate.") + translationManager.emptyString + width: parent.width + readOnly: true + + // @TODO: Legacy. Remove after Qt 5.8. + // https://stackoverflow.com/questions/41990013 + MouseArea { + anchors.fill: parent + enabled: false + } + } + } + + Rectangle { + Layout.minimumWidth: 120 * scaleRatio + Layout.preferredWidth: rescanButtonText.width + (20 * scaleRatio) + Layout.preferredHeight: parent.height + color: "transparent" + + Rectangle{ + width: parent.width + + height: 24 * scaleRatio + radius: 2 * scaleRatio + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: MoneroComponents.Style.buttonBackgroundColorDisabled + + Text { + id: rescanButtonText + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: MoneroComponents.Style.defaultFontColor + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 * scaleRatio + font.bold: true + text: qsTr("Rescan") + translationManager.emptyString + } + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onClicked: { + if (!currentWallet.rescanSpent()) { + console.error("Error: ", currentWallet.errorString); + informationPopup.title = qsTr("Error") + translationManager.emptyString; + informationPopup.text = qsTr("Error: ") + currentWallet.errorString + informationPopup.icon = StandardIcon.Critical + informationPopup.onCloseCallback = null + informationPopup.open(); + } else { + informationPopup.title = qsTr("Information") + translationManager.emptyString + informationPopup.text = qsTr("Successfully rescanned spent outputs.") + translationManager.emptyString + informationPopup.icon = StandardIcon.Information + informationPopup.onCloseCallback = null + informationPopup.open(); + } + } + } + } + } + } + } + + Component.onCompleted: { + console.log('SettingsWallet loaded'); + } +} + diff --git a/qml.qrc b/qml.qrc index 55799bbc..8d6f655b 100644 --- a/qml.qrc +++ b/qml.qrc @@ -19,7 +19,6 @@ pages/History.qml pages/AddressBook.qml pages/Mining.qml - pages/Settings.qml components/NetworkStatusItem.qml images/statusConnected.png images/statusDisconnected.png @@ -210,5 +209,17 @@ js/Utils.js components/RadioButton.qml images/editIcon.png + pages/settings/Settings.qml + pages/settings/SettingsWallet.qml + pages/settings/SettingsNode.qml + pages/settings/SettingsLog.qml + pages/settings/SettingsLayout.qml + pages/settings/SettingsInfo.qml + pages/settings/Navbar.qml + images/settings_remote.png + images/settings_navbar_side.png + images/settings_navbar_side_active.png + images/settings_local.png + components/WarningBox.qml