diff --git a/components/DaemonConsole.qml b/components/DaemonConsole.qml index 2643a339..225b2dba 100644 --- a/components/DaemonConsole.qml +++ b/components/DaemonConsole.qml @@ -27,22 +27,23 @@ // 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 2.0 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.1 import QtQuick.Controls.Styles 1.4 -import QtQuick.Window 2.0 +import QtQuick.Window 2.2 import "../components" as MoneroComponents +import "../js/Windows.js" as Windows +import "../js/Utils.js" as Utils Window { id: root modality: Qt.ApplicationModal - flags: Qt.Window | Qt.FramelessWindowHint - property alias title: dialogTitle.text + color: "black" + flags: Windows.flags property alias text: dialogContent.text property alias content: root.text - property alias okVisible: okButton.visible property alias textArea: dialogContent property var icon @@ -50,15 +51,25 @@ Window { signal accepted() signal rejected() + onClosing: { + inactiveOverlay.visible = false; + } function open() { - show() + inactiveOverlay.visible = true; + show(); } // TODO: implement without hardcoding sizes width: 480 height: 280 + // background gradient + Image { + anchors.fill: parent + source: "../images/middlePanelBg.jpg" + } + // Make window draggable MouseArea { anchors.fill: parent @@ -70,79 +81,136 @@ Window { ColumnLayout { id: mainLayout - spacing: 10 - anchors { fill: parent; margins: 35 } + + anchors.fill: parent + anchors.topMargin: 20 * scaleRatio + anchors.margins: 35 * scaleRatio + spacing: 20 * scaleRatio RowLayout { - id: column - //anchors {fill: parent; margins: 16 } - Layout.alignment: Qt.AlignHCenter + id: content + Layout.fillWidth: true + Layout.fillHeight: true - Label { - id: dialogTitle - horizontalAlignment: Text.AlignHCenter - font.pixelSize: 32 - font.family: "Arial" - color: "#555555" - } + Flickable { + id: flickable + anchors.fill: parent - } + TextArea.flickable: TextArea { + id : dialogContent + textFormat: TextEdit.RichText + selectByMouse: true + selectByKeyboard: true + anchors.fill: parent + font.family: "Ariel" + font.pixelSize: 14 * scaleRatio + color: MoneroComponents.Style.defaultFontColor + selectionColor: MoneroComponents.Style.dimmedFontColor + 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"); + textArea.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"; + } - RowLayout { - TextArea { - id : dialogContent - Layout.fillWidth: true - Layout.fillHeight: true - font.family: "Arial" - textFormat: TextEdit.AutoText - readOnly: true - font.pixelSize: 12 - } - } + // format multi-lines + if(msg.split("\n").length >= 2){ + msg = msg.split("\n").join('
'); + } - // Ok/Cancel buttons - RowLayout { - id: buttons - spacing: 60 - Layout.alignment: Qt.AlignHCenter + 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 + }); - MoneroComponents.StandardButton { - id: okButton - width: 120 - fontSize: 14 - text: qsTr("Close") + translationManager.emptyString - onClicked: { - root.close() - root.accepted() + var _timestamp = log_color("[" + timestamp + "]", "#FFFFFF"); + var _msg = log_color(msg, color); + textArea.append(_timestamp + " " + _msg); + // scroll to bottom + if(flickable.contentHeight > content.height){ + flickable.contentY = flickable.contentHeight + 20; + } + } + } + + 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 } } + } + + RowLayout { + Layout.fillWidth: true MoneroComponents.LineEdit { id: sendCommandText - width: 300 + Layout.fillWidth: true placeholderText: qsTr("command + enter (e.g help)") + translationManager.emptyString onAccepted: { - if(text.length > 0) - daemonManager.sendCommand(text,currentWallet.nettype); + if(text.length > 0) { + textArea.logCommand(">>> " + text) + daemonManager.sendCommand(text, currentWallet.nettype); + } text = "" } } - - // Status button -// MoneroComponents.StandardButton { -// id: sendCommandButton -// enabled: sendCommandText.text.length > 0 -// fontSize: 14 -// text: qsTr("Send command") -// onClicked: { -// daemonManager.sendCommand(sendCommandText.text,currentWallet.testnet); -// } -// } } } -} - + // window borders + Rectangle { + anchors.bottom: parent.bottom + anchors.top: parent.top + anchors.left: parent.left + width:1 + color: "#2F2F2F" + z: 2 + } + Rectangle { + anchors.bottom: parent.bottom + anchors.top: parent.top + anchors.right: parent.right + width:1 + color: "#2F2F2F" + z: 2 + } + Rectangle { + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.left: parent.left + height:1 + color: "#2F2F2F" + z: 2 + } +} \ No newline at end of file diff --git a/components/NewPasswordDialog.qml b/components/NewPasswordDialog.qml index d83364de..d94b5407 100644 --- a/components/NewPasswordDialog.qml +++ b/components/NewPasswordDialog.qml @@ -38,13 +38,7 @@ import "../components" as MoneroComponents Item { id: root visible: false - Rectangle { - id: bg - z: parent.z + 1 - anchors.fill: parent - color: "black" - opacity: 0.8 - } + z: parent.z + 2 property alias password: passwordInput1.text diff --git a/components/PasswordDialog.qml b/components/PasswordDialog.qml index 1f75f461..1534e1f3 100644 --- a/components/PasswordDialog.qml +++ b/components/PasswordDialog.qml @@ -38,6 +38,7 @@ import "../components" as MoneroComponents Item { id: root visible: false + z: parent.z + 2 property alias password: passwordInput.text property string walletName @@ -48,6 +49,7 @@ Item { signal closeCallback() function open(walletName) { + inactiveOverlay.visible = true // draw appwindow inactive root.walletName = walletName ? walletName : "" leftPanel.enabled = false middlePanel.enabled = false @@ -59,6 +61,7 @@ Item { } function close() { + inactiveOverlay.visible = false leftPanel.enabled = true middlePanel.enabled = true titleBar.enabled = true @@ -166,11 +169,4 @@ Item { } } - Rectangle { - id: bg - - anchors.fill: parent - color: "black" - opacity: 0.8 - } } diff --git a/components/TitleBar.qml b/components/TitleBar.qml index 3fd9a8a0..166e2be9 100644 --- a/components/TitleBar.qml +++ b/components/TitleBar.qml @@ -33,39 +33,55 @@ import QtQuick.Layouts 1.1 Rectangle { id: titleBar + height: { + if(!customDecorations || isMobile){ + return 0; + } + + if(small) return 38 * scaleRatio; + else return 50 * scaleRatio; + } + y: -height + z: 1 + + property string title property int mouseX: 0 property bool containsMouse: false property alias basicButtonVisible: goToBasicVersionButton.visible - property bool customDecorations: true + property bool customDecorations: persistentSettings.customDecorations + property bool showWhatIsButton: true + property bool showMinimizeButton: false + property bool showMaximizeButton: false + property bool showCloseButton: true + property bool showMoneroLogo: false + property bool small: false + + signal closeClicked + signal maximizeClicked + signal minimizeClicked signal goToBasicVersion(bool yes) - height: customDecorations && !isMobile ? 50 : 0 - y: -height - property string title - property alias maximizeButtonVisible: maximizeButton.visible - z: 1 Item { - id: test + // Background gradient width: parent.width - height: 50 - z: 1 + height: s + z: parent.z + 1 - // use jpg for gradiency Image { - anchors.fill: parent - height: parent.height - width: parent.width + anchors.fill: titleBar + height: titleBar.height + width: titleBar.width source: "../images/titlebarGradient.jpg" } } - Item{ + Item { id: titlebarlogo width: 125 - height: 50 + height: parent.height anchors.centerIn: parent - visible: customDecorations - z: 1 + visible: customDecorations && showMoneroLogo + z: parent.z + 1 Image { anchors.left: parent.left @@ -77,6 +93,15 @@ Rectangle { } } + Label { + id: titleLabel + visible: !showMoneroLogo && customDecorations && titleBar.title !== '' + anchors.centerIn: parent + fontSize: 18 + text: titleBar.title + z: parent.z + 1 + } + // collapse left panel Rectangle { id: goToBasicVersionButton @@ -85,10 +110,10 @@ Rectangle { anchors.top: parent.top anchors.left: parent.left color: "transparent" - height: 50 * scaleRatio + height: titleBar.height width: height visible: isMobile - z: 2 + z: parent.z + 2 Image { width: 14 @@ -118,10 +143,11 @@ Rectangle { anchors.top: parent.top anchors.bottom: parent.bottom visible: parent.customDecorations - z: 2 + z: parent.z + 2 Rectangle { id: minimizeButton + visible: showMinimizeButton anchors.top: parent.top anchors.bottom: parent.bottom width: 42 @@ -139,14 +165,13 @@ Rectangle { cursorShape: Qt.PointingHandCursor onEntered: minimizeButton.color = "#262626"; onExited: minimizeButton.color = "transparent"; - onClicked: { - appWindow.visibility = Window.Minimized - } + onClicked: minimizeClicked(); } } Rectangle { id: maximizeButton + visible: showMaximizeButton anchors.top: parent.top anchors.bottom: parent.bottom width: 42 @@ -167,15 +192,13 @@ Rectangle { cursorShape: Qt.PointingHandCursor onEntered: maximizeButton.color = "#262626"; onExited: maximizeButton.color = "transparent"; - onClicked: { - appWindow.visibility = appWindow.visibility !== Window.FullScreen ? Window.FullScreen : - Window.Windowed - } + onClicked: maximizeClicked(); } } Rectangle { id: closeButton + visible: showCloseButton anchors.top: parent.top anchors.bottom: parent.bottom width: 42 @@ -190,7 +213,7 @@ Rectangle { MouseArea { anchors.fill: parent - onClicked: appWindow.close(); + onClicked: closeClicked(); hoverEnabled: true cursorShape: Qt.PointingHandCursor onEntered: closeButton.color = "#262626"; @@ -199,4 +222,23 @@ Rectangle { } } + // window borders + Rectangle { + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.left: parent.left + height: 1 + color: "#2F2F2F" + z: parent.z + 1 + } + + Rectangle { + anchors.top: parent.top + anchors.right: parent.right + anchors.left: parent.left + visible: titleBar.small + height: 1 + color: "#2F2F2F" + z: parent.z + 1 + } } diff --git a/js/Utils.js b/js/Utils.js new file mode 100644 index 00000000..7cf582b2 --- /dev/null +++ b/js/Utils.js @@ -0,0 +1,25 @@ +/** + * Formats a date. + * @param {date} date - toggle decorations + * @param {params} params - + */ +function formatDate( date, params ) { + var options = { + weekday: "short", + year: "numeric", + month: "long", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + timeZone: "UTC", + timeZoneName: "short", + }; + + options = [options, params].reduce(function (r, o) { + Object.keys(o).forEach(function (k) { r[k] = o[k]; }); + return r; + }, {}); + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString + return new Date( date ).toLocaleString( 'en-US', options ); +} diff --git a/js/Windows.js b/js/Windows.js new file mode 100644 index 00000000..be2f503e --- /dev/null +++ b/js/Windows.js @@ -0,0 +1,34 @@ +var flagsCustomDecorations = (Qt.FramelessWindowHint | Qt.WindowSystemMenuHint | Qt.Window | Qt.WindowMinimizeButtonHint); +var flags = (Qt.WindowSystemMenuHint | Qt.Window | Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint | Qt.WindowMaximizeButtonHint); + +/** + * Toggles window decorations + * @param {bool} custom - toggle decorations + */ +function setCustomWindowDecorations(custom) { + // save x,y positions, because we need to hide/show the window + var x = appWindow.x + 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; + daemonConsolePopup.flags = flagsCustomDecorations; + } else { + appWindow.flags = flags; + daemonConsolePopup.flags = flags; + } + + // Reset window + appWindow.hide() + appWindow.x = x + appWindow.y = y + appWindow.show() +} diff --git a/main.qml b/main.qml index d3a56796..50621493 100644 --- a/main.qml +++ b/main.qml @@ -40,6 +40,7 @@ import moneroComponents.NetworkType 1.0 import "components" import "wizard" +import "js/Windows.js" as Windows ApplicationWindow { id: appWindow @@ -933,30 +934,9 @@ ApplicationWindow { // width: screenWidth //rightPanelExpanded ? 1269 : 1269 - 300 // height: 900 //300//maxWindowHeight; color: "#FFFFFF" - flags: persistentSettings.customDecorations ? (Qt.FramelessWindowHint | Qt.WindowSystemMenuHint | Qt.Window | Qt.WindowMinimizeButtonHint) : (Qt.WindowSystemMenuHint | Qt.Window | Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint | Qt.WindowMaximizeButtonHint) + flags: persistentSettings.customDecorations ? Windows.flagsCustomDecorations : Windows.flags onWidthChanged: x -= 0 - function setCustomWindowDecorations(custom) { - var x = appWindow.x - var y = appWindow.y - if (x < 0) - x = 0 - if (y < 0) - y = 0 - persistentSettings.customDecorations = custom; - titleBar.visible = custom; // hides custom titlebar based on customDecorations - - if (custom) - appWindow.flags = Qt.FramelessWindowHint | Qt.WindowSystemMenuHint | Qt.Window | Qt.WindowMinimizeButtonHint; - else - appWindow.flags = Qt.WindowSystemMenuHint | Qt.Window | Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint | Qt.WindowMaximizeButtonHint; - - appWindow.hide() - appWindow.x = x - appWindow.y = y - appWindow.show() - } - Component.onCompleted: { x = (Screen.width - width) / 2 y = (Screen.height - maxWindowHeight) / 2 @@ -1281,7 +1261,7 @@ ApplicationWindow { PropertyChanges { target: appWindow; width: (screenWidth < 930 || isAndroid || isIOS)? screenWidth : 930; } PropertyChanges { target: appWindow; height: maxWindowHeight; } PropertyChanges { target: resizeArea; visible: true } - PropertyChanges { target: titleBar; maximizeButtonVisible: false } + PropertyChanges { target: titleBar; showMaximizeButton: false } // PropertyChanges { target: frameArea; blocked: true } PropertyChanges { target: titleBar; visible: false } PropertyChanges { target: titleBar; y: 0 } @@ -1297,7 +1277,7 @@ ApplicationWindow { PropertyChanges { target: appWindow; width: (screenWidth < 969 || isAndroid || isIOS)? screenWidth : 969 } //rightPanelExpanded ? 1269 : 1269 - 300; PropertyChanges { target: appWindow; height: maxWindowHeight; } PropertyChanges { target: resizeArea; visible: true } - PropertyChanges { target: titleBar; maximizeButtonVisible: true } + PropertyChanges { target: titleBar; showMaximizeButton: true } // PropertyChanges { target: frameArea; blocked: true } PropertyChanges { target: titleBar; visible: true } // PropertyChanges { target: titleBar; y: 0 } @@ -1616,11 +1596,20 @@ ApplicationWindow { TitleBar { id: titleBar - anchors.left: parent.left - anchors.right: parent.right x: 0 y: 0 - customDecorations: persistentSettings.customDecorations + anchors.left: parent.left + anchors.right: parent.right + showMinimizeButton: true + showMaximizeButton: true + showWhatIsButton: false + showMoneroLogo: true + onCloseClicked: appWindow.close(); + onMaximizeClicked: { + appWindow.visibility = appWindow.visibility !== Window.FullScreen ? Window.FullScreen : + Window.Windowed + } + onMinimizeClicked: appWindow.visibility = Window.Minimized onGoToBasicVersion: { if (yes) { // basicPanel.currentView = middlePanel.currentView @@ -1649,15 +1638,6 @@ ApplicationWindow { } } } - - Rectangle { - anchors.bottom: parent.bottom - anchors.right: parent.right - anchors.left: parent.left - height:1 - color: "#2F2F2F" - z: 2 - } } // new ToolTip @@ -1824,7 +1804,25 @@ ApplicationWindow { middlePanel.focus = true middlePanel.focus = false } + } + // Daemon console + DaemonConsole { + id: daemonConsolePopup + height:500 + width:800 + title: qsTr("Daemon log") + translationManager.emptyString + onAccepted: { + close(); + } + } + // background gradient + Rectangle { + id: inactiveOverlay + visible: false + anchors.fill: parent + color: "black" + opacity: 0.8 } } diff --git a/pages/Settings.qml b/pages/Settings.qml index bc9c689c..0b27c898 100644 --- a/pages/Settings.qml +++ b/pages/Settings.qml @@ -32,6 +32,8 @@ 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" @@ -501,7 +503,7 @@ Rectangle { visible: !isMobile id: customDecorationsCheckBox checked: persistentSettings.customDecorations - onClicked: appWindow.setCustomWindowDecorations(checked) + onClicked: Windows.setCustomWindowDecorations(checked) text: qsTr("Custom decorations") + translationManager.emptyString } } @@ -719,17 +721,6 @@ Rectangle { } } - // Daemon console - DaemonConsole { - id: daemonConsolePopup - height:500 - width:800 - title: qsTr("Daemon log") + translationManager.emptyString - onAccepted: { - close(); - } - } - // Choose blockchain folder FileDialog { id: blockchainFileDialog @@ -794,7 +785,7 @@ Rectangle { function onDaemonConsoleUpdated(message){ // Update daemon console - daemonConsolePopup.textArea.append(message) + daemonConsolePopup.textArea.logMessage(message) } diff --git a/qml.qrc b/qml.qrc index 42b868e6..4e605d89 100644 --- a/qml.qrc +++ b/qml.qrc @@ -206,5 +206,7 @@ images/warning.png images/checkedBlackIcon.png images/rightArrowInactive.png + js/Windows.js + js/Utils.js