From a7f62c13b09bf7effa044a79b3b6056685ea5dec Mon Sep 17 00:00:00 2001 From: Black Hat Date: Sat, 17 Nov 2018 20:52:17 +0800 Subject: [PATCH] Working dark mode && more drawer specific changes. --- assets/img/roompanel-dark.svg | 219 ++++++++++++++++ imports/Spectral/Component/MaterialIcon.qml | 3 +- imports/Spectral/Component/SideNavButton.qml | 25 -- imports/Spectral/Component/SplitView.qml | 3 +- .../Component/Timeline/MessageDelegate.qml | 66 ----- .../Component/Timeline/SectionDelegate.qml | 1 - imports/Spectral/Effect/CircleMask.qml | 29 +++ imports/Spectral/Effect/RippleEffect.qml | 239 ++++++++++++++++++ imports/Spectral/Effect/qmldir | 1 + imports/Spectral/Page/Login.qml | 27 -- imports/Spectral/Page/LoginForm.ui.qml | 143 ----------- imports/Spectral/Panel/RoomHeader.qml | 83 +++--- imports/Spectral/Panel/RoomListDelegate.qml | 96 ------- imports/Spectral/Panel/RoomListPanel.qml | 203 +++++++++++++-- imports/Spectral/Panel/RoomPanelForm.ui.qml | 2 +- qml/main.qml | 110 +++++++- res.qrc | 7 +- src/controller.cpp | 24 +- 18 files changed, 829 insertions(+), 452 deletions(-) create mode 100644 assets/img/roompanel-dark.svg delete mode 100644 imports/Spectral/Component/SideNavButton.qml create mode 100644 imports/Spectral/Effect/CircleMask.qml create mode 100644 imports/Spectral/Effect/RippleEffect.qml delete mode 100644 imports/Spectral/Page/Login.qml delete mode 100644 imports/Spectral/Page/LoginForm.ui.qml delete mode 100644 imports/Spectral/Panel/RoomListDelegate.qml diff --git a/assets/img/roompanel-dark.svg b/assets/img/roompanel-dark.svg new file mode 100644 index 000000000..d240920ec --- /dev/null +++ b/assets/img/roompanel-dark.svg @@ -0,0 +1,219 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/imports/Spectral/Component/MaterialIcon.qml b/imports/Spectral/Component/MaterialIcon.qml index 5f9499445..d3fa21960 100644 --- a/imports/Spectral/Component/MaterialIcon.qml +++ b/imports/Spectral/Component/MaterialIcon.qml @@ -1,6 +1,7 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 +import QtQuick.Controls.Material 2.2 import Spectral.Setting 0.1 import Spectral.Font 0.1 @@ -10,7 +11,7 @@ Text { id: materialLabel - color: MSettings.darkTheme ? "white" : "dark" + color: Material.foreground font.pointSize: 16 font.family: MaterialFont.name horizontalAlignment: Text.AlignHCenter diff --git a/imports/Spectral/Component/SideNavButton.qml b/imports/Spectral/Component/SideNavButton.qml deleted file mode 100644 index d2fb7e3a3..000000000 --- a/imports/Spectral/Component/SideNavButton.qml +++ /dev/null @@ -1,25 +0,0 @@ -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.3 -import QtQuick.Controls.Material 2.2 - -import "qrc:/js/util.js" as Util - -ItemDelegate { - property var page - property bool selected: stackView.currentItem === page - property color highlightColor: Material.accent - - Rectangle { - width: selected ? 4 : 0 - height: parent.height - - color: highlightColor - - Behavior on width { - PropertyAnimation { easing.type: Easing.InOutCubic; duration: 200 } - } - } - - onClicked: Util.pushToStack(stackView, page) -} diff --git a/imports/Spectral/Component/SplitView.qml b/imports/Spectral/Component/SplitView.qml index dd43d61e9..4f176fa35 100644 --- a/imports/Spectral/Component/SplitView.qml +++ b/imports/Spectral/Component/SplitView.qml @@ -41,6 +41,7 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtQuick.Window 2.1 +import Spectral.Setting 0.1 Item { id: root @@ -64,7 +65,7 @@ Item { property Component handleDelegate: Rectangle { width: 1 height: 1 - color: "#E1E1E1" + color: MSettings.darkTheme ? "#424242" : "#E1E1E1" } /*! diff --git a/imports/Spectral/Component/Timeline/MessageDelegate.qml b/imports/Spectral/Component/Timeline/MessageDelegate.qml index f8f35d8db..e9494a5c4 100644 --- a/imports/Spectral/Component/Timeline/MessageDelegate.qml +++ b/imports/Spectral/Component/Timeline/MessageDelegate.qml @@ -74,24 +74,6 @@ RowLayout { spacing: 0 -// TimelineLabel { -// Layout.fillWidth: true - -// id: authorLabel - -// visible: messageRow.avatarVisible -// text: author.displayName -// Material.foreground: Material.accent -// coloredBackground: highlighted -// font.bold: true - -// MouseArea { -// anchors.fill: parent -// cursorShape: Qt.PointingHandCursor -// onClicked: roomPanelInput.insert(author.displayName) -// } -// } - TextEdit { Layout.fillWidth: true @@ -134,54 +116,6 @@ RowLayout { active: eventType === "image" || eventType === "file" || eventType === "audio" } - -// Row { -// Layout.alignment: Qt.AlignRight - -// spacing: 4 - -// TimelineLabel { -// visible: userMarker.length > 5 -// text: userMarker.length - 5 + "+" -// coloredBackground: highlighted -// Material.foreground: "grey" -// font.pointSize: 8 -// } - -// Repeater { -// model: userMarker.length > 5 ? userMarker.slice(0, 5) : userMarker - -// ImageItem { -// width: parent.height -// height: parent.height - -// hint: modelData.displayName -// source: modelData.paintable - -// MouseArea { -// anchors.fill: parent - -// cursorShape: Qt.PointingHandCursor - -// onClicked: { -// readMarkerDialog.listModel = userMarker -// readMarkerDialog.open() -// } -// } -// } -// } - -// TimelineLabel { -// id: timeLabel - -// visible: Math.abs(time - aboveTime) > 600000 || index == 0 -// text: Qt.formatTime(time, "hh:mm") -// coloredBackground: highlighted -// Material.foreground: "grey" -// font.pointSize: 8 -// } -// } - } Component { diff --git a/imports/Spectral/Component/Timeline/SectionDelegate.qml b/imports/Spectral/Component/Timeline/SectionDelegate.qml index 2fe7dd43a..59d43077f 100644 --- a/imports/Spectral/Component/Timeline/SectionDelegate.qml +++ b/imports/Spectral/Component/Timeline/SectionDelegate.qml @@ -3,7 +3,6 @@ import QtQuick.Controls 2.2 Label { text: section + " • " + Qt.formatTime(time, "hh:mm") - color: "#1D333E" font.pointSize: 9.75 font.weight: Font.Medium font.capitalization: Font.AllUppercase diff --git a/imports/Spectral/Effect/CircleMask.qml b/imports/Spectral/Effect/CircleMask.qml new file mode 100644 index 000000000..fc59f408b --- /dev/null +++ b/imports/Spectral/Effect/CircleMask.qml @@ -0,0 +1,29 @@ +import QtQuick 2.9 +import QtGraphicalEffects 1.0 + +Item { + id: item + + property alias source: mask.source + + Rectangle { + id: circleMask + + width: parent.width + height: parent.height + + smooth: true + visible: false + + radius: Math.max(width/2, height/2) + } + + OpacityMask { + id: mask + + width: parent.width + height: parent.height + + maskSource: circleMask + } +} diff --git a/imports/Spectral/Effect/RippleEffect.qml b/imports/Spectral/Effect/RippleEffect.qml new file mode 100644 index 000000000..970a39ce6 --- /dev/null +++ b/imports/Spectral/Effect/RippleEffect.qml @@ -0,0 +1,239 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 + +import Spectral.Component 2.0 +import Spectral.Setting 0.1 + +AutoMouseArea { + id: ripple + + property color color: MSettings.darkTheme ? Qt.rgba(255, 255, 255, 0.16) : Qt.rgba(0, 0, 0, 0.08) + property bool circular: false + property bool centered: false + property bool focused + property color focusColor: "transparent" + property int focusWidth: width - 32 + property Item control + + clip: true + + Connections { + target: control + + onPressedChanged: { + if (!control.pressed) + __private.removeLastCircle() + } + } + + onPressed: { + __private.createTapCircle(mouse.x, mouse.y) + + if (control) + mouse.accepted = false + } + + onReleased: __private.removeLastCircle() + onCanceled: __private.removeLastCircle() + + QtObject { + id: __private + + property int startRadius: circular ? width/10 : width/6 + property int endRadius + property bool showFocus: true + + property Item lastCircle + + function createTapCircle(x, y) { + endRadius = centered ? width/2 : radius(x, y) + 5 + showFocus = false + + lastCircle = tapCircle.createObject(ripple, { + "circleX": centered ? width/2 : x, + "circleY": centered ? height/2 : y + }) + } + + function removeLastCircle() { + if (lastCircle) + lastCircle.removeCircle() + } + + function radius(x, y) { + var dist1 = Math.max(dist(x, y, 0, 0), dist(x, y, width, height)) + var dist2 = Math.max(dist(x, y, width, 0), dist(x, y, 0, height)) + + return Math.max(dist1, dist2) + } + + function dist(x1, y1, x2, y2) { + var distX = x2 - x1 + var distY = y2 - y1 + + return Math.sqrt(distX * distX + distY * distY) + } + } + + Rectangle { + id: focusBackground + objectName: "focusBackground" + + width: parent.width + height: parent.height + + color: Qt.rgba(0,0,0,0.2) + + opacity: __private.showFocus && focused ? 1 : 0 + + Behavior on opacity { + NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } + } + } + + Rectangle { + id: focusCircle + objectName: "focusRipple" + + property bool focusedState + + x: (parent.width - width)/2 + y: (parent.height - height)/2 + + width: focused + ? focusedState ? focusWidth + : Math.min(parent.width - 8, focusWidth + 12) + : parent.width/5 + height: width + + radius: width/2 + + opacity: __private.showFocus && focused ? 1 : 0 + + color: focusColor.a === 0 ? Qt.rgba(1,1,1,0.4) : focusColor + + Behavior on opacity { + NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } + } + + Behavior on width { + NumberAnimation { duration: focusTimer.interval; } + } + + Timer { + id: focusTimer + running: focused + repeat: true + interval: 800 + + onTriggered: focusCircle.focusedState = !focusCircle.focusedState + } + } + + Component { + id: tapCircle + + Item { + id: circleItem + objectName: "tapRipple" + + property bool done + + property real circleX + property real circleY + + property bool closed + + width: parent.width + height: parent.height + + function removeCircle() { + done = true + + if (fillSizeAnimation.running) { + fillOpacityAnimation.stop() + closeAnimation.start() + + circleItem.destroy(500); + } else { + __private.showFocus = true + fadeAnimation.start(); + + circleItem.destroy(300); + } + } + + Item { + id: circleParent + + width: parent.width + height: parent.height + + visible: !circular + + Rectangle { + id: circleRectangle + + x: circleItem.circleX - radius + y: circleItem.circleY - radius + + width: radius * 2 + height: radius * 2 + + opacity: 0 + color: ripple.color + + NumberAnimation { + id: fillSizeAnimation + running: true + + target: circleRectangle; property: "radius"; duration: 500; + from: __private.startRadius; to: __private.endRadius; + easing.type: Easing.InOutQuad + + onStopped: { + if (done) + __private.showFocus = true + } + } + + NumberAnimation { + id: fillOpacityAnimation + running: true + + target: circleRectangle; property: "opacity"; duration: 300; + from: 0; to: 1; easing.type: Easing.InOutQuad + } + + NumberAnimation { + id: fadeAnimation + + target: circleRectangle; property: "opacity"; duration: 300; + from: 1; to: 0; easing.type: Easing.InOutQuad + } + + SequentialAnimation { + id: closeAnimation + + NumberAnimation { + target: circleRectangle; property: "opacity"; duration: 250; + to: 1; easing.type: Easing.InOutQuad + } + + NumberAnimation { + target: circleRectangle; property: "opacity"; duration: 250; + from: 1; to: 0; easing.type: Easing.InOutQuad + } + } + } + } + + CircleMask { + anchors.fill: parent + source: circleParent + visible: circular + } + } + } +} diff --git a/imports/Spectral/Effect/qmldir b/imports/Spectral/Effect/qmldir index b4c470ea4..89f10e489 100644 --- a/imports/Spectral/Effect/qmldir +++ b/imports/Spectral/Effect/qmldir @@ -1,2 +1,3 @@ module Spectral.Effect ElevationEffect 2.0 ElevationEffect.qml +RippleEffect 2.0 RippleEffect.qml diff --git a/imports/Spectral/Page/Login.qml b/imports/Spectral/Page/Login.qml deleted file mode 100644 index fbf5a069d..000000000 --- a/imports/Spectral/Page/Login.qml +++ /dev/null @@ -1,27 +0,0 @@ -import QtQuick 2.9 - -LoginForm { - loginButton.onClicked: doLogin() - - Shortcut { - sequence: "Return" - onActivated: doLogin() - } - - function doLogin() { - if (!(serverField.text.startsWith("http") && serverField.text.includes("://"))) { - loginButtonTooltip.text = "Server address should start with http(s)://" - loginButtonTooltip.open() - return - } - - loginButton.text = "Logging in..." - loginButton.enabled = false - controller.loginWithCredentials(serverField.text, usernameField.text, passwordField.text) - - controller.connectionAdded.connect(function(conn) { - stackView.pop() - accountListView.currentConnection = conn - }) - } -} diff --git a/imports/Spectral/Page/LoginForm.ui.qml b/imports/Spectral/Page/LoginForm.ui.qml deleted file mode 100644 index dead32099..000000000 --- a/imports/Spectral/Page/LoginForm.ui.qml +++ /dev/null @@ -1,143 +0,0 @@ -import QtQuick 2.9 -import QtQuick.Layouts 1.3 -import QtGraphicalEffects 1.0 -import QtQuick.Controls 2.2 -import QtQuick.Controls.Material 2.2 - -import Spectral.Component 2.0 - -import Spectral.Setting 0.1 - -Page { - property var controller - - property alias loginButton: loginButton - property alias serverField: serverField - property alias usernameField: usernameField - property alias passwordField: passwordField - property alias loginButtonTooltip: loginButtonTooltip - - Row { - anchors.fill: parent - - Rectangle { - width: parent.width / 2 - height: parent.height - - color: Material.accent - - Column { - x: 32 - anchors.verticalCenter: parent.verticalCenter - - Label { - text: "Matrix Login." - font.pointSize: 28 - font.bold: true - font.capitalization: Font.AllUppercase - color: "white" - } - - Label { - text: "A new method of messaging." - font.pointSize: 12 - font.capitalization: Font.AllUppercase - color: "white" - } - } - } - - Pane { - width: parent.width / 2 - height: parent.height - - padding: 64 - - ColumnLayout { - width: parent.width - - id: mainCol - - AutoTextField { - Layout.fillWidth: true - - id: serverField - - leftPadding: 16 - topPadding: 0 - bottomPadding: 0 - - text: "https://matrix.org" - placeholderText: "Server" - - background: Rectangle { - implicitHeight: 48 - - color: MSettings.darkTheme ? "#242424" : "#eaeaea" - border.color: parent.activeFocus ? Material.accent : "transparent" - border.width: 2 - } - } - - AutoTextField { - Layout.fillWidth: true - - id: usernameField - - leftPadding: 16 - topPadding: 0 - bottomPadding: 0 - - placeholderText: "Username" - - background: Rectangle { - implicitHeight: 48 - - color: MSettings.darkTheme ? "#242424" : "#eaeaea" - border.color: parent.activeFocus ? Material.accent : "transparent" - border.width: 2 - } - } - - AutoTextField { - Layout.fillWidth: true - - id: passwordField - - leftPadding: 16 - topPadding: 0 - bottomPadding: 0 - - placeholderText: "Password" - echoMode: TextInput.Password - - background: Rectangle { - implicitHeight: 48 - - color: MSettings.darkTheme ? "#242424" : "#eaeaea" - border.color: parent.activeFocus ? Material.accent : "transparent" - border.width: 2 - } - } - - Button { - Layout.fillWidth: true - - id: loginButton - - text: "LOGIN" - highlighted: true - - ToolTip { - id: loginButtonTooltip - } - } - } - } - } -} - -/*##^## Designer { - D{i:0;autoSize:true;height:480;width:640} -} - ##^##*/ diff --git a/imports/Spectral/Panel/RoomHeader.qml b/imports/Spectral/Panel/RoomHeader.qml index 3b8f1d7f6..619e80ebb 100644 --- a/imports/Spectral/Panel/RoomHeader.qml +++ b/imports/Spectral/Panel/RoomHeader.qml @@ -6,7 +6,7 @@ import QtQuick.Controls.Material 2.2 import Spectral 0.1 import Spectral.Effect 2.0 -Rectangle { +Control { property alias paintable: headerImage.source property alias topic: headerTopicLabel.text property bool atTop: false @@ -14,64 +14,65 @@ Rectangle { id: header - color: atTop ? "transparent" : "white" + background: Rectangle { + color: Material.background - layer.enabled: !atTop - layer.effect: ElevationEffect { - elevation: 4 + opacity: atTop ? 0 : 1 + + layer.enabled: true + layer.effect: ElevationEffect { + elevation: 2 + } } - ItemDelegate { + RowLayout { anchors.fill: parent + anchors.margins: 12 - id: roomHeader + spacing: 12 - onClicked: header.clicked() + ImageItem { + Layout.preferredWidth: height + Layout.fillHeight: true - RowLayout { - anchors.fill: parent - anchors.margins: 12 + id: headerImage - spacing: 12 + source: currentRoom.paintable + hint: currentRoom ? currentRoom.displayName : "No name" + } - ImageItem { - Layout.preferredWidth: height - Layout.fillHeight: true + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true - id: headerImage + visible: parent.width > 64 - source: currentRoom.paintable - hint: currentRoom ? currentRoom.displayName : "No name" - } - - ColumnLayout { + Label { Layout.fillWidth: true Layout.fillHeight: true - visible: parent.width > 64 + text: currentRoom ? currentRoom.displayName : "" + font.pointSize: 12 + elide: Text.ElideRight + wrapMode: Text.NoWrap + } - Label { - Layout.fillWidth: true - Layout.fillHeight: true + Label { + Layout.fillWidth: true + Layout.fillHeight: true - text: currentRoom ? currentRoom.displayName : "" - color: "#1D333E" - font.pointSize: 12 - elide: Text.ElideRight - wrapMode: Text.NoWrap - } + id: headerTopicLabel - Label { - Layout.fillWidth: true - Layout.fillHeight: true - - id: headerTopicLabel - - color: "#5B7480" - elide: Text.ElideRight - wrapMode: Text.NoWrap - } + color: "#5B7480" + elide: Text.ElideRight + wrapMode: Text.NoWrap } } } + + RippleEffect { + anchors.fill: parent + + onClicked: header.clicked() + } } diff --git a/imports/Spectral/Panel/RoomListDelegate.qml b/imports/Spectral/Panel/RoomListDelegate.qml deleted file mode 100644 index a342806fa..000000000 --- a/imports/Spectral/Panel/RoomListDelegate.qml +++ /dev/null @@ -1,96 +0,0 @@ -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.3 -import QtQuick.Controls.Material 2.2 - -import Spectral 0.1 -import Spectral.Setting 0.1 - -import Spectral.Component 2.0 - -Item { - AutoMouseArea { - anchors.fill: parent - - onSecondaryClicked: { - roomContextMenu.model = model - roomContextMenu.popup() - } - onPrimaryClicked: { - if (category === RoomType.Invited) { - inviteDialog.currentRoom = currentRoom - inviteDialog.open() - } else { - leaveRoom(enteredRoom) - enterRoom(currentRoom) - enteredRoom = currentRoom - } - } - } - - Rectangle { - anchors.fill: parent - - visible: highlightCount > 0 || currentRoom === enteredRoom - color: Material.accent - opacity: 0.1 - } - - Rectangle { - width: unreadCount > 0 ? 4 : 0 - height: parent.height - - color: Material.accent - - Behavior on width { - PropertyAnimation { easing.type: Easing.InOutCubic; duration: 200 } - } - } - - RowLayout { - anchors.fill: parent - anchors.margins: 12 - - spacing: 12 - - ImageItem { - id: imageItem - - Layout.preferredWidth: height - Layout.fillHeight: true - - source: paintable - hint: name || "No Name" - } - - ColumnLayout { - Layout.fillWidth: true - Layout.fillHeight: true - Layout.alignment: Qt.AlignHCenter - - visible: parent.width > 64 - - Label { - Layout.fillWidth: true - Layout.fillHeight: true - - text: name || "No Name" - color: "#1D333E" - font.pointSize: 12 - elide: Text.ElideRight - wrapMode: Text.NoWrap - } - - Label { - Layout.fillWidth: true - Layout.fillHeight: true - - text: (lastEvent == "" ? topic : lastEvent).replace(/(\r\n\t|\n|\r\t)/gm,"") - color: "#5B7480" - font.pointSize: 9.75 - elide: Text.ElideRight - wrapMode: Text.NoWrap - } - } - } -} diff --git a/imports/Spectral/Panel/RoomListPanel.qml b/imports/Spectral/Panel/RoomListPanel.qml index 1858cebe0..342c59c8e 100644 --- a/imports/Spectral/Panel/RoomListPanel.qml +++ b/imports/Spectral/Panel/RoomListPanel.qml @@ -17,7 +17,7 @@ Rectangle { property var controller: null readonly property var user: controller.connection ? controller.connection.localUser : null - readonly property int filter: 0 + property int filter: 0 property var enteredRoom: null property alias errorControl: errorControl @@ -140,7 +140,7 @@ Rectangle { background: Rectangle { color: Material.primary } - ItemDelegate { + RippleEffect { anchors.fill: parent } } @@ -172,11 +172,19 @@ Rectangle { } } + ItemDelegate { + Layout.fillWidth: true + + text: "Add Account" + + onClicked: loginDialog.open() + } + Rectangle { Layout.fillWidth: true Layout.preferredHeight: 1 - color: "#e7ebeb" + color: MSettings.darkTheme ? "#424242" : "#e7ebeb" } ItemDelegate { @@ -187,6 +195,14 @@ Rectangle { onClicked: stackView.push(settingsPage) } + ItemDelegate { + Layout.fillWidth: true + + text: "Logout" + + onClicked: controller.logout(controller.connection) + } + ItemDelegate { Layout.fillWidth: true @@ -249,14 +265,18 @@ Rectangle { spacing: 0 - Control { + Rectangle { Layout.fillWidth: true Layout.preferredHeight: 64 visible: stackView.depth > 1 - contentItem: RowLayout { + color: Material.primary + + RowLayout { anchors.fill: parent + anchors.margins: 4 + ToolButton { Layout.preferredWidth: height Layout.fillHeight: true @@ -277,10 +297,6 @@ Rectangle { elide: Label.ElideRight } } - - background: Rectangle { - color: Material.primary - } } StackView { @@ -289,6 +305,8 @@ Rectangle { id: stackView + clip: true + initialItem: mainPage } } @@ -299,8 +317,6 @@ Rectangle { spacing: 0 Control { - readonly property bool isSearching: searchField.text - Layout.fillWidth: true Layout.preferredHeight: 64 @@ -316,22 +332,66 @@ Rectangle { Layout.preferredWidth: height Layout.fillHeight: true - contentItem: MaterialIcon { - icon: roomListHeader.isSearching ? "\ue5cd" : "\ue8b6" - color: roomListHeader.isSearching ? "#1D333E" : "7F7F7F" - } + visible: !searchField.active - onClicked: { - if (searchField.focus) { - searchField.clear() - searchField.focus = false - } else { - searchField.focus = true + contentItem: MaterialIcon { + icon: { + switch (filter) { + case 0: return "\ue8b6" + case 1: return "\ue7f5" + case 2: return "\ue7ff" + case 3: return "\ue7fc" + } } } + + Menu { + id: filterMenu + + MenuItem { + text: "All" + + onClicked: filter = 0 + } + + MenuSeparator {} + + MenuItem { + text: "New" + + onClicked: filter = 1 + } + + MenuItem { + text: "People" + + onClicked: filter = 2 + } + + MenuItem { + text: "Group" + + onClicked: filter = 3 + } + } + + onClicked: filterMenu.popup() + } + + ItemDelegate { + Layout.preferredWidth: height + Layout.fillHeight: true + + visible: searchField.active + + contentItem: MaterialIcon { icon: "\ue5cd" } + + onClicked: searchField.clear() } AutoTextField { + readonly property bool active: text + Layout.fillWidth: true Layout.fillHeight: true @@ -349,7 +409,7 @@ Rectangle { Layout.fillHeight: true Layout.alignment: Qt.AlignRight - visible: !roomListHeader.isSearching + visible: !searchField.active source: root.user ? root.user.paintable : null hint: root.user ? root.user.displayName : "?" @@ -360,6 +420,17 @@ Rectangle { } } } + + background: Rectangle { + color: Material.background + + opacity: listView.atYBeginning ? 0 : 1 + + layer.enabled: true + layer.effect: ElevationEffect { + elevation: 2 + } + } } Control { @@ -423,9 +494,93 @@ Rectangle { ScrollBar.vertical: ScrollBar {} - delegate: RoomListDelegate { - width: parent.width + delegate: Item { + width: listView.width height: 64 + + Rectangle { + anchors.fill: parent + + visible: highlightCount > 0 || currentRoom === enteredRoom + color: Material.accent + opacity: 0.1 + } + + Rectangle { + width: unreadCount > 0 ? 4 : 0 + height: parent.height + + color: Material.accent + + Behavior on width { + PropertyAnimation { easing.type: Easing.InOutCubic; duration: 200 } + } + } + + RowLayout { + anchors.fill: parent + anchors.margins: 12 + + spacing: 12 + + ImageItem { + id: imageItem + + Layout.preferredWidth: height + Layout.fillHeight: true + + source: paintable + hint: name || "No Name" + } + + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter + + visible: parent.width > 64 + + Label { + Layout.fillWidth: true + Layout.fillHeight: true + + text: name || "No Name" + font.pointSize: 12 + elide: Text.ElideRight + wrapMode: Text.NoWrap + } + + Label { + Layout.fillWidth: true + Layout.fillHeight: true + + text: (lastEvent == "" ? topic : lastEvent).replace(/(\r\n\t|\n|\r\t)/gm,"") + color: "#5B7480" + font.pointSize: 9.75 + elide: Text.ElideRight + wrapMode: Text.NoWrap + } + } + } + + RippleEffect { + anchors.fill: parent + + onSecondaryClicked: { + roomContextMenu.model = model + roomContextMenu.popup() + } + onPrimaryClicked: { + if (category === RoomType.Invited) { + inviteDialog.currentRoom = currentRoom + inviteDialog.open() + } else { + leaveRoom(enteredRoom) + enterRoom(currentRoom) + enteredRoom = currentRoom + } + } + } } section.property: "display" diff --git a/imports/Spectral/Panel/RoomPanelForm.ui.qml b/imports/Spectral/Panel/RoomPanelForm.ui.qml index 5f153be66..d7236a4a4 100644 --- a/imports/Spectral/Panel/RoomPanelForm.ui.qml +++ b/imports/Spectral/Panel/RoomPanelForm.ui.qml @@ -54,7 +54,7 @@ Item { visible: currentRoom - source: "qrc:/assets/img/roompanel.svg" + source: MSettings.darkTheme ? "qrc:/assets/img/roompanel-dark.svg" : "qrc:/assets/img/roompanel.svg" fillMode: Image.PreserveAspectCrop } diff --git a/qml/main.qml b/qml/main.qml index 24aaeb25c..5644add1b 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -28,6 +28,9 @@ ApplicationWindow { visible: true title: qsTr("Spectral") + Material.foreground: Material.theme == Material.Dark ? "#FFFFFF" : "#1D333E" + Material.background: Material.theme == Material.Dark ? "#303030" : "#FFFFFF" + Platform.SystemTrayIcon { visible: MSettings.showTray iconSource: "qrc:/assets/img/icon.png" @@ -52,8 +55,8 @@ ApplicationWindow { quitOnLastWindowClosed: !MSettings.showTray onNotificationClicked: { - roomPage.enteredRoom = spectralController.connection.room(roomId) - roomPage.goToEvent(eventId) + roomForm.enteredRoom = spectralController.connection.room(roomId) + roomForm.goToEvent(eventId) showWindow() } onErrorOccured: { @@ -64,6 +67,95 @@ ApplicationWindow { onSyncDone: roomListForm.errorControl.visible = false } + Dialog { + property bool busy: false + + width: 360 + height: 300 + x: (window.width - width) / 2 + y: (window.height - height) / 2 + + id: loginDialog + + parent: ApplicationWindow.overlay + + title: "Login" + + contentItem: ColumnLayout { + AutoTextField { + Layout.fillWidth: true + + id: serverField + + placeholderText: "Server Address" + text: "https://matrix.org" + } + + AutoTextField { + Layout.fillWidth: true + + id: usernameField + + placeholderText: "Username" + } + + AutoTextField { + Layout.fillWidth: true + + id: passwordField + + placeholderText: "Password" + echoMode: TextInput.Password + } + } + + footer: DialogButtonBox { + Button { + text: "OK" + flat: true + enabled: !loginDialog.busy + + onClicked: loginDialog.doLogin() + } + + Button { + text: "Cancel" + flat: true + enabled: !loginDialog.busy + + onClicked: loginDialog.close() + } + + ToolTip { + id: loginButtonTooltip + } + } + + Component.onCompleted: { + spectralController.onErrorOccured.connect(function(error, detail) { + loginDialog.busy = false + loginButtonTooltip.text = error + ": " + detail + loginButtonTooltip.open() + }) + } + + function doLogin() { + if (!(serverField.text.startsWith("http") && serverField.text.includes("://"))) { + loginButtonTooltip.text = "Server address should start with http(s)://" + loginButtonTooltip.open() + return + } + + loginDialog.busy = true + spectralController.loginWithCredentials(serverField.text, usernameField.text, passwordField.text) + + spectralController.connectionAdded.connect(function(conn) { + busy = false + loginDialog.close() + }) + } + } + SplitView { anchors.fill: parent @@ -108,13 +200,9 @@ ApplicationWindow { window.hide() } - function showError() { - - } - -// Component.onCompleted: { -// spectralController.initiated.connect(function() { -// if (spectralController.accountCount == 0) stackView.push(loginPage) -// }) -// } + Component.onCompleted: { + spectralController.initiated.connect(function() { + if (spectralController.accountCount == 0) loginDialog.open() + }) + } } diff --git a/res.qrc b/res.qrc index cdb60b50f..224dd114a 100644 --- a/res.qrc +++ b/res.qrc @@ -13,13 +13,11 @@ imports/Spectral/Component/AutoMouseArea.qml imports/Spectral/Component/MaterialIcon.qml imports/Spectral/Component/qmldir - imports/Spectral/Component/SideNavButton.qml imports/Spectral/Effect/ElevationEffect.qml imports/Spectral/Effect/qmldir imports/Spectral/Menu/MessageContextMenu.qml imports/Spectral/Menu/qmldir imports/Spectral/Menu/RoomContextMenu.qml - imports/Spectral/Page/Login.qml imports/Spectral/Page/qmldir assets/font/material.ttf assets/img/icon.icns @@ -29,14 +27,12 @@ imports/Spectral/Font/MaterialFont.qml imports/Spectral/Font/qmldir imports/Spectral/Setting/qmldir - imports/Spectral/Page/LoginForm.ui.qml imports/Spectral/Panel/qmldir imports/Spectral/Panel/RoomDrawer.qml imports/Spectral/Panel/RoomListPanel.qml imports/Spectral/Panel/RoomPanel.qml imports/Spectral/Panel/RoomPanelForm.ui.qml imports/Spectral/Panel/RoomHeader.qml - imports/Spectral/Panel/RoomListDelegate.qml imports/Spectral/Component/ScrollHelper.qml imports/Spectral/Component/AutoListView.qml imports/Spectral/Component/Timeline/TimelineImage.qml @@ -48,5 +44,8 @@ imports/Spectral/Component/Timeline/SectionDelegate.qml assets/img/roompanel.svg assets/img/matrix.svg + imports/Spectral/Effect/RippleEffect.qml + imports/Spectral/Effect/CircleMask.qml + assets/img/roompanel-dark.svg diff --git a/src/controller.cpp b/src/controller.cpp index 764907d70..823081c23 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -58,26 +58,27 @@ inline QString accessTokenFileName(const AccountSettings& account) { void Controller::loginWithCredentials(QString serverAddr, QString user, QString pass) { if (!user.isEmpty() && !pass.isEmpty()) { - Connection* m_connection = new Connection(this); - m_connection->setHomeserver(QUrl(serverAddr)); - m_connection->connectToServer(user, pass, ""); - connect(m_connection, &Connection::connected, [=] { - AccountSettings account(m_connection->userId()); + Connection* conn = new Connection(this); + conn->setHomeserver(QUrl(serverAddr)); + conn->connectToServer(user, pass, ""); + connect(conn, &Connection::connected, [=] { + AccountSettings account(conn->userId()); account.setKeepLoggedIn(true); account.clearAccessToken(); // Drop the legacy - just in case - account.setHomeserver(m_connection->homeserver()); - account.setDeviceId(m_connection->deviceId()); + account.setHomeserver(conn->homeserver()); + account.setDeviceId(conn->deviceId()); account.setDeviceName("Spectral"); - if (!saveAccessToken(account, m_connection->accessToken())) + if (!saveAccessToken(account, conn->accessToken())) qWarning() << "Couldn't save access token"; account.sync(); - addConnection(m_connection); + addConnection(conn); + setConnection(conn); }); - connect(m_connection, &Connection::networkError, + connect(conn, &Connection::networkError, [=](QString error, QByteArray detail) { emit errorOccured("Network Error", error); }); - connect(m_connection, &Connection::loginError, + connect(conn, &Connection::loginError, [=](QString error, QByteArray detail) { emit errorOccured("Login Failed", error); }); @@ -98,6 +99,7 @@ void Controller::logout(Connection* conn) { conn->stopSync(); emit conn->stateChanged(); emit conn->loggedOut(); + if (!m_connections.isEmpty()) setConnection(m_connections[0]); }); connect(job, &LogoutJob::failure, this, [=] { emit errorOccured("Server-side Logout Failed", job->errorString());