From cccc43a9aed208080625a1fdf03513c09766446c Mon Sep 17 00:00:00 2001 From: miruka Date: Tue, 26 Mar 2019 03:19:55 -0400 Subject: [PATCH] Reorganize sidePane, accounts and rooms - Accordion design for accounts and rooms (not finished) - Toolbar and account/room lists reduce correctly, buttons become hamburger menu if not enough width - Can set status using the "Set status message" account fields - Uniformized avatar sizes for sidePane, roomHeader and SendBox --- harmonyqml/AccountDelegate.qml | 88 +++++++++++++++-------- harmonyqml/AccountList.qml | 11 +++ harmonyqml/ActionButton.qml | 30 +------- harmonyqml/{TopBar.qml => ButtonsBar.qml} | 4 +- harmonyqml/ChatPage.qml | 1 + harmonyqml/HButton.qml | 31 ++++++++ harmonyqml/HMouseArea.qml | 5 -- harmonyqml/MessageDelegate.qml | 2 +- harmonyqml/RoomDelegate.qml | 16 ++--- harmonyqml/RoomHeader.qml | 2 +- harmonyqml/RoomList.qml | 25 +++++++ harmonyqml/RoomPane.qml | 30 -------- harmonyqml/SendBox.qml | 7 +- harmonyqml/SidePane.qml | 21 ++++++ harmonyqml/UI.qml | 17 +++-- harmonyqml/backend/base.py | 34 +++++++-- harmonyqml/backend/dummy.py | 22 +++--- harmonyqml/icons/down.svg | 1 + harmonyqml/icons/up.svg | 1 + 19 files changed, 219 insertions(+), 129 deletions(-) create mode 100644 harmonyqml/AccountList.qml rename harmonyqml/{TopBar.qml => ButtonsBar.qml} (88%) create mode 100644 harmonyqml/HButton.qml delete mode 100644 harmonyqml/HMouseArea.qml create mode 100644 harmonyqml/RoomList.qml delete mode 100644 harmonyqml/RoomPane.qml create mode 100644 harmonyqml/SidePane.qml create mode 100644 harmonyqml/icons/down.svg create mode 100644 harmonyqml/icons/up.svg diff --git a/harmonyqml/AccountDelegate.qml b/harmonyqml/AccountDelegate.qml index 45acae6b..0a468693 100644 --- a/harmonyqml/AccountDelegate.qml +++ b/harmonyqml/AccountDelegate.qml @@ -2,53 +2,83 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.4 -Row { - readonly property string displayName: - Backend.getUser(section).display_name +ColumnLayout { + id: "accountDelegate" + spacing: 0 + width: parent.width - id: row - width: roomListView.width - height: Math.max(accountLabel.height + statusEdit.height, avatar.height) + RowLayout { + id: "row" + spacing: 0 - Avatar { id: avatar; username: displayName; dimmension: 32 } - - Rectangle { - color: "#111" - width: parent.width - avatar.width - height: parent.height + Avatar { id: "avatar"; username: display_name; dimmension: 36 } ColumnLayout { - anchors.fill: parent - spacing: 1 + Layout.fillWidth: true + Layout.fillHeight: true + spacing: 0 PlainLabel { - id: accountLabel - text: displayName - color: "#CCC" + id: "accountLabel" + text: display_name elide: Text.ElideRight maximumLineCount: 1 Layout.fillWidth: true - - topPadding: -2 - bottomPadding: -2 - leftPadding: 5 - rightPadding: 5 + leftPadding: 6 + rightPadding: leftPadding } + TextField { - id: statusEdit + id: "statusEdit" + text: status_message || "" placeholderText: qsTr("Set status message") - background: Rectangle { color: "#333" } - color: "#CCC" + background: null + color: "black" selectByMouse: true font.family: "Roboto" font.pixelSize: 12 Layout.fillWidth: true + padding: 0 + leftPadding: accountLabel.leftPadding + rightPadding: leftPadding - topPadding: 0 - bottomPadding: 0 - leftPadding: 5 - rightPadding: 5 + onEditingFinished: { + Backend.setStatusMessage(user_id, text) + pageStack.forceActiveFocus() + } + } + } + + HButton { + id: "toggleExpand" + iconName: roomList.visible ? "up" : "down" + Layout.maximumWidth: 28 + Layout.maximumHeight: Layout.maximumWidth + + onClicked: { + toggleExpand.ToolTip.hide() + roomList.visible = ! roomList.visible } } } + + RoomList { + id: "roomList" + visible: true + user: Backend.getUser(user_id) + + Layout.minimumHeight: + roomList.visible ? + roomList.contentHeight + roomList.anchors.margins * 2 : + 0 + Layout.maximumHeight: Layout.minimumHeight + + Layout.minimumWidth: parent.width - Layout.leftMargin * 2 + Layout.maximumWidth: Layout.minimumWidth + + Layout.margins: accountList.spacing + Layout.leftMargin: + sidePane.width < 36 + Layout.margins ? 0 : Layout.margins + Layout.rightMargin: Layout.leftMargin + } } diff --git a/harmonyqml/AccountList.qml b/harmonyqml/AccountList.qml new file mode 100644 index 00000000..1b29903a --- /dev/null +++ b/harmonyqml/AccountList.qml @@ -0,0 +1,11 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.4 + +ListView { + id: "accountList" + spacing: 8 + model: Backend.accountsModel + delegate: AccountDelegate {} + clip: true +} diff --git a/harmonyqml/ActionButton.qml b/harmonyqml/ActionButton.qml index d714e120..39d5c631 100644 --- a/harmonyqml/ActionButton.qml +++ b/harmonyqml/ActionButton.qml @@ -2,41 +2,15 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.4 -ToolButton { - property string tooltip: "" - property string iconName: "" - property string targetPage: "" - +HButton { function toolBarIsBig() { - return roomPane.width > + return sidePane.width > Layout.minimumWidth * (toolBar.children.length - 2) } id: "button" - display: ToolButton.IconOnly - icon.source: "icons/" + iconName + ".svg" - background: Rectangle { color: "transparent" } - visible: toolBarIsBig() Layout.fillHeight: true Layout.fillWidth: true Layout.minimumWidth: height - - onClicked: { toolTip.hide(); pageStack.show_page(targetPage) } - - ToolTip { - id: "toolTip" - text: tooltip - delay: Qt.styleHints.mousePressAndHoldInterval - visible: text ? toolTipZone.containsMouse : false - } - MouseArea { - id: toolTipZone - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.NoButton // Make button receive clicks normally - - onEntered: button.background.color = "#656565" - onExited: button.background.color = "transparent" - } } diff --git a/harmonyqml/TopBar.qml b/harmonyqml/ButtonsBar.qml similarity index 88% rename from harmonyqml/TopBar.qml rename to harmonyqml/ButtonsBar.qml index 049c65c5..5fdfc82e 100644 --- a/harmonyqml/TopBar.qml +++ b/harmonyqml/ButtonsBar.qml @@ -17,13 +17,11 @@ RowLayout { ActionButton { iconName: "settings" tooltip: "Settings" - targetPage: "SettingsPage" } ActionButton { iconName: "add_account" tooltip: "Add new account" - targetPage: "AddAccountPage" } ActionButton { @@ -33,7 +31,7 @@ RowLayout { ActionButton { iconName: "search" - tooltip: "Filter rooms and people" + tooltip: "Filter rooms" } diff --git a/harmonyqml/ChatPage.qml b/harmonyqml/ChatPage.qml index 33bf3064..dba9056e 100644 --- a/harmonyqml/ChatPage.qml +++ b/harmonyqml/ChatPage.qml @@ -3,6 +3,7 @@ import QtQuick.Controls 2.2 import QtQuick.Layouts 1.4 ColumnLayout { + property var user: null property var room: null id: chatPage diff --git a/harmonyqml/HButton.qml b/harmonyqml/HButton.qml new file mode 100644 index 00000000..81532f0d --- /dev/null +++ b/harmonyqml/HButton.qml @@ -0,0 +1,31 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.4 + +ToolButton { + property string tooltip: "" + property string iconName: "" + + id: "button" + display: ToolButton.IconOnly + icon.source: "icons/" + iconName + ".svg" + background: Rectangle { color: "transparent" } + + onClicked: toolTip.hide() + + ToolTip { + id: "toolTip" + text: tooltip + delay: Qt.styleHints.mousePressAndHoldInterval + visible: text ? toolTipZone.containsMouse : false + } + MouseArea { + id: "toolTipZone" + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.NoButton // Make button receive clicks normally + + onEntered: button.background.color = "#656565" + onExited: button.background.color = "transparent" + } +} diff --git a/harmonyqml/HMouseArea.qml b/harmonyqml/HMouseArea.qml deleted file mode 100644 index cae30141..00000000 --- a/harmonyqml/HMouseArea.qml +++ /dev/null @@ -1,5 +0,0 @@ -import QtQuick 2.7 - -MouseArea { - cursorShape: Qt.PointingHandCursor -} diff --git a/harmonyqml/MessageDelegate.qml b/harmonyqml/MessageDelegate.qml index 2706ed12..d374c1cb 100644 --- a/harmonyqml/MessageDelegate.qml +++ b/harmonyqml/MessageDelegate.qml @@ -13,7 +13,7 @@ Column { Backend.getUser(sender_id).display_name readonly property bool isOwn: - chatPage.room.account_id === sender_id + chatPage.user.user_id === sender_id readonly property var previousData: index > 0 ? messageListView.model.get(index - 1) : null diff --git a/harmonyqml/RoomDelegate.qml b/harmonyqml/RoomDelegate.qml index a1ac25fb..9b3251be 100644 --- a/harmonyqml/RoomDelegate.qml +++ b/harmonyqml/RoomDelegate.qml @@ -2,17 +2,22 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.4 -Item { +MouseArea { id: "root" - width: roomListView.width + width: roomList.width height: Math.max(roomLabel.height + subtitleLabel.height, avatar.height) + onClicked: pageStack.show_room( + roomList.user, + roomList.model.get(index) + ) + RowLayout { anchors.fill: parent id: row spacing: 1 - Avatar { id: avatar; username: display_name; dimmension: 32 } + Avatar { id: avatar; username: display_name; dimmension: 36 } ColumnLayout { spacing: 0 @@ -48,9 +53,4 @@ Item { Item { Layout.fillWidth: true } } - - HMouseArea { - anchors.fill: parent - onClicked: pageStack.show_room(roomListView.model.get(index)) - } } diff --git a/harmonyqml/RoomHeader.qml b/harmonyqml/RoomHeader.qml index 745bb69c..da4682a8 100644 --- a/harmonyqml/RoomHeader.qml +++ b/harmonyqml/RoomHeader.qml @@ -5,7 +5,7 @@ import QtQuick.Layouts 1.4 Rectangle { id: root Layout.fillWidth: true - Layout.minimumHeight: 32 + Layout.minimumHeight: 36 Layout.maximumHeight: Layout.minimumHeight color: "#BBB" diff --git a/harmonyqml/RoomList.qml b/harmonyqml/RoomList.qml new file mode 100644 index 00000000..b219d108 --- /dev/null +++ b/harmonyqml/RoomList.qml @@ -0,0 +1,25 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.4 + +ListView { + property var user: null + + property int contentHeight: 0 + + onCountChanged: { + var children = roomList.children + var childrenHeight = 0 + + for (var i = 0; i < children.length; i++) { + childrenHeight += children[i].height + } + + contentHeight = childrenHeight + spacing * (children.length - 1) + } + + id: "roomList" + spacing: 8 + model: Backend.roomsModel[user.user_id] + delegate: RoomDelegate {} +} diff --git a/harmonyqml/RoomPane.qml b/harmonyqml/RoomPane.qml deleted file mode 100644 index aec29082..00000000 --- a/harmonyqml/RoomPane.qml +++ /dev/null @@ -1,30 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.4 - -Rectangle { - id: roomPane - color: "gray" - clip: true // Avoid artifacts when resizing pane width to minimum - - ColumnLayout { - anchors.fill: parent - spacing: 0 - - TopBar {} - - ListView { - Layout.fillWidth: true - Layout.fillHeight: true - - id: roomListView - spacing: 0 - model: Backend.roomsModel - delegate: RoomDelegate {} - //highlight: Rectangle {color: "lightsteelblue"; radius: 5} - - section.property: "account_id" - section.delegate: AccountDelegate {} - } - } -} diff --git a/harmonyqml/SendBox.qml b/harmonyqml/SendBox.qml index 3d26e639..c5b63638 100644 --- a/harmonyqml/SendBox.qml +++ b/harmonyqml/SendBox.qml @@ -19,9 +19,10 @@ Rectangle { Avatar { id: "avatar" - username: Backend.getUser(chatPage.room.account_id).display_name + username: chatPage.user.display_name dimmension: root.Layout.minimumHeight - visible: textArea.text === "" + //visible: textArea.text === "" + visible: textArea.height <= root.Layout.minimumHeight } ScrollView { @@ -49,7 +50,7 @@ Rectangle { return } - Backend.sendMessage(chatPage.room.account_id, + Backend.sendMessage(chatPage.user.user_id, chatPage.room.room_id, textArea.text) textArea.clear() diff --git a/harmonyqml/SidePane.qml b/harmonyqml/SidePane.qml new file mode 100644 index 00000000..7293d151 --- /dev/null +++ b/harmonyqml/SidePane.qml @@ -0,0 +1,21 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.4 + +Rectangle { + id: sidePane + color: "gray" + clip: true // Avoid artifacts when resizing pane width to minimum + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + AccountList { + Layout.fillWidth: true + Layout.fillHeight: true + } + + ButtonsBar {} + } +} diff --git a/harmonyqml/UI.qml b/harmonyqml/UI.qml index 274a78c3..e85070b9 100644 --- a/harmonyqml/UI.qml +++ b/harmonyqml/UI.qml @@ -6,21 +6,26 @@ import QtQuick.Layouts 1.4 Controls1.SplitView { anchors.fill: parent - RoomPane { - Layout.minimumWidth: 32 - width: 180 + SidePane { + Layout.minimumWidth: 36 + width: 200 } StackView { function show_page(componentName) { pageStack.replace(componentName + ".qml") } - function show_room(room_obj) { - pageStack.replace("ChatPage.qml", { room: room_obj }) + function show_room(user_obj, room_obj) { + pageStack.replace( + "ChatPage.qml", { user: user_obj, room: room_obj } + ) } id: "pageStack" - initialItem: ChatPage { room: Backend.roomsModel.get(0) } + initialItem: ChatPage { + user: Backend.accountsModel.get(0) + room: Backend.roomsModel[Backend.accountsModel.get(0).user_id].get(0) + } onCurrentItemChanged: currentItem.forceActiveFocus() diff --git a/harmonyqml/backend/base.py b/harmonyqml/backend/base.py index da3c1cdf..23512c83 100644 --- a/harmonyqml/backend/base.py +++ b/harmonyqml/backend/base.py @@ -11,13 +11,13 @@ from .list_model import ListModel, _QtListModel class User(NamedTuple): - user_id: str - display_name: str - avatar_url: Optional[str] = None + user_id: str + display_name: str + avatar_url: Optional[str] = None + status_message: Optional[str] = None class Room(NamedTuple): - account_id: str room_id: str display_name: str subtitle: str = "" @@ -41,13 +41,19 @@ class Backend(QObject): super().__init__() self._known_users: Dict[str, User] = {} - self.rooms: ListModel = ListModel() + self.accounts: ListModel = ListModel() + self.rooms: DefaultDict[str, ListModel] = DefaultDict(ListModel) self.messages: DefaultDict[str, ListModel] = DefaultDict(ListModel) @pyqtProperty(_QtListModel, constant=True) - def roomsModel(self) -> _QtListModel: - return self.rooms.qt_model + def accountsModel(self) -> _QtListModel: + return self.accounts.qt_model + + + @pyqtProperty("QVariantMap", constant=True) + def roomsModel(self) -> Dict[str, _QtListModel]: + return {account_id: l.qt_model for account_id, l in self.rooms.items()} @pyqtProperty("QVariantMap", constant=True) @@ -73,6 +79,10 @@ class Backend(QObject): @pyqtSlot(str, result="QVariantMap") def getUser(self, user_id: str) -> Dict[str, Any]: + for user in self.accounts: + if user.user_id == user_id: + return user._asdict() + try: return self._known_users[user_id]._asdict() except KeyError: @@ -87,3 +97,13 @@ class Backend(QObject): # pylint: disable=no-self-use md5 = hashlib.md5(bytes(string, "utf-8")).hexdigest() return float("0.%s" % int(md5[-10:], 16)) + + + @pyqtSlot(str, str) + def setStatusMessage(self, user_id: str, to: str) -> None: + for user in self.accounts: + if user.user_id == user_id: + user.status_message = to + break + else: + raise ValueError(f"{user_id} not found in Backend.accounts") diff --git a/harmonyqml/backend/dummy.py b/harmonyqml/backend/dummy.py index 2cccc468..13b095c9 100644 --- a/harmonyqml/backend/dummy.py +++ b/harmonyqml/backend/dummy.py @@ -3,7 +3,7 @@ from PyQt5.QtCore import QDateTime, Qt -from .base import Backend, Message, Room +from .base import Backend, Message, Room, User class DummyBackend(Backend): @@ -15,16 +15,22 @@ class DummyBackend(Backend): db = lambda t: QDateTime.fromString(f"2019-03-20T{t}.456", Qt.ISODateWithMs) - self.rooms.extend([ - Room("@renko:matrix.org", "!test:matrix.org", "Test", "Test room"), - Room("@renko:matrix.org", "!mary:matrix.org", "Mary", + self.accounts.extend([ + User("@renko:matrix.org", "Renko", None, "Sleeping, zzz..."), + User("@mary:matrix.org", "Mary"), + ]) + + self.rooms["@renko:matrix.org"].extend([ + Room("!test:matrix.org", "Test", "Test room"), + Room("!mary:matrix.org", "Mary", "Lorem ipsum sit dolor amet this is a long text to test " "wrapping of room subtitle etc 1234 example foo bar abc", 2), - Room("@renko:matrix.org", "!foo:matrix.org", "Another room"), + Room("!foo:matrix.org", "Another room"), + ]) - Room("@mary:matrix.org", "!test:matrix.org", "Test", "Test room"), - Room("@mary:matrix.org", "!mary:matrix.org", "Renko", - "Lorem ipsum sit dolor amet"), + self.rooms["@mary:matrix.org"].extend([ + Room("!test:matrix.org", "Test", "Test room"), + Room("!mary:matrix.org", "Renko", "Lorem ipsum sit dolor amet"), ]) self.messages["!test:matrix.org"].extend([ diff --git a/harmonyqml/icons/down.svg b/harmonyqml/icons/down.svg new file mode 100644 index 00000000..001a6592 --- /dev/null +++ b/harmonyqml/icons/down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/harmonyqml/icons/up.svg b/harmonyqml/icons/up.svg new file mode 100644 index 00000000..bebe0a45 --- /dev/null +++ b/harmonyqml/icons/up.svg @@ -0,0 +1 @@ + \ No newline at end of file