Remove first row, modify room list panel.

square-messages
Black Hat 2018-11-17 00:04:51 +08:00
parent a296fffd91
commit aaae68a5bf
10 changed files with 322 additions and 507 deletions

View File

@ -15,13 +15,6 @@ Drawer {
id: roomDrawer
edge: Qt.RightEdge
interactive: false
ToolButton {
contentItem: MaterialIcon { icon: "\ue5c4" }
onClicked: roomDrawer.close()
}
ColumnLayout {
anchors.fill: parent

View File

@ -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 {

View File

@ -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 {

View File

@ -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}
}
##^##*/

View File

@ -55,7 +55,7 @@ Item {
visible: currentRoom
source: "qrc:/assets/img/roompanel.svg"
fillMode: Image.Pad
fillMode: Image.PreserveAspectCrop
}
ColumnLayout {

View File

@ -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

View File

@ -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)

View File

@ -38,7 +38,6 @@
<file>imports/Spectral/Panel/qmldir</file>
<file>imports/Spectral/Panel/RoomDrawer.qml</file>
<file>imports/Spectral/Panel/RoomListPanel.qml</file>
<file>imports/Spectral/Panel/RoomListPanelForm.ui.qml</file>
<file>imports/Spectral/Panel/RoomPanel.qml</file>
<file>imports/Spectral/Panel/RoomPanelForm.ui.qml</file>
<file>imports/Spectral/Panel/RoomHeader.qml</file>

View File

@ -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,

View File

@ -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<Connection*> m_connections;
QPointer<Connection> 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);