diff --git a/imports/Spectral/Panel/RoomDrawer.qml b/imports/Spectral/Panel/RoomDrawer.qml index dc07bbd..77e3a26 100644 --- a/imports/Spectral/Panel/RoomDrawer.qml +++ b/imports/Spectral/Panel/RoomDrawer.qml @@ -15,13 +15,6 @@ Drawer { id: roomDrawer edge: Qt.RightEdge - interactive: false - - ToolButton { - contentItem: MaterialIcon { icon: "\ue5c4" } - - onClicked: roomDrawer.close() - } ColumnLayout { anchors.fill: parent diff --git a/imports/Spectral/Panel/RoomListDelegate.qml b/imports/Spectral/Panel/RoomListDelegate.qml index e09726c..a342806 100644 --- a/imports/Spectral/Panel/RoomListDelegate.qml +++ b/imports/Spectral/Panel/RoomListDelegate.qml @@ -8,14 +8,10 @@ import Spectral.Setting 0.1 import Spectral.Component 2.0 -Rectangle { - color: MSettings.darkTheme ? "#303030" : "#fafafa" - +Item { AutoMouseArea { anchors.fill: parent - hoverEnabled: miniMode - onSecondaryClicked: { roomContextMenu.model = model roomContextMenu.popup() @@ -30,9 +26,6 @@ Rectangle { enteredRoom = currentRoom } } - - ToolTip.visible: miniMode && containsMouse - ToolTip.text: name } Rectangle { diff --git a/imports/Spectral/Panel/RoomListPanel.qml b/imports/Spectral/Panel/RoomListPanel.qml index 6472f4a..1d5ed6a 100644 --- a/imports/Spectral/Panel/RoomListPanel.qml +++ b/imports/Spectral/Panel/RoomListPanel.qml @@ -1,15 +1,44 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import QtQuick.Controls.Material 2.2 + +import Spectral.Component 2.0 +import Spectral.Menu 2.0 + +import Spectral 0.1 +import Spectral.Setting 0.1 + import SortFilterProxyModel 0.2 -RoomListPanelForm { - model: sortedRoomListModel +Rectangle { + property var controller: null + readonly property var user: controller.connection ? controller.connection.localUser : null + + readonly property int filter: 0 + property var enteredRoom: null + property alias errorControl: errorControl + + signal enterRoom(var room) + signal leaveRoom(var room) + + id: root + + color: MSettings.darkTheme ? "#303030" : "#FFFFFF" + + RoomListModel { + id: roomListModel + + connection: controller.connection + + onNewMessage: if (!window.active) spectralController.postNotification(roomId, eventId, roomName, senderName, text, icon, iconPath) + } SortFilterProxyModel { id: sortedRoomListModel - sourceModel: listModel + sourceModel: roomListModel proxyRoles: ExpressionRole { name: "display" @@ -53,9 +82,239 @@ RoomListPanelForm { ] } - Shortcut { - sequence: StandardKey.Find - onActivated: searchField.forceActiveFocus() + Drawer { + width: Math.max(root.width, 360) + height: root.height + + id: drawer + + edge: Qt.LeftEdge + + ColumnLayout { + width: parent.width + spacing: 0 + + Control { + Layout.fillWidth: true + Layout.preferredHeight: 330 + + padding: 24 + + contentItem: ColumnLayout { + spacing: 4 + + ImageItem { + Layout.preferredWidth: 200 + Layout.preferredHeight: 200 + Layout.margins: 12 + Layout.alignment: Qt.AlignHCenter + + source: root.user ? root.user.paintable : null + hint: root.user ? root.user.displayName : "?" + } + + Label { + Layout.alignment: Qt.AlignHCenter + + text: root.user ? root.user.displayName : "No Name" + color: "white" + font.pointSize: 16.5 + } + + Label { + Layout.alignment: Qt.AlignHCenter + + text: root.user ? root.user.id : "@example:matrix.org" + color: "white" + opacity: 0.7 + font.pointSize: 9.75 + } + } + + background: Rectangle { color: "#455A64" } + } + + Repeater { + model: AccountListModel { + controller: spectralController + } + + delegate: ItemDelegate { + Layout.fillWidth: true + + text: user.displayName + + onClicked: controller.connection = connection + } + } + + ItemDelegate { + Layout.fillWidth: true + + text: "Exit" + + onClicked: Qt.quit() + } + } + } + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + Control { + Layout.fillWidth: true + Layout.preferredHeight: 64 + + topPadding: 12 + bottomPadding: 12 + leftPadding: 12 + rightPadding: 18 + + contentItem: RowLayout { + ItemDelegate { + Layout.preferredWidth: height + Layout.fillHeight: true + + contentItem: MaterialIcon { + icon: searchField.visible ? "\ue5cd" : "\ue8b6" + color: searchField.visible ? "#1D333E" : "#7F7F7F" + } + + onClicked: { + if (searchField.visible) searchField.clear() + searchField.visible = !searchField.visible + } + } + + AutoTextField { + Layout.fillWidth: true + Layout.fillHeight: true + + id: searchField + + visible: false + topPadding: 0 + bottomPadding: 0 + placeholderText: "Search..." + + background: Item {} + } + + Label { + Layout.fillWidth: true + Layout.fillHeight: true + + visible: !searchField.visible + + text: root.user ? root.user.displayName : "No Name" + elide: Text.ElideRight + wrapMode: Text.NoWrap + font.pointSize: 12 + color: "#7F7F7F" + verticalAlignment: Text.AlignVCenter + } + + ImageItem { + Layout.preferredWidth: height + Layout.fillHeight: true + + visible: !searchField.visible + + source: root.user ? root.user.paintable : null + hint: root.user ? root.user.displayName : "?" + + MouseArea { + anchors.fill: parent + onClicked: drawer.open() + } + } + } + } + + Control { + property string error: "" + property string detail: "" + + Layout.fillWidth: true + + id: errorControl + + visible: false + + topPadding: 16 + bottomPadding: 16 + leftPadding: 24 + rightPadding: 24 + + contentItem: ColumnLayout { + Label { + Layout.fillWidth: true + + text: errorControl.error + font.pointSize: 12 + color: "white" + wrapMode: Text.Wrap + } + Label { + Layout.fillWidth: true + + text: errorControl.detail + font.pointSize: 10.5 + color: "white" + opacity: 0.6 + wrapMode: Text.Wrap + } + ItemDelegate { + Layout.preferredHeight: 32 + Layout.alignment: Qt.AlignRight + + text: "Dismiss" + Material.foreground: "white" + onClicked: errorControl.visible = false + } + } + + background: Rectangle { color: "#273338" } + } + + AutoListView { + Layout.fillWidth: true + Layout.fillHeight: true + + id: listView + + spacing: 0 + clip: true + + model: sortedRoomListModel + + boundsBehavior: Flickable.DragOverBounds + + ScrollBar.vertical: ScrollBar {} + + delegate: RoomListDelegate { + width: parent.width + height: 64 + } + + section.property: "display" + section.criteria: ViewSection.FullString + section.delegate: Label { + width: parent.width + height: 24 + + text: section + color: "#5B7480" + leftPadding: 16 + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + + RoomContextMenu { + id: roomContextMenu + } + } } Dialog { diff --git a/imports/Spectral/Panel/RoomListPanelForm.ui.qml b/imports/Spectral/Panel/RoomListPanelForm.ui.qml deleted file mode 100644 index 45cbba1..0000000 --- a/imports/Spectral/Panel/RoomListPanelForm.ui.qml +++ /dev/null @@ -1,138 +0,0 @@ -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.3 -import QtGraphicalEffects 1.0 -import QtQuick.Controls.Material 2.2 -import QtQml.Models 2.3 - -import Spectral.Component 2.0 -import Spectral.Menu 2.0 -import Spectral.Effect 2.0 - -import Spectral 0.1 -import Spectral.Setting 0.1 -import SortFilterProxyModel 0.2 - -import "qrc:/js/util.js" as Util - -Rectangle { - property var listModel - property int filter: 0 - property var enteredRoom: null - - property alias searchField: searchField - property alias model: listView.model - - property bool miniMode: width == 64 - - signal enterRoom(var room) - signal leaveRoom(var room) - - color: MSettings.darkTheme ? "#323232" : "#f3f3f3" - - Label { - text: miniMode ? "Empty" : "Here? No, not here." - anchors.centerIn: parent - visible: listView.count === 0 - } - - ColumnLayout { - anchors.fill: parent - spacing: 0 - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - Layout.margins: 12 - - color: MSettings.darkTheme ? "#303030" : "#fafafa" - - RowLayout { - anchors.fill: parent - - spacing: 0 - - MaterialIcon { - Layout.preferredWidth: height - Layout.fillHeight: true - - visible: !miniMode && !searchField.text - - icon: "\ue8b6" - color: "grey" - } - - ItemDelegate { - Layout.preferredWidth: height - Layout.fillHeight: true - - visible: !miniMode && searchField.text - - contentItem: MaterialIcon { - icon: "\ue5cd" - color: "grey" - } - - onClicked: searchField.text = "" - } - - AutoTextField { - Layout.fillWidth: true - Layout.fillHeight: true - - id: searchField - - topPadding: 0 - bottomPadding: 0 - placeholderText: "Search..." - - background: Item { - } - } - } - } - - AutoListView { - Layout.fillWidth: true - Layout.fillHeight: true - - id: listView - - spacing: 0 - clip: true - - boundsBehavior: Flickable.DragOverBounds - - ScrollBar.vertical: ScrollBar { - } - - delegate: RoomListDelegate { - width: parent.width - height: 64 - } - - section.property: "display" - section.criteria: ViewSection.FullString - section.delegate: Label { - width: parent.width - height: 24 - - text: section - color: "grey" - leftPadding: miniMode ? undefined : 16 - elide: Text.ElideRight - verticalAlignment: Text.AlignVCenter - horizontalAlignment: miniMode ? Text.AlignHCenter : undefined - } - - RoomContextMenu { - id: roomContextMenu - } - } - } -} - -/*##^## Designer { - D{i:0;autoSize:true;height:480;width:640} -} - ##^##*/ diff --git a/imports/Spectral/Panel/RoomPanelForm.ui.qml b/imports/Spectral/Panel/RoomPanelForm.ui.qml index 96e7d79..5f153be 100644 --- a/imports/Spectral/Panel/RoomPanelForm.ui.qml +++ b/imports/Spectral/Panel/RoomPanelForm.ui.qml @@ -55,7 +55,7 @@ Item { visible: currentRoom source: "qrc:/assets/img/roompanel.svg" - fillMode: Image.Pad + fillMode: Image.PreserveAspectCrop } ColumnLayout { diff --git a/imports/Spectral/Panel/RoomPanelInput.qml b/imports/Spectral/Panel/RoomPanelInput.qml index 37bb67b..01dd2dc 100644 --- a/imports/Spectral/Panel/RoomPanelInput.qml +++ b/imports/Spectral/Panel/RoomPanelInput.qml @@ -102,14 +102,6 @@ Rectangle { } } - Rectangle { - width: currentRoom && currentRoom.hasFileUploading ? parent.width * currentRoom.fileUploadingProgress / 100 : 0 - height: parent.height - - opacity: 0.2 - color: Material.accent - } - RowLayout { anchors.fill: parent @@ -169,6 +161,14 @@ Rectangle { background: Item { } + Rectangle { + width: currentRoom && currentRoom.hasFileUploading ? parent.width * currentRoom.fileUploadingProgress / 100 : 0 + height: parent.height + + opacity: 0.2 + color: Material.accent + } + Timer { id: timeoutTimer diff --git a/qml/main.qml b/qml/main.qml index 1dc0d86..291301d 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -5,8 +5,10 @@ import QtQuick.Controls.Material 2.2 import Qt.labs.settings 1.0 import Qt.labs.platform 1.0 as Platform +import Spectral.Panel 2.0 import Spectral.Component 2.0 import Spectral.Page 2.0 +import Spectral.Effect 2.0 import Spectral 0.1 import Spectral.Setting 0.1 @@ -14,8 +16,6 @@ import Spectral.Setting 0.1 import "qrc:/js/util.js" as Util ApplicationWindow { - readonly property var currentConnection: accountListView.currentConnection ? accountListView.currentConnection : null - Material.theme: MSettings.darkTheme ? Material.Dark : Material.Light width: 960 @@ -52,353 +52,50 @@ ApplicationWindow { quitOnLastWindowClosed: !MSettings.showTray onNotificationClicked: { - roomPage.enteredRoom = currentConnection.room(roomId) + roomPage.enteredRoom = spectralController.connection.room(roomId) roomPage.goToEvent(eventId) showWindow() } onErrorOccured: { - errorDialog.error = error - errorDialog.detail = detail - errorDialog.open() + roomListForm.errorControl.error = error + roomListForm.errorControl.detail = detail + roomListForm.errorControl.visible = true } + onSyncDone: roomListForm.errorControl.visible = false } - AccountListModel { - id: accountListModel - controller: spectralController - } - - Dialog { - property string error - property string detail - - x: (window.width - width) / 2 - y: (window.height - height) / 2 - - id: errorDialog - - title: error + " Error" - contentItem: Label { text: errorDialog.detail } - } - - Component { - id: loginPage - - Login { controller: spectralController } - } - - Room { - id: roomPage - - parent: null - - connection: currentConnection - } - - Setting { - id: settingPage - - parent: null - - listModel: accountListModel - } - - RowLayout { + SplitView { anchors.fill: parent - spacing: 0 - Rectangle { - Layout.preferredWidth: 64 - Layout.fillHeight: true + RoomListPanel { + width: window.width * 0.35 + Layout.minimumWidth: 180 - id: sideNav + id: roomListForm - color: Material.primary + clip: true - ColumnLayout { - anchors.fill: parent - spacing: 0 + controller: spectralController - AutoListView { - property var currentConnection: null - - Layout.fillWidth: true - Layout.fillHeight: true - - id: accountListView - - model: accountListModel - - spacing: 0 - - clip: true - - delegate: Column { - property bool expanded: accountListView.currentConnection === connection - - width: parent.width - - spacing: 0 - - SideNavButton { - width: parent.width - height: width - - selected: stackView.currentItem === page && currentConnection === connection - - ImageItem { - anchors.fill: parent - anchors.margins: 12 - - hint: user.displayName - source: user.paintable - } - - highlightColor: Material.accent - - page: roomPage - - onClicked: { - accountListView.currentConnection = connection - roomPage.filter = 0 - } - } - - Column { - width: parent.width - height: expanded ? implicitHeight : 0 - - spacing: 0 - clip: true - - SideNavButton { - width: parent.width - height: width - - MaterialIcon { - anchors.fill: parent - - icon: "\ue7f7" - color: "white" - } - - onClicked: roomPage.filter = 1 - } - - SideNavButton { - width: parent.width - height: width - - MaterialIcon { - anchors.fill: parent - - icon: "\ue7fd" - color: "white" - } - - onClicked: roomPage.filter = 2 - } - - SideNavButton { - width: parent.width - height: width - - MaterialIcon { - anchors.fill: parent - - icon: "\ue7fb" - color: "white" - } - - onClicked: roomPage.filter = 3 - } - - Behavior on height { - PropertyAnimation { easing.type: Easing.InOutCubic; duration: 200 } - } - } - } - } - - SideNavButton { - Layout.fillWidth: true - Layout.preferredHeight: width - - MaterialIcon { - anchors.fill: parent - - icon: "\ue145" - color: "white" - } - - enabled: !addRoomMenu.opened - onClicked: addRoomMenu.popup() - - Menu { - id: addRoomMenu - - MenuItem { - text:"New Room" - onTriggered: addRoomDialog.open() - - Dialog { - id: addRoomDialog - parent: ApplicationWindow.overlay - - x: (window.width - width) / 2 - y: (window.height - height) / 2 - width: 360 - - title: "New Room" - modal: true - standardButtons: Dialog.Ok | Dialog.Cancel - - contentItem: Column { - AutoTextField { - width: parent.width - - id: addRoomDialogNameTextField - - placeholderText: "Name" - } - AutoTextField { - width: parent.width - - id: addRoomDialogTopicTextField - - placeholderText: "Topic" - } - } - - onAccepted: spectralController.createRoom(currentConnection, addRoomDialogNameTextField.text, addRoomDialogTopicTextField.text) - } - } - - MenuItem { - text: "Join Room" - - onTriggered: joinRoomDialog.open() - - Dialog { - x: (window.width - width) / 2 - y: (window.height - height) / 2 - width: 360 - - id: joinRoomDialog - - parent: ApplicationWindow.overlay - - title: "Input Room Alias or ID" - modal: true - standardButtons: Dialog.Ok | Dialog.Cancel - - contentItem: AutoTextField { - id: joinRoomDialogTextField - placeholderText: "#matrix:matrix.org" - } - - onAccepted: spectralController.joinRoom(currentConnection, joinRoomDialogTextField.text) - } - } - - MenuItem { - text: "Direct Chat" - - onTriggered: directChatDialog.open() - - Dialog { - x: (window.width - width) / 2 - y: (window.height - height) / 2 - width: 360 - - id: directChatDialog - - parent: ApplicationWindow.overlay - - title: "Input User ID" - modal: true - standardButtons: Dialog.Ok | Dialog.Cancel - - contentItem: AutoTextField { - id: directChatDialogTextField - placeholderText: "@bot:matrix.org" - } - - onAccepted: spectralController.createDirectChat(currentConnection, directChatDialogTextField.text) - } - } - } - } - - SideNavButton { - Layout.fillWidth: true - Layout.preferredHeight: width - - MaterialIcon { - anchors.fill: parent - - icon: "\ue8b8" - color: "white" - } - page: settingPage - } - - SideNavButton { - Layout.fillWidth: true - Layout.preferredHeight: width - - MaterialIcon { - anchors.fill: parent - - icon: "\ue8ac" - color: "white" - } - - onClicked: MSettings.confirmOnExit ? confirmExitDialog.open() : Qt.quit() - - Dialog { - x: (window.width - width) / 2 - y: (window.height - height) / 2 - width: 360 - - id: confirmExitDialog - - parent: ApplicationWindow.overlay - - title: "Exit" - modal: true - standardButtons: Dialog.Ok | Dialog.Cancel - - contentItem: Column { - Label { text: "Exit?" } - CheckBox { - text: "Do not ask next time" - checked: !MSettings.confirmOnExit - - onCheckedChanged: MSettings.confirmOnExit = !checked - } - } - - onAccepted: Qt.quit() - } - } - } + onLeaveRoom: roomForm.saveReadMarker(room) } - StackView { + RoomPanel { Layout.fillWidth: true - Layout.fillHeight: true + Layout.minimumWidth: 480 - id: stackView + id: roomForm - initialItem: roomPage + clip: true + + currentRoom: roomListForm.enteredRoom } } Binding { target: imageProvider property: "connection" - value: currentConnection + value: spectralController.connection } function showWindow() { @@ -411,6 +108,10 @@ ApplicationWindow { window.hide() } + function showError() { + + } + Component.onCompleted: { spectralController.initiated.connect(function() { if (spectralController.accountCount == 0) stackView.push(loginPage) diff --git a/res.qrc b/res.qrc index 778e88c..133b5c7 100644 --- a/res.qrc +++ b/res.qrc @@ -38,7 +38,6 @@ imports/Spectral/Panel/qmldir imports/Spectral/Panel/RoomDrawer.qml imports/Spectral/Panel/RoomListPanel.qml - imports/Spectral/Panel/RoomListPanelForm.ui.qml imports/Spectral/Panel/RoomPanel.qml imports/Spectral/Panel/RoomPanelForm.ui.qml imports/Spectral/Panel/RoomHeader.qml diff --git a/src/controller.cpp b/src/controller.cpp index 06dffed..764907d 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -75,7 +75,7 @@ void Controller::loginWithCredentials(QString serverAddr, QString user, }); connect(m_connection, &Connection::networkError, [=](QString error, QByteArray detail) { - emit errorOccured("Network", error); + emit errorOccured("Network Error", error); }); connect(m_connection, &Connection::loginError, [=](QString error, QByteArray detail) { @@ -110,6 +110,7 @@ void Controller::addConnection(Connection* c) { m_connections.push_back(c); connect(c, &Connection::syncDone, this, [=] { + emit syncDone(); c->sync(30000); static int counter = 0; @@ -152,11 +153,12 @@ void Controller::invokeLogin() { }); connect(c, &Connection::networkError, [=](QString error, QByteArray detail) { - emit errorOccured("Network", error); + emit errorOccured("Network Error", error); }); c->connectWithToken(account.userId(), accessToken, account.deviceId()); } } + if (!m_connections.isEmpty()) setConnection(m_connections[0]); emit initiated(); } @@ -184,7 +186,7 @@ bool Controller::saveAccessToken(const AccountSettings& account, auto fileDir = QFileInfo(accountTokenFile).dir(); if (!((fileDir.exists() || fileDir.mkpath(".")) && accountTokenFile.open(QFile::WriteOnly))) { - emit errorOccured("Token", "Cannot save access token."); + emit errorOccured("I/O Denied", "Cannot save access token."); } else { accountTokenFile.write(accessToken); return true; @@ -227,14 +229,6 @@ void Controller::playAudio(QUrl localFile) { connect(player, &QMediaPlayer::stateChanged, [=] { player->deleteLater(); }); } -QColor Controller::color(QString userId) { - return QColor(SettingsGroup("UI/Color").value(userId, "#498882").toString()); -} - -void Controller::setColor(QString userId, QColor newColor) { - SettingsGroup("UI/Color").setValue(userId, newColor.name()); -} - void Controller::postNotification(const QString& roomId, const QString& eventId, const QString& roomName, const QString& senderName, diff --git a/src/controller.h b/src/controller.h index 0234fb4..3db7c64 100644 --- a/src/controller.h +++ b/src/controller.h @@ -20,6 +20,8 @@ class Controller : public QObject { connectionDropped) Q_PROPERTY(bool quitOnLastWindowClosed READ quitOnLastWindowClosed WRITE setQuitOnLastWindowClosed NOTIFY quitOnLastWindowClosedChanged) + Q_PROPERTY(Connection* connection READ connection WRITE setConnection NOTIFY + connectionChanged) public: explicit Controller(QObject* parent = nullptr); @@ -47,13 +49,23 @@ class Controller : public QObject { } } - Q_INVOKABLE QColor color(QString userId); - Q_INVOKABLE void setColor(QString userId, QColor newColor); + Connection* connection() { + if (m_connection.isNull()) return nullptr; + return m_connection; + } + + void setConnection(Connection* conn) { + if (!conn) return; + if (conn == m_connection) return; + m_connection = conn; + emit connectionChanged(); + } private: QClipboard* m_clipboard = QApplication::clipboard(); NotificationsManager notificationsManager; QVector m_connections; + QPointer m_connection; QByteArray loadAccessToken(const AccountSettings& account); bool saveAccessToken(const AccountSettings& account, @@ -67,11 +79,13 @@ class Controller : public QObject { signals: void busyChanged(); void errorOccured(QString error, QString detail); + void syncDone(); void connectionAdded(Connection* conn); void connectionDropped(Connection* conn); void initiated(); void notificationClicked(const QString roomId, const QString eventId); void quitOnLastWindowClosedChanged(); + void connectionChanged(); public slots: void logout(Connection* conn);