Merge pull request #2240

35a0f25 PasswordDialog: merge components and bug fixes (selsta)
This commit is contained in:
luigi1111 2019-07-15 18:38:32 -05:00
commit 3a9f0e1100
No known key found for this signature in database
GPG key ID: F4ACA0183641E010
13 changed files with 369 additions and 713 deletions

View file

@ -1,287 +0,0 @@
// Copyright (c) 2014-2019, 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.9
import QtQuick.Controls 2.0
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.0
import FontAwesome 1.0
import "../components" as MoneroComponents
Item {
id: root
visible: false
z: parent.z + 2
property bool isHidden: true
property alias password: passwordInput1.text
// same signals as Dialog has
signal accepted()
signal rejected()
signal closeCallback()
function open() {
isHidden = true
passwordInput1.echoMode = TextInput.Password;
passwordInput2.echoMode = TextInput.Password;
inactiveOverlay.visible = true
leftPanel.enabled = false
middlePanel.enabled = false
titleBar.state = "essentials"
root.visible = true;
passwordInput1.text = "";
passwordInput2.text = "";
passwordInput1.focus = true
}
function close() {
inactiveOverlay.visible = false
leftPanel.enabled = true
middlePanel.enabled = true
titleBar.state = "default"
root.visible = false;
closeCallback();
}
function toggleIsHidden() {
passwordInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
passwordInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
isHidden = !isHidden;
}
// TODO: implement without hardcoding sizes
width: 480
height: 360
ColumnLayout {
z: inactiveOverlay.z + 1
id: mainLayout
spacing: 10
anchors { fill: parent; margins: 35 }
ColumnLayout {
id: column
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Layout.maximumWidth: 400
Label {
text: qsTr("Please enter new password") + translationManager.emptyString
Layout.fillWidth: true
font.pixelSize: 16
font.family: MoneroComponents.Style.fontLight.name
color: MoneroComponents.Style.defaultFontColor
}
TextField {
id : passwordInput1
Layout.topMargin: 6
Layout.fillWidth: true
horizontalAlignment: TextInput.AlignLeft
verticalAlignment: TextInput.AlignVCenter
font.family: MoneroComponents.Style.fontLight.name
font.pixelSize: 24
echoMode: TextInput.Password
bottomPadding: 10
leftPadding: 10
topPadding: 10
color: MoneroComponents.Style.defaultFontColor
selectionColor: MoneroComponents.Style.textSelectionColor
selectedTextColor: MoneroComponents.Style.textSelectedColor
KeyNavigation.tab: passwordInput2
background: Rectangle {
radius: 2
border.color: MoneroComponents.Style.inputBorderColorInActive
border.width: 1
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
MoneroComponents.Label {
fontSize: 20
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
opacity: 0.7
fontFamily: FontAwesome.fontFamily
anchors.right: parent.right
anchors.rightMargin: 15
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
toggleIsHidden()
}
onEntered: {
parent.opacity = 0.9
parent.fontSize = 24
}
onExited: {
parent.opacity = 0.7
parent.fontSize = 20
}
}
}
}
Keys.onEscapePressed: {
root.close()
root.rejected()
}
}
// padding
Rectangle {
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
height: 10
opacity: 0
color: "black"
}
Label {
text: qsTr("Please confirm new password") + translationManager.emptyString
Layout.fillWidth: true
font.pixelSize: 16
font.family: MoneroComponents.Style.fontLight.name
color: MoneroComponents.Style.defaultFontColor
}
TextField {
id : passwordInput2
Layout.topMargin: 6
Layout.fillWidth: true
horizontalAlignment: TextInput.AlignLeft
verticalAlignment: TextInput.AlignVCenter
font.family: MoneroComponents.Style.fontLight.name
font.pixelSize: 24
echoMode: TextInput.Password
KeyNavigation.tab: okButton
bottomPadding: 10
leftPadding: 10
topPadding: 10
color: MoneroComponents.Style.defaultFontColor
selectionColor: MoneroComponents.Style.textSelectionColor
selectedTextColor: MoneroComponents.Style.textSelectedColor
background: Rectangle {
radius: 2
border.color: MoneroComponents.Style.inputBorderColorInActive
border.width: 1
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
MoneroComponents.Label {
fontSize: 20
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
opacity: 0.7
fontFamily: FontAwesome.fontFamily
anchors.right: parent.right
anchors.rightMargin: 15
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
toggleIsHidden()
}
onEntered: {
parent.opacity = 0.9
parent.fontSize = 24
}
onExited: {
parent.opacity = 0.7
parent.fontSize = 20
}
}
}
}
Keys.onReturnPressed: {
if (passwordInput1.text === passwordInput2.text) {
root.close()
root.accepted()
}
}
Keys.onEscapePressed: {
root.close()
root.rejected()
}
}
// padding
Rectangle {
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
height: 10
opacity: 0
color: "black"
}
// Ok/Cancel buttons
RowLayout {
id: buttons
spacing: 16
Layout.topMargin: 16
Layout.alignment: Qt.AlignRight
MoneroComponents.StandardButton {
id: cancelButton
text: qsTr("Cancel") + translationManager.emptyString
KeyNavigation.tab: passwordInput1
onClicked: {
root.close()
root.rejected()
}
}
MoneroComponents.StandardButton {
id: okButton
text: qsTr("Continue") + translationManager.emptyString
KeyNavigation.tab: cancelButton
enabled: passwordInput1.text === passwordInput2.text
onClicked: {
root.close()
root.accepted()
}
}
}
}
}
}

View file

@ -1,321 +0,0 @@
// Copyright (c) 2014-2019, 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.Controls 2.0
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.0
import FontAwesome 1.0
import "../components" as MoneroComponents
Item {
id: root
visible: false
z: parent.z + 2
property bool isHidden: true
property alias passphrase: passphaseInput1.text
property string walletName
property string errorText
// same signals as Dialog has
signal accepted()
signal rejected()
signal closeCallback()
function open(walletName, errorText) {
isHidden = true
passphaseInput1.echoMode = TextInput.Password;
passphaseInput2.echoMode = TextInput.Password;
inactiveOverlay.visible = true
root.walletName = walletName ? walletName : ""
root.errorText = errorText ? errorText : "";
leftPanel.enabled = false
middlePanel.enabled = false
titleBar.state = "essentials"
root.visible = true;
passphaseInput1.text = "";
passphaseInput2.text = "";
passphaseInput1.focus = true
}
function close() {
inactiveOverlay.visible = false
leftPanel.enabled = true
middlePanel.enabled = true
titleBar.state = "default"
root.visible = false;
closeCallback();
}
function toggleIsHidden() {
passphaseInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
passphaseInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
isHidden = !isHidden;
}
function showError(errorText) {
open(root.walletName, errorText);
}
// TODO: implement without hardcoding sizes
width: 480
height: 360
ColumnLayout {
z: inactiveOverlay.z + 1
id: mainLayout
spacing: 10
anchors { fill: parent; margins: 35 }
ColumnLayout {
id: column
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Layout.maximumWidth: 400
Label {
text: (root.walletName.length > 0 ? qsTr("Please enter wallet device passphrase for: ") + root.walletName : qsTr("Please enter wallet device passphrase")) + translationManager.emptyString
Layout.fillWidth: true
font.pixelSize: 16
font.family: MoneroComponents.Style.fontLight.name
color: MoneroComponents.Style.defaultFontColor
}
Label {
text: qsTr("Warning: passphrase entry on host is a security risk as it can be captured by malware. It is advised to prefer device-based passphrase entry.") + translationManager.emptyString
Layout.fillWidth: true
wrapMode: Text.Wrap
font.pixelSize: 14
font.family: MoneroComponents.Style.fontLight.name
color: MoneroComponents.Style.warningColor
}
Label {
text: root.errorText
visible: root.errorText
color: MoneroComponents.Style.errorColor
font.pixelSize: 16
font.family: MoneroComponents.Style.fontLight.name
Layout.fillWidth: true
wrapMode: Text.Wrap
}
TextField {
id : passphaseInput1
Layout.topMargin: 6
Layout.fillWidth: true
horizontalAlignment: TextInput.AlignLeft
verticalAlignment: TextInput.AlignVCenter
font.family: MoneroComponents.Style.fontLight.name
font.pixelSize: 24
echoMode: TextInput.Password
bottomPadding: 10
leftPadding: 10
topPadding: 10
color: MoneroComponents.Style.defaultFontColor
selectionColor: MoneroComponents.Style.textSelectionColor
selectedTextColor: MoneroComponents.Style.textSelectedColor
KeyNavigation.tab: passphaseInput2
background: Rectangle {
radius: 2
border.color: MoneroComponents.Style.inputBorderColorInActive
border.width: 1
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
MoneroComponents.Label {
fontSize: 20
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
opacity: 0.7
fontFamily: FontAwesome.fontFamily
anchors.right: parent.right
anchors.rightMargin: 15
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
toggleIsHidden()
}
onEntered: {
parent.opacity = 0.9
parent.fontSize = 24
}
onExited: {
parent.opacity = 0.7
parent.fontSize = 20
}
}
}
}
Keys.onEscapePressed: {
root.close()
root.rejected()
}
}
// padding
Rectangle {
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
height: 10
opacity: 0
color: "transparent"
}
Label {
text: qsTr("Please re-enter") + translationManager.emptyString
Layout.fillWidth: true
font.pixelSize: 16
font.family: MoneroComponents.Style.fontLight.name
color: MoneroComponents.Style.defaultFontColor
}
TextField {
id : passphaseInput2
Layout.topMargin: 6
Layout.fillWidth: true
horizontalAlignment: TextInput.AlignLeft
verticalAlignment: TextInput.AlignVCenter
font.family: MoneroComponents.Style.fontLight.name
font.pixelSize: 24
echoMode: TextInput.Password
KeyNavigation.tab: okButton
bottomPadding: 10
leftPadding: 10
topPadding: 10
color: MoneroComponents.Style.defaultFontColor
selectionColor: MoneroComponents.Style.dimmedFontColor
selectedTextColor: MoneroComponents.Style.defaultFontColor
background: Rectangle {
radius: 2
border.color: MoneroComponents.Style.inputBorderColorInActive
border.width: 1
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
MoneroComponents.Label {
fontSize: 20
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
opacity: 0.7
fontFamily: FontAwesome.fontFamily
anchors.right: parent.right
anchors.rightMargin: 15
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
toggleIsHidden()
}
onEntered: {
parent.opacity = 0.9
parent.fontSize = 24
}
onExited: {
parent.opacity = 0.7
parent.fontSize = 20
}
}
}
}
Keys.onReturnPressed: {
if (passphaseInput1.text === passphaseInput2.text) {
root.close()
root.accepted()
}
}
Keys.onEscapePressed: {
root.close()
root.rejected()
}
}
// padding
Rectangle {
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
height: 10
opacity: 0
color: "black"
}
// Ok/Cancel buttons
RowLayout {
id: buttons
spacing: 16
Layout.topMargin: 16
Layout.alignment: Qt.AlignRight
MoneroComponents.StandardButton {
id: cancelButton
text: qsTr("Cancel") + translationManager.emptyString
KeyNavigation.tab: passphaseInput1
onClicked: {
root.close()
root.rejected()
}
}
MoneroComponents.StandardButton {
id: okButton
text: qsTr("Continue") + translationManager.emptyString
KeyNavigation.tab: cancelButton
enabled: passphaseInput1.text === passphaseInput2.text
onClicked: {
root.close()
root.accepted()
}
}
}
}
}
}

View file

@ -44,34 +44,63 @@ Item {
z: parent.z + 2 z: parent.z + 2
property bool isHidden: true property bool isHidden: true
property alias password: passwordInput.text property alias password: passwordInput1.text
property string walletName property string walletName
property string errorText property string errorText
property bool shiftIsPressed: false property bool passwordDialogMode
property bool isCapsLocksActive: false property bool passphraseDialogMode
property bool backspaceIsPressed: false property bool newPasswordDialogMode
// same signals as Dialog has // same signals as Dialog has
signal accepted() signal accepted()
signal acceptedNewPassword()
signal acceptedPassphrase()
signal rejected() signal rejected()
signal rejectedNewPassword()
signal rejectedPassphrase()
signal closeCallback() signal closeCallback()
function open(walletName, errorText) { function _openInit(walletName, errorText) {
isHidden = true isHidden = true
passwordInput.echoMode = TextInput.Password capsLockTextLabel.visible = oshelper.isCapsLock();
passwordInput.text = "" passwordInput1.echoMode = TextInput.Password
passwordInput.forceActiveFocus(); passwordInput2.echoMode = TextInput.Password
passwordInput1.text = ""
passwordInput2.text = ""
passwordInput1.forceActiveFocus();
inactiveOverlay.visible = true // draw appwindow inactive inactiveOverlay.visible = true // draw appwindow inactive
root.walletName = walletName ? walletName : "" root.walletName = walletName ? walletName : ""
errorTextLabel.text = errorText ? errorText : ""; errorTextLabel.text = errorText ? errorText : "";
leftPanel.enabled = false leftPanel.enabled = false
middlePanel.enabled = false middlePanel.enabled = false
wizard.enabled = false
titleBar.state = "essentials" titleBar.state = "essentials"
root.visible = true; root.visible = true;
appWindow.hideBalanceForced = true; appWindow.hideBalanceForced = true;
appWindow.updateBalance(); appWindow.updateBalance();
} }
function open(walletName, errorText) {
passwordDialogMode = true;
passphraseDialogMode = false;
newPasswordDialogMode = false;
_openInit(walletName, errorText);
}
function openPassphraseDialog() {
passwordDialogMode = false;
passphraseDialogMode = true;
newPasswordDialogMode = false;
_openInit("", "");
}
function openNewPasswordDialog() {
passwordDialogMode = false;
passphraseDialogMode = false;
newPasswordDialogMode = true;
_openInit("", "");
}
function showError(errorText) { function showError(errorText) {
open(root.walletName, errorText); open(root.walletName, errorText);
} }
@ -80,6 +109,7 @@ Item {
inactiveOverlay.visible = false inactiveOverlay.visible = false
leftPanel.enabled = true leftPanel.enabled = true
middlePanel.enabled = true middlePanel.enabled = true
wizard.enabled = true
titleBar.state = "default" titleBar.state = "default"
root.visible = false; root.visible = false;
@ -88,6 +118,12 @@ Item {
closeCallback(); closeCallback();
} }
function toggleIsHidden() {
passwordInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
passwordInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
isHidden = !isHidden;
}
ColumnLayout { ColumnLayout {
z: inactiveOverlay.z + 1 z: inactiveOverlay.z + 1
id: mainLayout id: mainLayout
@ -102,7 +138,11 @@ Item {
Layout.maximumWidth: 400 Layout.maximumWidth: 400
Label { Label {
text: (root.walletName.length > 0 ? qsTr("Please enter wallet password for: ") + root.walletName : qsTr("Please enter wallet password")) + translationManager.emptyString text: {
var device = passwordDialogMode ? qsTr("wallet password") : qsTr("wallet device passphrase");
(root.walletName.length > 0 ? qsTr("Please enter %1 for: ").arg(device) + root.walletName : qsTr("Please enter %1").arg(device)) + translationManager.emptyString;
}
visible: !newPasswordDialogMode
Layout.fillWidth: true Layout.fillWidth: true
font.pixelSize: 16 font.pixelSize: 16
@ -111,19 +151,41 @@ Item {
color: MoneroComponents.Style.defaultFontColor color: MoneroComponents.Style.defaultFontColor
} }
Label {
text: qsTr("Warning: passphrase entry on host is a security risk as it can be captured by malware. It is advised to prefer device-based passphrase entry.") + translationManager.emptyString
visible: passphraseDialogMode
Layout.fillWidth: true
wrapMode: Text.Wrap
font.pixelSize: 14
font.family: MoneroComponents.Style.fontLight.name
color: MoneroComponents.Style.warningColor
}
Label { Label {
id: errorTextLabel id: errorTextLabel
visible: root.errorText || text !== "" visible: root.errorText || text !== ""
color: MoneroComponents.Style.errorColor color: MoneroComponents.Style.errorColor
font.pixelSize: 16 font.pixelSize: 16
font.family: MoneroComponents.Style.fontLight.name font.family: MoneroComponents.Style.fontLight.name
Layout.fillWidth: true Layout.fillWidth: true
wrapMode: Text.Wrap wrapMode: Text.Wrap
} }
Label {
id: capsLockTextLabel
visible: false
color: MoneroComponents.Style.errorColor
font.pixelSize: 16
font.family: MoneroComponents.Style.fontLight.name
Layout.fillWidth: true
wrapMode: Text.Wrap
text: qsTr("CAPSLOCKS IS ON.") + translationManager.emptyString;
}
TextField { TextField {
id : passwordInput id: passwordInput1
Layout.topMargin: 6 Layout.topMargin: 6
Layout.fillWidth: true Layout.fillWidth: true
horizontalAlignment: TextInput.AlignLeft horizontalAlignment: TextInput.AlignLeft
@ -131,24 +193,20 @@ Item {
font.family: MoneroComponents.Style.fontLight.name font.family: MoneroComponents.Style.fontLight.name
font.pixelSize: 24 font.pixelSize: 24
echoMode: TextInput.Password echoMode: TextInput.Password
KeyNavigation.tab: okButton KeyNavigation.tab: {
if (passwordDialogMode) {
return okButton
} else {
return passwordInput2
}
}
bottomPadding: 10 bottomPadding: 10
leftPadding: 10 leftPadding: 10
topPadding: 10 topPadding: 10
color: MoneroComponents.Style.defaultFontColor color: MoneroComponents.Style.defaultFontColor
selectionColor: MoneroComponents.Style.textSelectionColor selectionColor: MoneroComponents.Style.textSelectionColor
selectedTextColor: MoneroComponents.Style.textSelectedColor selectedTextColor: MoneroComponents.Style.textSelectedColor
onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock();
onTextChanged: {
var letter = text[passwordInput.text.length - 1];
isCapsLocksActive = Utils.isUpperLock(shiftIsPressed, letter);
if(isCapsLocksActive && !backspaceIsPressed){
errorTextLabel.text = qsTr("CAPSLOCKS IS ON.") + translationManager.emptyString;
}
else{
errorTextLabel.text = "";
}
}
background: Rectangle { background: Rectangle {
radius: 2 radius: 2
@ -177,8 +235,7 @@ Item {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
hoverEnabled: true hoverEnabled: true
onClicked: { onClicked: {
passwordInput.echoMode = isHidden ? TextInput.Normal : TextInput.Password; toggleIsHidden();
isHidden = !isHidden;
} }
onEntered: { onEntered: {
parent.opacity = 0.9 parent.opacity = 0.9
@ -195,28 +252,129 @@ Item {
Keys.enabled: root.visible Keys.enabled: root.visible
Keys.onReturnPressed: { Keys.onReturnPressed: {
root.close() root.close()
root.accepted() if (passwordDialogMode) {
root.accepted()
} else if (newPasswordDialogMode) {
root.acceptedNewPassword()
} else if (passphraseDialogMode) {
root.acceptedPassphrase()
}
} }
Keys.onEscapePressed: { Keys.onEscapePressed: {
root.close() root.close()
root.rejected() if (passwordDialogMode) {
} root.rejected()
Keys.onPressed: { } else if (newPasswordDialogMode) {
if(event.key === Qt.Key_Shift){ root.rejectedNewPassword()
shiftIsPressed = true; } else if (passphraseDialogMode) {
} root.rejectedPassphrase()
if(event.key === Qt.Key_Backspace){
backspaceIsPressed = true;
} }
} }
Keys.onReleased: { }
if(event.key === Qt.Key_Shift){
shiftIsPressed = false; // padding
} Rectangle {
if(event.key === Qt.Key_Backspace){ visible: !passwordDialogMode
backspaceIsPressed =false; Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
height: 10
opacity: 0
color: "black"
}
Label {
visible: !passwordDialogMode
text: qsTr("Please confirm new password") + translationManager.emptyString
Layout.fillWidth: true
font.pixelSize: 16
font.family: MoneroComponents.Style.fontLight.name
color: MoneroComponents.Style.defaultFontColor
}
TextField {
id: passwordInput2
visible: !passwordDialogMode
Layout.topMargin: 6
Layout.fillWidth: true
horizontalAlignment: TextInput.AlignLeft
verticalAlignment: TextInput.AlignVCenter
font.family: MoneroComponents.Style.fontLight.name
font.pixelSize: 24
echoMode: TextInput.Password
KeyNavigation.tab: okButton
bottomPadding: 10
leftPadding: 10
topPadding: 10
color: MoneroComponents.Style.defaultFontColor
selectionColor: MoneroComponents.Style.textSelectionColor
selectedTextColor: MoneroComponents.Style.textSelectedColor
onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock();
background: Rectangle {
radius: 2
border.color: MoneroComponents.Style.inputBorderColorInActive
border.width: 1
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
MoneroComponents.Label {
fontSize: 20
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
opacity: 0.7
fontFamily: FontAwesome.fontFamily
anchors.right: parent.right
anchors.rightMargin: 15
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
toggleIsHidden()
}
onEntered: {
parent.opacity = 0.9
parent.fontSize = 24
}
onExited: {
parent.opacity = 0.7
parent.fontSize = 20
}
}
} }
} }
Keys.onReturnPressed: {
if (passwordInput1.text === passwordInput2.text) {
root.close()
if (newPasswordDialogMode) {
root.acceptedNewPassword()
} else if (passphraseDialogMode) {
root.acceptedPassphrase()
}
}
}
Keys.onEscapePressed: {
root.close()
if (newPasswordDialogMode) {
root.rejectedNewPassword()
} else if (passphraseDialogMode) {
root.rejectedPassphrase()
}
}
}
// padding
Rectangle {
visible: !passwordDialogMode
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
height: 10
opacity: 0
color: "black"
} }
// Ok/Cancel buttons // Ok/Cancel buttons
@ -230,10 +388,16 @@ Item {
id: cancelButton id: cancelButton
small: true small: true
text: root.walletName.length > 0 ? qsTr("Change wallet") + translationManager.emptyString : qsTr("Cancel") + translationManager.emptyString text: root.walletName.length > 0 ? qsTr("Change wallet") + translationManager.emptyString : qsTr("Cancel") + translationManager.emptyString
KeyNavigation.tab: passwordInput KeyNavigation.tab: passwordInput1
onClicked: { onClicked: {
root.close() root.close()
root.rejected() if (passwordDialogMode) {
root.rejected()
} else if (newPasswordDialogMode) {
root.rejectedNewPassword()
} else if (passphraseDialogMode) {
root.rejectedPassphrase()
}
} }
} }
@ -242,13 +406,19 @@ Item {
small: true small: true
text: qsTr("Continue") + translationManager.emptyString text: qsTr("Continue") + translationManager.emptyString
KeyNavigation.tab: cancelButton KeyNavigation.tab: cancelButton
enabled: (passwordDialogMode == true) ? true : passwordInput1.text === passwordInput2.text
onClicked: { onClicked: {
root.close() root.close()
root.accepted() if (passwordDialogMode) {
root.accepted()
} else if (newPasswordDialogMode) {
root.acceptedNewPassword()
} else if (passphraseDialogMode) {
root.acceptedPassphrase()
}
} }
} }
} }
} }
} }
} }

View file

@ -112,25 +112,6 @@ function roundDownToNearestThousand(_num){
return Math.floor(_num/1000.0)*1000 return Math.floor(_num/1000.0)*1000
} }
function isAlpha(letter){ return letter.match(/^[A-Za-z0-9]+$/) !== null; }
function isLowerCaseChar(letter){ return letter === letter.toLowerCase(); }
function isUpperLock(shift, letter){
if(!isAlpha((letter))) return false;
if(shift) {
if(isLowerCaseChar(letter))
return true;
else
return false;
} else {
if(isLowerCaseChar(letter))
return false;
else
return true;
}
}
function qmlEach(item, properties, ignoredObjectNames, arr){ function qmlEach(item, properties, ignoredObjectNames, arr){
// Traverse QML object tree and return components that match // Traverse QML object tree and return components that match
// via property names. Similar to jQuery("myclass").each(... // via property names. Similar to jQuery("myclass").each(...

View file

@ -573,15 +573,15 @@ ApplicationWindow {
hideProcessingSplash(); hideProcessingSplash();
console.log(">>> wallet passphrase needed: ") console.log(">>> wallet passphrase needed: ")
passphraseDialog.onAcceptedCallback = function() { passwordDialog.onAcceptedPassphraseCallback = function() {
walletManager.onPassphraseEntered(passphraseDialog.passphrase); walletManager.onPassphraseEntered(passwordDialog.password);
this.onWalletOpening(); this.onWalletOpening();
} }
passphraseDialog.onRejectedCallback = function() { passwordDialog.onRejectedPassphraseCallback = function() {
walletManager.onPassphraseEntered("", true); walletManager.onPassphraseEntered("", true);
this.onWalletOpening(); this.onWalletOpening();
} }
passphraseDialog.open() passwordDialog.openPassphraseDialog()
} }
function onWalletUpdate() { function onWalletUpdate() {
@ -1498,23 +1498,6 @@ ApplicationWindow {
} }
} }
PassphraseDialog {
id: passphraseDialog
visible: false
z: parent.z + 1
anchors.fill: parent
property var onAcceptedCallback
property var onRejectedCallback
onAccepted: {
if (onAcceptedCallback)
onAcceptedCallback();
}
onRejected: {
if (onRejectedCallback)
onRejectedCallback();
}
}
PasswordDialog { PasswordDialog {
id: passwordDialog id: passwordDialog
visible: false visible: false
@ -1522,6 +1505,8 @@ ApplicationWindow {
anchors.fill: parent anchors.fill: parent
property var onAcceptedCallback property var onAcceptedCallback
property var onRejectedCallback property var onRejectedCallback
property var onAcceptedPassphraseCallback
property var onRejectedPassphraseCallback
onAccepted: { onAccepted: {
if (onAcceptedCallback) if (onAcceptedCallback)
onAcceptedCallback(); onAcceptedCallback();
@ -1530,16 +1515,9 @@ ApplicationWindow {
if (onRejectedCallback) if (onRejectedCallback)
onRejectedCallback(); onRejectedCallback();
} }
} onAcceptedNewPassword: {
if (currentWallet.setPassword(passwordDialog.password)) {
NewPasswordDialog { appWindow.walletPassword = passwordDialog.password;
id: newPasswordDialog
z: parent.z + 1
visible:false
anchors.fill: parent
onAccepted: {
if (currentWallet.setPassword(newPasswordDialog.password)) {
appWindow.walletPassword = newPasswordDialog.password;
informationPopup.title = qsTr("Information") + translationManager.emptyString; informationPopup.title = qsTr("Information") + translationManager.emptyString;
informationPopup.text = qsTr("Password changed successfully") + translationManager.emptyString; informationPopup.text = qsTr("Password changed successfully") + translationManager.emptyString;
informationPopup.icon = StandardIcon.Information; informationPopup.icon = StandardIcon.Information;
@ -1551,7 +1529,14 @@ ApplicationWindow {
informationPopup.onCloseCallback = null; informationPopup.onCloseCallback = null;
informationPopup.open(); informationPopup.open();
} }
onRejected: { onRejectedNewPassword: {}
onAcceptedPassphrase: {
if (onAcceptedPassphraseCallback)
onAcceptedPassphraseCallback();
}
onRejectedPassphrase: {
if (onRejectedPassphraseCallback)
onRejectedPassphraseCallback();
} }
} }

View file

@ -68,7 +68,8 @@ HEADERS += \
src/qt/mime.h \ src/qt/mime.h \
src/qt/KeysFiles.h \ src/qt/KeysFiles.h \
src/qt/utils.h \ src/qt/utils.h \
src/qt/prices.h src/qt/prices.h \
src/qt/macoshelper.h
SOURCES += main.cpp \ SOURCES += main.cpp \
filter.cpp \ filter.cpp \
@ -334,7 +335,8 @@ linux {
-llmdb \ -llmdb \
-lsodium \ -lsodium \
-lhidapi-libusb \ -lhidapi-libusb \
-lcrypto $$TREZOR_LINKER -lcrypto $$TREZOR_LINKER \
-lX11
if(!android) { if(!android) {
LIBS+= \ LIBS+= \
@ -359,6 +361,8 @@ macx {
# message("using static libraries") # message("using static libraries")
# LIBS+= -Wl,-Bstatic # LIBS+= -Wl,-Bstatic
# } # }
QT += macextras
OBJECTIVE_SOURCES += src/qt/macoshelper.mm
LIBS+= \ LIBS+= \
-L/usr/local/lib \ -L/usr/local/lib \
-L/usr/local/opt/openssl/lib \ -L/usr/local/opt/openssl/lib \
@ -372,6 +376,7 @@ macx {
-lboost_chrono \ -lboost_chrono \
-lboost_program_options \ -lboost_program_options \
-framework CoreFoundation \ -framework CoreFoundation \
-framework AppKit \
-lhidapi \ -lhidapi \
-lssl \ -lssl \
-lsodium \ -lsodium \

View file

@ -31,6 +31,20 @@
#include <QDir> #include <QDir>
#include <QDebug> #include <QDebug>
#include <QString> #include <QString>
#ifdef Q_OS_MAC
#include "qt/macoshelper.h"
#endif
#ifdef Q_OS_WIN32
#include <windows.h>
#endif
#ifdef Q_OS_LINUX
#include <X11/XKBlib.h>
#undef KeyPress
#undef KeyRelease
#undef FocusIn
#undef FocusOut
// #undef those Xlib #defines that conflict with QEvent::Type enum
#endif
OSHelper::OSHelper(QObject *parent) : QObject(parent) OSHelper::OSHelper(QObject *parent) : QObject(parent)
{ {
@ -58,6 +72,27 @@ bool OSHelper::removeTemporaryWallet(const QString &fileName) const
return cache_deleted && address_deleted && keys_deleted; return cache_deleted && address_deleted && keys_deleted;
} }
// https://stackoverflow.com/a/3006934
bool OSHelper::isCapsLock() const
{
// platform dependent method of determining if CAPS LOCK is on
#if defined(Q_OS_WIN32) // MS Windows version
return GetKeyState(VK_CAPITAL) == 1;
#elif defined(Q_OS_LINUX) // X11 version
Display * d = XOpenDisplay((char*)0);
bool caps_state = false;
if (d) {
unsigned n;
XkbGetIndicatorState(d, XkbUseCoreKbd, &n);
caps_state = (n & 0x01) == 1;
}
return caps_state;
#elif defined(Q_OS_MAC)
return MacOSHelper::isCapsLock();
#endif
return false;
}
QString OSHelper::temporaryPath() const QString OSHelper::temporaryPath() const
{ {
return QDir::tempPath(); return QDir::tempPath();

View file

@ -42,6 +42,7 @@ public:
Q_INVOKABLE QString temporaryFilename() const; Q_INVOKABLE QString temporaryFilename() const;
Q_INVOKABLE QString temporaryPath() const; Q_INVOKABLE QString temporaryPath() const;
Q_INVOKABLE bool removeTemporaryWallet(const QString &walletName) const; Q_INVOKABLE bool removeTemporaryWallet(const QString &walletName) const;
Q_INVOKABLE bool isCapsLock() const;
signals: signals:

View file

@ -339,7 +339,7 @@ Rectangle {
onClicked: { onClicked: {
passwordDialog.onAcceptedCallback = function() { passwordDialog.onAcceptedCallback = function() {
if(appWindow.walletPassword === passwordDialog.password){ if(appWindow.walletPassword === passwordDialog.password){
newPasswordDialog.open() passwordDialog.openNewPasswordDialog()
} else { } else {
informationPopup.title = qsTr("Error") + translationManager.emptyString; informationPopup.title = qsTr("Error") + translationManager.emptyString;
informationPopup.text = qsTr("Wrong password") + translationManager.emptyString; informationPopup.text = qsTr("Wrong password") + translationManager.emptyString;

View file

@ -96,9 +96,7 @@
<file>pages/SharedRingDB.qml</file> <file>pages/SharedRingDB.qml</file>
<file>components/effects/ImageMask.qml</file> <file>components/effects/ImageMask.qml</file>
<file>components/IconButton.qml</file> <file>components/IconButton.qml</file>
<file>components/PassphraseDialog.qml</file>
<file>components/PasswordDialog.qml</file> <file>components/PasswordDialog.qml</file>
<file>components/NewPasswordDialog.qml</file>
<file>components/InputDialog.qml</file> <file>components/InputDialog.qml</file>
<file>components/ProcessingSplash.qml</file> <file>components/ProcessingSplash.qml</file>
<file>components/ProgressBar.qml</file> <file>components/ProgressBar.qml</file>

40
src/qt/macoshelper.h Normal file
View file

@ -0,0 +1,40 @@
// Copyright (c) 2014-2019, 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.
#ifndef MACOSHELPER_H
#define MACOSHELPER_H
class MacOSHelper
{
MacOSHelper() {}
public:
static bool isCapsLock();
};
#endif //MACOSHELPER_H

49
src/qt/macoshelper.mm Normal file
View file

@ -0,0 +1,49 @@
// Copyright (c) 2014-2019, 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.
#include <QtCore>
#include <QtGui>
#include <QtMac>
#include "macoshelper.h"
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>
#include <Availability.h>
bool MacOSHelper::isCapsLock()
{
#ifdef __MAC_10_12
NSUInteger flags = [NSEvent modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
return (flags == NSEventModifierFlagCapsLock);
#else
NSUInteger flags = [NSEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
return (flags & NSAlphaShiftKeyMask);
#endif
}

View file

@ -498,15 +498,15 @@ Rectangle {
splash.close() splash.close()
console.log(">>> wallet passphrase needed: "); console.log(">>> wallet passphrase needed: ");
passphraseDialog.onAcceptedCallback = function() { passwordDialog.onAcceptedPassphraseCallback = function() {
walletManager.onPassphraseEntered(passphraseDialog.passphrase); walletManager.onPassphraseEntered(passwordDialog.password);
creatingWalletDeviceSplash(); creatingWalletDeviceSplash();
} }
passphraseDialog.onRejectedCallback = function() { passwordDialog.onRejectedPassphraseCallback = function() {
walletManager.onPassphraseEntered("", true); walletManager.onPassphraseEntered("", true);
creatingWalletDeviceSplash(); creatingWalletDeviceSplash();
} }
passphraseDialog.open() passwordDialog.openPassphraseDialog()
} }
function onDeviceButtonRequest(code){ function onDeviceButtonRequest(code){