diff --git a/TODO.md b/TODO.md index d3204b89..6cfbfbf1 100644 --- a/TODO.md +++ b/TODO.md @@ -33,7 +33,7 @@ - Terrible performance using `QT_QPA_PLATFORM=wayland-egl`, must use `xcb` - UI - - Highlight selected account/room + - Remove first html lists left margin - Popup: - label size diff --git a/src/python/backend.py b/src/python/backend.py index a164c1d7..b7281534 100644 --- a/src/python/backend.py +++ b/src/python/backend.py @@ -165,16 +165,18 @@ class Backend: for account in sorted(self.models[Account].values()): data.append({ - "type": "Account", - "id": account.user_id, - "data": account.__dict__, + "type": "Account", + "id": account.user_id, + "user_id": account.user_id, + "data": account.__dict__, }) for room in sorted(self.models[Room, account.user_id].values()): data.append({ - "type": "Room", - "id": (account.user_id, room.room_id), - "data": room.__dict__, + "type": "Room", + "id": "/".join((account.user_id, room.room_id)), + "user_id": account.user_id, + "data": room.__dict__, }) return data diff --git a/src/qml/Base/HCheckBox.qml b/src/qml/Base/HCheckBox.qml index 00c0ecd8..ed8e1ca6 100644 --- a/src/qml/Base/HCheckBox.qml +++ b/src/qml/Base/HCheckBox.qml @@ -26,7 +26,7 @@ CheckBox { anchors.centerIn: parent dimension: parent.width - 2 svgName: "check-mark" - colorize: theme.colors.accentText + colorize: theme.colors.strongAccentBackground visible: scale > 0 scale: box.checked ? 1 : 0 diff --git a/src/qml/SidePane/AccountDelegate.qml b/src/qml/SidePane/AccountDelegate.qml deleted file mode 100644 index 6d4afa0e..00000000 --- a/src/qml/SidePane/AccountDelegate.qml +++ /dev/null @@ -1,94 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Layouts 1.12 -import "../Base" - -Column { - id: accountDelegate - width: parent.width - spacing: theme.spacing / 2 - - opacity: - paneToolBar.roomFilter && roomList.model.count < 1 ? 0.3 : 1 - Behavior on opacity { HNumberAnimation {} } - - property alias roomList: roomList - - property bool forceExpand: paneToolBar.roomFilter && roomList.model.count - property bool expanded: true - readonly property var modelItem: model - - readonly property bool isCurrent: - window.uiState.page == "Pages/EditAccount/EditAccount.qml" && - window.uiState.pageProperties.userId == model.user_id - - Component.onCompleted: - expanded = ! window.uiState.collapseAccounts[model.user_id] - - onExpandedChanged: { - window.uiState.collapseAccounts[model.user_id] = ! expanded - window.uiStateChanged() - } - - function activate() { - pageStack.showPage( - "EditAccount/EditAccount", { "userId": model.user_id } - ) - } - - HInteractiveRectangle { - id: rectangle - width: parent.width - height: childrenRect.height - color: theme.sidePane.account.background - - checked: accountDelegate.isCurrent - - TapHandler { onTapped: accountDelegate.activate() } - - HRowLayout { - id: row - width: parent.width - - HUserAvatar { - id: avatar - userId: model.user_id - displayName: model.display_name - avatarUrl: model.avatar_url - } - - HLabel { - id: accountLabel - color: theme.sidePane.account.name - text: model.display_name || model.user_id - font.pixelSize: theme.fontSize.big - elide: HLabel.ElideRight - leftPadding: sidePane.currentSpacing - rightPadding: leftPadding - - Layout.fillWidth: true - Layout.fillHeight: true - } - - ExpandButton { - id: expandButton - opacity: paneToolBar.roomFilter ? 0 : 1 - expandableItem: accountDelegate - Layout.preferredHeight: row.height - } - } - } - - RoomList { - id: roomList - visible: height > 0 - width: parent.width - height: - childrenRect.height * - (accountDelegate.expanded || accountDelegate.forceExpand ? 1 : 0) - clip: heightAnimation.running - - userId: modelItem.user_id - - Behavior on height { HNumberAnimation { id: heightAnimation } } - } -} diff --git a/src/qml/SidePane/AccountList.qml b/src/qml/SidePane/AccountList.qml deleted file mode 100644 index 229bbc54..00000000 --- a/src/qml/SidePane/AccountList.qml +++ /dev/null @@ -1,15 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Layouts 1.12 -import "../Base" - -HListView { - id: accountList - clip: true - - model: HListModel { - keyField: "user_id" - source: modelSources["Account"] || [] - } - - delegate: AccountDelegate {} -} diff --git a/src/qml/SidePane/AccountRoomList.qml b/src/qml/SidePane/AccountRoomList.qml new file mode 100644 index 00000000..4dbc386a --- /dev/null +++ b/src/qml/SidePane/AccountRoomList.qml @@ -0,0 +1,21 @@ +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import "../Base" + +HListView { + id: accountRoomList + + // property bool forceExpand: paneToolBar.roomFilter && roomList.model.count + property bool forceExpand: false + + model: HListModel { + keyField: "id" + source: window.sidePaneModelSource + } + + delegate: Loader { + width: accountRoomList.width + source: "Delegate" + + (model.type == "Account" ? "Account.qml" : "Room.qml") + } +} diff --git a/src/qml/SidePane/DelegateAccount.qml b/src/qml/SidePane/DelegateAccount.qml new file mode 100644 index 00000000..18cdd92e --- /dev/null +++ b/src/qml/SidePane/DelegateAccount.qml @@ -0,0 +1,93 @@ +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import "../Base" + +HInteractiveRectangle { + id: accountDelegate + height: row.height + color: theme.sidePane.account.background + + checked: accountDelegate.isCurrent + readonly property bool isCurrent: + window.uiState.page == "Pages/EditAccount/EditAccount.qml" && + window.uiState.pageProperties.userId == model.data.user_id + + readonly property bool collapsed: + ! accountRoomList.forceExpand && + window.uiState.collapseAccounts[model.data.user_id] || false + + function toggleCollapse() { + window.uiState.collapseAccounts[model.data.user_id] = ! collapsed + window.uiStateChanged() + } + + function activate() { + pageStack.showPage( + "EditAccount/EditAccount", { "userId": model.data.user_id } + ) + } + + TapHandler { onTapped: accountDelegate.activate() } + + HRowLayout { + id: row + width: parent.width + + HUserAvatar { + id: avatar + userId: model.data.user_id + displayName: model.data.display_name + avatarUrl: model.data.avatar_url + + opacity: collapsed ? theme.sidePane.account.collapsedOpacity : 1 + Behavior on opacity { HNumberAnimation {} } + + Layout.topMargin: model.index > 0 ? sidePane.currentSpacing / 2 : 0 + Layout.bottomMargin: Layout.topMargin + } + + HLabel { + id: accountLabel + color: theme.sidePane.account.name + text: model.data.display_name || model.data.user_id + font.pixelSize: theme.fontSize.big + elide: HLabel.ElideRight + + leftPadding: sidePane.currentSpacing + verticalAlignment: Text.AlignVCenter + + opacity: collapsed ? theme.sidePane.account.collapsedOpacity : 1 + Behavior on opacity { HNumberAnimation {} } + + Layout.fillWidth: true + Layout.fillHeight: true + } + + HUIButton { + id: expandButton + iconName: "expand" + iconDimension: 16 + backgroundColor: "transparent" + leftPadding: sidePane.currentSpacing + rightPadding: leftPadding + onClicked: accountDelegate.toggleCollapse() + + visible: opacity > 0 + opacity: + accountRoomList.forceExpand ? 0 : + collapsed ? theme.sidePane.account.collapsedOpacity + 0.2 : + 1 + Behavior on opacity { HNumberAnimation {} } + + iconTransform: Rotation { + origin.x: expandButton.iconDimension / 2 + origin.y: expandButton.iconDimension / 2 + + angle: collapsed ? 180 : 90 + Behavior on angle { HNumberAnimation {} } + } + + Layout.fillHeight: true + } + } +} diff --git a/src/qml/SidePane/RoomDelegate.qml b/src/qml/SidePane/DelegateRoom.qml similarity index 67% rename from src/qml/SidePane/RoomDelegate.qml rename to src/qml/SidePane/DelegateRoom.qml index ef5f992f..d1e2bb2c 100644 --- a/src/qml/SidePane/RoomDelegate.qml +++ b/src/qml/SidePane/DelegateRoom.qml @@ -5,49 +5,58 @@ import "../utils.js" as Utils HInteractiveRectangle { id: roomDelegate - width: roomList.width - height: rowLayout.height color: theme.sidePane.room.background - opacity: model.left ? theme.sidePane.room.leftRoomOpacity : 1 + readonly property bool collapsed: + ! accountRoomList.forceExpand && + window.uiState.collapseAccounts[model.user_id] || false + + visible: height > 0 + height: collapsed ? 0 : rowLayout.height + Behavior on height { HNumberAnimation {} } + + opacity: model.data.left ? theme.sidePane.room.leftRoomOpacity : 1 Behavior on opacity { HNumberAnimation {} } checked: isCurrent readonly property bool isCurrent: window.uiState.page == "Chat/Chat.qml" && - window.uiState.pageProperties.userId == userId && - window.uiState.pageProperties.roomId == model.room_id + window.uiState.pageProperties.userId == model.user_id && + window.uiState.pageProperties.roomId == model.data.room_id - function activate() { pageStack.showRoom(userId, model.room_id) } + function activate() { + pageStack.showRoom(model.user_id, model.data.room_id) + print(model.user_id, model.data.room_id) + } TapHandler { onTapped: activate() } HRowLayout { id: rowLayout - x: sidePane.currentSpacing - width: parent.width - sidePane.currentSpacing * 1.5 - height: roomName.height + subtitle.height + - sidePane.currentSpacing spacing: sidePane.currentSpacing + x: spacing + width: parent.width - spacing * 1.75 + height: roomName.height + subtitle.height + spacing HRoomAvatar { id: roomAvatar - displayName: model.display_name - avatarUrl: model.avatar_url + displayName: model.data.display_name + avatarUrl: model.data.avatar_url } HColumnLayout { Layout.fillWidth: true HRowLayout { - spacing: theme.spacing / 2 + spacing: rowLayout.spacing HLabel { id: roomName color: theme.sidePane.room.name - text: model.display_name || "Empty room" + text: model.data.display_name || "Empty room" textFormat: - model.display_name? Text.PlainText : Text.StyledText + model.data.display_name? + Text.PlainText : Text.StyledText elide: Text.ElideRight verticalAlignment: Qt.AlignVCenter @@ -59,13 +68,15 @@ HInteractiveRectangle { visible: Layout.maximumWidth > 0 Layout.maximumWidth: - model.inviter_id && ! model.left ? implicitWidth : 0 + model.data.inviter_id && ! model.data.left ? + implicitWidth : 0 Behavior on Layout.maximumWidth { HNumberAnimation {} } } HLabel { readonly property var evDate: - model.last_event ? model.last_event.date : null + model.data.last_event ? + model.data.last_event.date : null id: lastEventDate font.pixelSize: theme.fontSize.small @@ -97,9 +108,9 @@ HInteractiveRectangle { elide: Text.ElideRight text: { - if (! model.last_event) { return "" } + if (! model.data.last_event) { return "" } - let ev = model.last_event + let ev = model.data.last_event if (ev.event_type === "RoomMessageEmote" || ! ev.event_type.startsWith("RoomMessage")) { diff --git a/src/qml/SidePane/ExpandButton.qml b/src/qml/SidePane/ExpandButton.qml deleted file mode 100644 index c2f169c6..00000000 --- a/src/qml/SidePane/ExpandButton.qml +++ /dev/null @@ -1,23 +0,0 @@ -import QtQuick 2.12 -import "../Base" - -HUIButton { - property var expandableItem: null - - id: expandButton - iconName: "expand" - iconDimension: 16 - backgroundColor: "transparent" - onClicked: expandableItem.expanded = ! expandableItem.expanded - - visible: opacity > 0 - opacity: expandableItem.forceExpand ? 0 : 1 - Behavior on opacity { HNumberAnimation {} } - - iconTransform: Rotation { - origin.x: expandButton.iconDimension / 2 - origin.y: expandButton.iconDimension / 2 - angle: expandableItem.expanded || expandableItem.forceExpand ? 90 : 180 - Behavior on angle { HNumberAnimation {} } - } -} diff --git a/src/qml/SidePane/RoomList.qml b/src/qml/SidePane/RoomList.qml deleted file mode 100644 index d84671cd..00000000 --- a/src/qml/SidePane/RoomList.qml +++ /dev/null @@ -1,19 +0,0 @@ -import QtQuick 2.12 -import "../Base" -import "../utils.js" as Utils - -HFixedListView { - id: roomList - - property string userId: "" - - model: HListModel { - source: Utils.filterModelSource( - modelSources[["Room", userId]] || [], - paneToolBar.roomFilter, - ) - keyField: "room_id" - } - - delegate: RoomDelegate {} -} diff --git a/src/qml/SidePane/SidePane.qml b/src/qml/SidePane/SidePane.qml index bba0cd2c..621ae65e 100644 --- a/src/qml/SidePane/SidePane.qml +++ b/src/qml/SidePane/SidePane.qml @@ -5,13 +5,13 @@ import "../utils.js" as Utils HRectangle { id: sidePane - clip: true // Avoid artifacts when collapsed + clip: true opacity: mainUI.accountsPresent && ! reduce ? 1 : 0 visible: opacity > 0 color: theme.sidePane.background - property alias accountList: accountList + property alias accountRoomList: accountRoomList property alias paneToolBar: paneToolBar property real autoWidthRatio: theme.sidePane.autoWidthRatio @@ -142,13 +142,12 @@ HRectangle { HColumnLayout { anchors.fill: parent - AccountList { - id: accountList + AccountRoomList { + id: accountRoomList + clip: true + Layout.fillWidth: true Layout.fillHeight: true - - spacing: currentSpacing - bottomMargin: currentSpacing } PaneToolBar { diff --git a/src/qml/Window.qml b/src/qml/Window.qml index 93936d00..e3ff75de 100644 --- a/src/qml/Window.qml +++ b/src/qml/Window.qml @@ -15,6 +15,7 @@ ApplicationWindow { // Note: For JS object variables, the corresponding method to notify // key/value changes must be called manually, e.g. settingsChanged(). property var modelSources: ({}) + property var sidePaneModelSource: [] property var mainUI: null diff --git a/src/qml/event_handlers.js b/src/qml/event_handlers.js index ed9495c0..e0465b92 100644 --- a/src/qml/event_handlers.js +++ b/src/qml/event_handlers.js @@ -22,7 +22,7 @@ function onCoroutineDone(uuid, result) { function onModelUpdated(syncId, data, serializedSyncId) { if (serializedSyncId == ["Account"] || serializedSyncId[0] == "Room") { py.callCoro("get_flat_sidepane_data", [], data => { - print( JSON.stringify( data, null, 4)) + window.sidePaneModelSource = data }) } diff --git a/src/themes/Default.qpl b/src/themes/Default.qpl index 573153dc..cbe9cbcc 100644 --- a/src/themes/Default.qpl +++ b/src/themes/Default.qpl @@ -48,6 +48,12 @@ colors: color inputBackground: hsluv(hue, saturation, intensity * 2, Math.min(0.6, opacity)) + color accentBackground: + hsluv(hue, saturation * 1.25, intensity * 42, Math.min(0.6, opacity)) + + color strongAccentBackground: + hsluv(hue, saturation * 2, intensity * 52, Math.min(0.6, opacity)) + color brightText: hsluv(0, 0, intensity * 100) color text: hsluv(0, 0, intensity * 80) color halfDimText: hsluv(0, 0, intensity * 70) @@ -83,10 +89,9 @@ controls: button: color background: colors.inputBackground + color checkedBackground: colors.accentBackground color disabledBackground: hsluv(0, 0, colors.intensity * 2, Math.min(0.6, opacity)) - color checkedBackground: - hsluv(0, 0, colors.intensity * 40, Math.min(0.6, opacity)) color hoveredOverlay: hsluv(0, 0, 100) color text: colors.text @@ -95,13 +100,13 @@ controls: interactiveRectangle: color background: "transparent" - color hoveredOverlay: hsluv(0, 0, 100) - color pressedOverlay: hsluv(0, 0, 100) - color checkedOverlay: hsluv(0, 0, 100) + color hoveredOverlay: hsluv(0, 0, 50) + color pressedOverlay: hsluv(0, 0, 50) + color checkedOverlay: hsluv(0, 0, 50) - real hoveredOpacity: 0.1 + real hoveredOpacity: 0.3 real pressedOpacity: 0.2 - real checkedOpacity: 0.08 + real checkedOpacity: 0.2 textField: color background: colors.inputBackground @@ -109,7 +114,7 @@ controls: int borderWidth: 1 color border: "transparent" - color focusedBorder: colors.accentText + color focusedBorder: colors.strongAccentBackground color text: colors.text color focusedText: colors.text @@ -158,8 +163,9 @@ sidePane: color background: colors.strongBackground account: - color background: "transparent" - color name: colors.text + real collapsedOpacity: 0.3 + color background: "transparent" + color name: colors.text room: real leftRoomOpacity: 0.65