diff --git a/imports/Spectral/Component/AutoTextField.qml b/imports/Spectral/Component/AutoTextField.qml index a70d1e4..1925826 100644 --- a/imports/Spectral/Component/AutoTextField.qml +++ b/imports/Spectral/Component/AutoTextField.qml @@ -1,8 +1,62 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 -import Spectral.Setting 0.1 +import QtQuick.Controls.Material 2.3 TextField { + id: textField + selectByMouse: true + + topPadding: 8 + bottomPadding: 8 + + background: Item { + Label { + id: floatingPlaceholder + + anchors.top: parent.top + anchors.left: parent.left + anchors.topMargin: textField.topPadding + anchors.leftMargin: textField.leftPadding + transformOrigin: Item.TopLeft + visible: false + color: Material.accent + + states: [ + State { + name: "shown" + when: textField.text.length !== 0 + PropertyChanges { target: floatingPlaceholder; scale: 0.8 } + PropertyChanges { target: floatingPlaceholder; anchors.topMargin: -floatingPlaceholder.height * 0.4 } + } + ] + + transitions: [ + Transition { + to: "shown" + SequentialAnimation { + PropertyAction { target: floatingPlaceholder; property: "text"; value: textField.placeholderText } + PropertyAction { target: floatingPlaceholder; property: "visible"; value: true } + PropertyAction { target: textField; property: "placeholderTextColor"; value: "transparent" } + ParallelAnimation { + NumberAnimation { target: floatingPlaceholder; property: "scale"; duration: 250; easing.type: Easing.InOutQuad } + NumberAnimation { target: floatingPlaceholder; property: "anchors.topMargin"; duration: 250; easing.type: Easing.InOutQuad } + } + } + }, + Transition { + from: "shown" + SequentialAnimation { + ParallelAnimation { + NumberAnimation { target: floatingPlaceholder; property: "scale"; duration: 250; easing.type: Easing.InOutQuad } + NumberAnimation { target: floatingPlaceholder; property: "anchors.topMargin"; duration: 250; easing.type: Easing.InOutQuad } + } + PropertyAction { target: textField; property: "placeholderTextColor"; value: "grey" } + PropertyAction { target: floatingPlaceholder; property: "visible"; value: false } + } + } + ] + } + } } diff --git a/imports/Spectral/Panel/RoomDrawer.qml b/imports/Spectral/Panel/RoomDrawer.qml index b6b547e..f22bc04 100644 --- a/imports/Spectral/Panel/RoomDrawer.qml +++ b/imports/Spectral/Panel/RoomDrawer.qml @@ -59,54 +59,62 @@ Drawer { Layout.fillWidth: true } - RowLayout { + Control { Layout.fillWidth: true - spacing: 8 + padding: 0 - MaterialIcon { - Layout.preferredWidth: 32 - Layout.preferredHeight: 32 - Layout.alignment: Qt.AlignTop + contentItem: RowLayout { + spacing: 8 - icon: "\ue88f" - color: MPalette.lighter + MaterialIcon { + Layout.preferredWidth: 32 + Layout.preferredHeight: 32 + Layout.alignment: Qt.AlignTop + + icon: "\ue88f" + color: MPalette.lighter + } + + ColumnLayout { + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + wrapMode: Label.Wrap + text: room && room.canonicalAlias ? room.canonicalAlias : "No Canonical Alias" + color: MPalette.accent + } + + Label { + Layout.fillWidth: true + + wrapMode: Label.Wrap + text: "Main Alias" + color: MPalette.lighter + } + + Label { + Layout.fillWidth: true + + wrapMode: Label.Wrap + text: room && room.topic ? room.topic : "No Topic" + color: MPalette.foreground + } + + Label { + Layout.fillWidth: true + + wrapMode: Label.Wrap + text: "Topic" + color: MPalette.lighter + } + } } - ColumnLayout { - Layout.fillWidth: true - - Label { - Layout.fillWidth: true - - wrapMode: Label.Wrap - text: room && room.canonicalAlias ? room.canonicalAlias : "No Canonical Alias" - color: MPalette.accent - } - - Label { - Layout.fillWidth: true - - wrapMode: Label.Wrap - text: "Main Alias" - color: MPalette.lighter - } - - Label { - Layout.fillWidth: true - - wrapMode: Label.Wrap - text: room && room.topic ? room.topic : "No Topic" - color: MPalette.foreground - } - - Label { - Layout.fillWidth: true - - wrapMode: Label.Wrap - text: "Topic" - color: MPalette.lighter - } + background: RippleEffect { + onPrimaryClicked: roomDetailDialog.open() } } @@ -209,9 +217,115 @@ Drawer { contentItem: AutoTextField { id: inviteUserDialogTextField - placeholderText: "@bot:matrix.org" + placeholderText: "User ID" } onAccepted: room.inviteToRoom(inviteUserDialogTextField.text) } + + Dialog { + anchors.centerIn: parent + width: 480 + + id: roomDetailDialog + + parent: ApplicationWindow.overlay + + title: "Room Settings - " + (room ? room.displayName : "") + modal: true + + contentItem: ColumnLayout { + RowLayout { + Layout.fillWidth: true + + spacing: 16 + + Avatar { + Layout.preferredWidth: 72 + Layout.preferredHeight: 72 + Layout.alignment: Qt.AlignTop + + hint: room ? room.displayName : "No name" + source: room ? room.avatarMediaId : null + } + + ColumnLayout { + Layout.fillWidth: true + Layout.margins: 4 + + AutoTextField { + Layout.fillWidth: true + + text: room ? room.name : "" + placeholderText: "Room Name" + } + + AutoTextField { + Layout.fillWidth: true + + text: room ? room.topic : "" + placeholderText: "Room Topic" + } + } + } + + MenuSeparator { + Layout.fillWidth: true + } + + ColumnLayout { + Layout.fillWidth: true + + RowLayout { + Layout.fillWidth: true + + Label { + Layout.preferredWidth: 100 + + wrapMode: Label.Wrap + text: "Main Alias" + color: MPalette.lighter + } + + ComboBox { + Layout.fillWidth: true + + model: room ? room.aliases : null + + currentIndex: room ? room.aliases.indexOf(room.canonicalAlias) : -1 + } + } + + RowLayout { + Layout.fillWidth: true + + Label { + Layout.preferredWidth: 100 + Layout.alignment: Qt.AlignTop + + wrapMode: Label.Wrap + text: "Aliases" + color: MPalette.lighter + } + + ColumnLayout { + Layout.fillWidth: true + + Repeater { + model: room ? room.aliases : null + + delegate: Label { + Layout.fillWidth: true + + text: modelData + + font.pixelSize: 12 + color: MPalette.lighter + } + } + } + } + } + } + } } diff --git a/imports/Spectral/Panel/RoomListPanel.qml b/imports/Spectral/Panel/RoomListPanel.qml index 7bf74c5..560291c 100644 --- a/imports/Spectral/Panel/RoomListPanel.qml +++ b/imports/Spectral/Panel/RoomListPanel.qml @@ -44,8 +44,8 @@ Item { switch (category) { case 1: return "Invited" case 2: return "Favorites" - case 3: return "Rooms" - case 4: return "People" + case 3: return "People" + case 4: return "Rooms" case 5: return "Low Priority" } } @@ -80,135 +80,6 @@ Item { ] } -// Drawer { -// width: Math.max(root.width, 400) -// height: root.height - -// id: drawer - -// edge: Qt.LeftEdge - -// ColumnLayout { -// anchors.fill: parent - -// id: mainColumn - -// spacing: 0 - -// Control { -// Layout.fillWidth: true -// Layout.preferredHeight: 330 - -// padding: 24 - -// contentItem: ColumnLayout { -// spacing: 4 - -// Avatar { -// Layout.preferredWidth: 200 -// Layout.preferredHeight: 200 -// Layout.margins: 12 -// Layout.alignment: Qt.AlignHCenter - -// source: root.user ? root.user.avatarMediaId : null -// hint: root.user ? root.user.displayName : "?" -// } - -// Label { -// Layout.alignment: Qt.AlignHCenter - -// text: root.user ? root.user.displayName : "No Name" -// color: "white" -// font.pixelSize: 22 -// } - -// Label { -// Layout.alignment: Qt.AlignHCenter - -// text: root.user ? root.user.id : "@example:matrix.org" -// color: "white" -// opacity: 0.7 -// font.pixelSize: 13 -// } -// } - -// background: Rectangle { color: Material.primary } - -// RippleEffect { -// anchors.fill: parent -// } -// } - -// ScrollView { -// Layout.fillWidth: true -// Layout.fillHeight: true - -// clip: true - -// ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - -// ColumnLayout { -// width: mainColumn.width -// spacing: 0 - -// Repeater { -// model: AccountListModel { -// controller: spectralController -// } - -// delegate: ItemDelegate { -// Layout.fillWidth: true - -// text: user.displayName - -// onClicked: { -// controller.connection = connection -// drawer.close() -// } -// } -// } - -// ItemDelegate { -// Layout.fillWidth: true - -// text: "Add Account" - -// onClicked: loginDialog.open() -// } - -// Rectangle { -// Layout.fillWidth: true -// Layout.preferredHeight: 1 - -// color: MSettings.darkTheme ? "#424242" : "#e7ebeb" -// } - -// ItemDelegate { -// Layout.fillWidth: true - -// text: "Settings" -// } - -// ItemDelegate { -// Layout.fillWidth: true - -// text: "Logout" - -// onClicked: controller.logout(controller.connection) -// } - -// ItemDelegate { -// Layout.fillWidth: true - -// text: "Exit" - -// onClicked: Qt.quit() -// } -// } -// } -// } -// } - ColumnLayout { anchors.fill: parent spacing: 0 @@ -225,7 +96,7 @@ Item { rightPadding: 18 contentItem: RowLayout { - ItemDelegate { + ToolButton { Layout.preferredWidth: height Layout.fillHeight: true @@ -275,7 +146,7 @@ Item { onClicked: filterMenu.popup() } - ItemDelegate { + ToolButton { Layout.preferredWidth: height Layout.fillHeight: true @@ -290,16 +161,12 @@ Item { readonly property bool active: text Layout.fillWidth: true - Layout.fillHeight: true + Layout.alignment: Qt.AlignVCenter id: searchField - topPadding: 0 - bottomPadding: 0 placeholderText: "Search..." color: MPalette.lighter - - background: Item {} } Avatar { @@ -520,6 +387,7 @@ Item { onTriggered: category === RoomType.Favorite ? currentRoom.removeTag("m.favourite") : currentRoom.addTag("m.favourite", 1.0) } + MenuItem { text: "Deprioritize" checkable: true @@ -527,14 +395,18 @@ Item { onTriggered: category === RoomType.Deprioritized ? currentRoom.removeTag("m.lowpriority") : currentRoom.addTag("m.lowpriority", 1.0) } + MenuSeparator {} + MenuItem { text: "Mark as Read" onTriggered: currentRoom.markAllMessagesAsRead() } + MenuItem { text: "Leave Room" + Material.foreground: Material.Red onTriggered: currentRoom.forget() } diff --git a/qml/main.qml b/qml/main.qml index ba7e2bc..3de3130 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -87,9 +87,9 @@ ApplicationWindow { title: "Login" - contentItem: Column { + contentItem: ColumnLayout { AutoTextField { - width: parent.width + Layout.fillWidth: true id: serverField @@ -98,7 +98,7 @@ ApplicationWindow { } AutoTextField { - width: parent.width + Layout.fillWidth: true id: usernameField @@ -108,7 +108,7 @@ ApplicationWindow { } AutoTextField { - width: parent.width + Layout.fillWidth: true id: passwordField @@ -120,14 +120,6 @@ ApplicationWindow { } footer: DialogButtonBox { - Button { - text: "OK" - flat: true - enabled: !loginDialog.busy - - onClicked: loginDialog.doLogin() - } - Button { text: "Cancel" flat: true @@ -136,6 +128,14 @@ ApplicationWindow { onClicked: loginDialog.close() } + Button { + text: "OK" + flat: true + enabled: !loginDialog.busy + + onClicked: loginDialog.doLogin() + } + ToolTip { id: loginButtonTooltip @@ -442,7 +442,7 @@ ApplicationWindow { title: "Start a Chat" contentItem: ColumnLayout { - TextField { + AutoTextField { Layout.fillWidth: true id: identifierField @@ -473,7 +473,7 @@ ApplicationWindow { title: "Create a Room" contentItem: ColumnLayout { - TextField { + AutoTextField { Layout.fillWidth: true id: roomNameField @@ -481,7 +481,7 @@ ApplicationWindow { placeholderText: "Room Name" } - TextField { + AutoTextField { Layout.fillWidth: true id: roomTopicField @@ -496,7 +496,7 @@ ApplicationWindow { } Drawer { - width: (inPortrait ? 0.67 : 0.3) * window.width + width: Math.min((inPortrait ? 0.67 : 0.3) * window.width, 360) height: window.height modal: inPortrait interactive: inPortrait @@ -531,7 +531,7 @@ ApplicationWindow { } RoomDrawer { - width: (inPortrait ? 0.67 : 0.3) * window.width + width: Math.min((inPortrait ? 0.67 : 0.3) * window.width, 360) height: window.height modal: inPortrait interactive: inPortrait diff --git a/src/roomlistmodel.cpp b/src/roomlistmodel.cpp index 3eec7c1..d364755 100644 --- a/src/roomlistmodel.cpp +++ b/src/roomlistmodel.cpp @@ -16,8 +16,10 @@ RoomListModel::RoomListModel(QObject* parent) : QAbstractListModel(parent) {} RoomListModel::~RoomListModel() {} void RoomListModel::setConnection(Connection* connection) { - if (connection == m_connection) return; - if (m_connection) m_connection->disconnect(this); + if (connection == m_connection) + return; + if (m_connection) + m_connection->disconnect(this); if (!connection) { qDebug() << "Removing current connection..."; m_connection = nullptr; @@ -29,7 +31,8 @@ void RoomListModel::setConnection(Connection* connection) { m_connection = connection; - for (SpectralRoom* room : m_rooms) room->disconnect(this); + for (SpectralRoom* room : m_rooms) + room->disconnect(this); connect(connection, &Connection::connected, this, &RoomListModel::doResetModel); @@ -40,6 +43,13 @@ void RoomListModel::setConnection(Connection* connection) { connect(connection, &Connection::leftRoom, this, &RoomListModel::updateRoom); connect(connection, &Connection::aboutToDeleteRoom, this, &RoomListModel::deleteRoom); + connect(connection, &Connection::directChatsListChanged, this, + [=](Connection::DirectChatsMap additions, + Connection::DirectChatsMap removals) { + for (QString roomID : additions.values() + removals.values()) + refresh(static_cast(connection->room(roomID)), + {CategoryRole}); + }); doResetModel(); } @@ -47,11 +57,14 @@ void RoomListModel::setConnection(Connection* connection) { void RoomListModel::doResetModel() { beginResetModel(); m_rooms.clear(); - for (auto r : m_connection->roomMap()) doAddRoom(r); + for (auto r : m_connection->roomMap()) + doAddRoom(r); endResetModel(); } -SpectralRoom* RoomListModel::roomAt(int row) { return m_rooms.at(row); } +SpectralRoom* RoomListModel::roomAt(int row) { + return m_rooms.at(row); +} void RoomListModel::doAddRoom(Room* r) { if (auto* room = static_cast(r)) { @@ -77,16 +90,19 @@ void RoomListModel::connectRoomSignals(SpectralRoom* room) { connect(room, &Room::addedMessages, this, [=] { refresh(room, {LastEventRole}); }); connect(room, &Room::notificationCountChanged, this, [=] { - if (room->notificationCount() == 0) return; - if (room->timelineSize() == 0) return; - const RoomEvent* lastEvent = room->messageEvents().rbegin()->get(); - if (lastEvent->isStateEvent()) return; - User* sender = room->user(lastEvent->senderId()); - if (sender == room->localUser()) return; - emit newMessage( - room->id(), lastEvent->id(), room->displayName(), - sender->displayname(), room->eventToString(*lastEvent), - room->avatar(128)); + if (room->notificationCount() == 0) + return; + if (room->timelineSize() == 0) + return; + const RoomEvent* lastEvent = room->messageEvents().rbegin()->get(); + if (lastEvent->isStateEvent()) + return; + User* sender = room->user(lastEvent->senderId()); + if (sender == room->localUser()) + return; + emit newMessage(room->id(), lastEvent->id(), room->displayName(), + sender->displayname(), room->eventToString(*lastEvent), + room->avatar(128)); }); } @@ -129,7 +145,8 @@ void RoomListModel::updateRoom(Room* room, Room* prev) { void RoomListModel::deleteRoom(Room* room) { qDebug() << "Deleting room" << room->id(); const auto it = std::find(m_rooms.begin(), m_rooms.end(), room); - if (it == m_rooms.end()) return; // Already deleted, nothing to do + if (it == m_rooms.end()) + return; // Already deleted, nothing to do qDebug() << "Erasing room" << room->id(); const int row = it - m_rooms.begin(); beginRemoveRows(QModelIndex(), row, row); @@ -138,34 +155,49 @@ void RoomListModel::deleteRoom(Room* room) { } int RoomListModel::rowCount(const QModelIndex& parent) const { - if (parent.isValid()) return 0; + if (parent.isValid()) + return 0; return m_rooms.count(); } QVariant RoomListModel::data(const QModelIndex& index, int role) const { - if (!index.isValid()) return QVariant(); + if (!index.isValid()) + return QVariant(); if (index.row() >= m_rooms.count()) { qDebug() << "UserListModel: something wrong here..."; return QVariant(); } SpectralRoom* room = m_rooms.at(index.row()); - if (role == NameRole) return room->displayName(); - if (role == AvatarRole) return room->avatarMediaId(); - if (role == TopicRole) return room->topic(); + if (role == NameRole) + return room->displayName(); + if (role == AvatarRole) + return room->avatarMediaId(); + if (role == TopicRole) + return room->topic(); if (role == CategoryRole) { - if (room->joinState() == JoinState::Invite) return RoomType::Invited; - if (room->isFavourite()) return RoomType::Favorite; - if (room->isDirectChat()) return RoomType::Direct; - if (room->isLowPriority()) return RoomType::Deprioritized; + if (room->joinState() == JoinState::Invite) + return RoomType::Invited; + if (room->isFavourite()) + return RoomType::Favorite; + if (room->isDirectChat()) + return RoomType::Direct; + if (room->isLowPriority()) + return RoomType::Deprioritized; return RoomType::Normal; } - if (role == UnreadCountRole) return room->unreadCount(); - if (role == NotificationCountRole) return room->notificationCount(); - if (role == HighlightCountRole) return room->highlightCount(); - if (role == LastEventRole) return room->lastEvent(); - if (role == LastActiveTimeRole) return room->lastActiveTime(); - if (role == CurrentRoomRole) return QVariant::fromValue(room); + if (role == UnreadCountRole) + return room->unreadCount(); + if (role == NotificationCountRole) + return room->notificationCount(); + if (role == HighlightCountRole) + return room->highlightCount(); + if (role == LastEventRole) + return room->lastEvent(); + if (role == LastActiveTimeRole) + return room->lastActiveTime(); + if (role == CurrentRoomRole) + return QVariant::fromValue(room); return QVariant(); } diff --git a/src/roomlistmodel.h b/src/roomlistmodel.h index d8c56d6..23ea92b 100644 --- a/src/roomlistmodel.h +++ b/src/roomlistmodel.h @@ -17,8 +17,8 @@ class RoomType : public QObject { enum Types { Invited = 1, Favorite, - Normal, Direct, + Normal, Deprioritized, }; REGISTER_ENUM(Types) @@ -75,9 +75,12 @@ class RoomListModel : public QAbstractListModel { signals: void connectionChanged(); void roomAdded(SpectralRoom* room); - void newMessage(const QString& roomId, const QString& eventId, - const QString& roomName, const QString& senderName, - const QString& text, const QImage& icon); + void newMessage(const QString& roomId, + const QString& eventId, + const QString& roomName, + const QString& senderName, + const QString& text, + const QImage& icon); }; #endif // ROOMLISTMODEL_H