diff --git a/src/gui/Base/HTile.qml b/src/gui/Base/HTile.qml deleted file mode 100644 index 2efaf9b9..00000000 --- a/src/gui/Base/HTile.qml +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later - -import QtQuick 2.12 -import QtQuick.Layouts 1.12 - -HButton { - id: tile - - - signal leftClicked() - signal rightClicked() - signal longPressed() - - default property alias additionalData: contentItem.data - - property bool compact: window.settings.compactMode - property real contentOpacity: 1 - - readonly property alias title: title - readonly property alias additionalInfo: additionalInfo - readonly property alias rightInfo: rightInfo - readonly property alias subtitle: subtitle - readonly property Item loadedImage: imageLoader.item - - property alias contextMenu: contextMenuLoader.sourceComponent - - property Component image - - - contentItem: HRowLayout { - id: contentItem - spacing: tile.spacing - opacity: tile.contentOpacity - - HLoader { - id: imageLoader - sourceComponent: image - } - - HColumnLayout { - Layout.fillWidth: true - - HRowLayout { - spacing: tile.spacing - - HLabel { - id: title - text: "Missing title" - elide: Text.ElideRight - verticalAlignment: Qt.AlignVCenter - - Layout.fillWidth: true - Layout.fillHeight: true - } - - HLabel { - id: rightInfo - font.pixelSize: theme.fontSize.small - verticalAlignment: Qt.AlignVCenter - color: theme.colors.halfDimText - visible: Layout.maximumWidth > 0 - - Layout.fillHeight: true - Layout.maximumWidth: - text && tile.width >= 200 * theme.uiScale ? - implicitWidth : 0 - - Behavior on Layout.maximumWidth { HNumberAnimation {} } - } - - HRowLayout { - id: additionalInfo - visible: visibleChildren.length > 0 - } - - } - - HRichLabel { - id: subtitle - textFormat: Text.StyledText - font.pixelSize: theme.fontSize.small - verticalAlignment: Qt.AlignVCenter - elide: Text.ElideRight - color: theme.colors.dimText - visible: Layout.maximumHeight > 0 - - Layout.maximumHeight: ! compact && text ? implicitHeight : 0 - Layout.fillWidth: true - Layout.fillHeight: true - - Behavior on Layout.maximumHeight { HNumberAnimation {} } - } - } - } - - - Binding on topPadding { - value: spacing / 4 - when: compact - } - - Binding on bottomPadding { - value: spacing / 4 - when: compact - } - - TapHandler { - acceptedButtons: Qt.LeftButton - onTapped: leftClicked() - onLongPressed: tile.longPressed() - } - - TapHandler { - acceptedButtons: Qt.RightButton - acceptedPointerTypes: PointerDevice.GenericPointer | PointerDevice.Pen - onTapped: { - rightClicked() - if (contextMenu) contextMenuLoader.active = true - } - } - - TapHandler { - acceptedPointerTypes: PointerDevice.Finger | PointerDevice.Pen - onLongPressed: { - rightClicked() - if (contextMenu) contextMenuLoader.active = true - } - } - - Connections { - enabled: contextMenuLoader.status === Loader.Ready - target: contextMenuLoader.item - onClosed: contextMenuLoader.active = false - } - - HLoader { - id: contextMenuLoader - active: false - onLoaded: item.popup() - } -} diff --git a/src/gui/Base/HTile/ContentRow.qml b/src/gui/Base/HTile/ContentRow.qml new file mode 100644 index 00000000..08c63248 --- /dev/null +++ b/src/gui/Base/HTile/ContentRow.qml @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick 2.12 +import ".." + +HRowLayout { + spacing: tile.spacing + opacity: tile.contentOpacity + + + property HTile tile +} diff --git a/src/gui/Base/HTile/HTile.qml b/src/gui/Base/HTile/HTile.qml new file mode 100644 index 00000000..23c13285 --- /dev/null +++ b/src/gui/Base/HTile/HTile.qml @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick 2.12 +import ".." + +HButton { + id: tile + + + signal leftClicked() + signal rightClicked() + signal longPressed() + + + property bool compact: window.settings.compactMode + property real contentOpacity: 1 + + property alias contextMenu: contextMenuLoader.sourceComponent + + + Binding on topPadding { + value: spacing / 4 + when: compact + } + + Binding on bottomPadding { + value: spacing / 4 + when: compact + } + + TapHandler { + acceptedButtons: Qt.LeftButton + onTapped: leftClicked() + onLongPressed: tile.longPressed() + } + + TapHandler { + acceptedButtons: Qt.RightButton + acceptedPointerTypes: PointerDevice.GenericPointer | PointerDevice.Pen + onTapped: { + rightClicked() + if (contextMenu) contextMenuLoader.active = true + } + } + + TapHandler { + acceptedPointerTypes: PointerDevice.Finger | PointerDevice.Pen + onLongPressed: { + rightClicked() + if (contextMenu) contextMenuLoader.active = true + } + } + + Connections { + enabled: contextMenuLoader.status === Loader.Ready + target: contextMenuLoader.item + onClosed: contextMenuLoader.active = false + } + + HLoader { + id: contextMenuLoader + active: false + onLoaded: item.popup() + } +} diff --git a/src/gui/Base/HTileDelegate.qml b/src/gui/Base/HTile/HTileDelegate.qml similarity index 97% rename from src/gui/Base/HTileDelegate.qml rename to src/gui/Base/HTile/HTileDelegate.qml index ba9c6967..cf0ec20c 100644 --- a/src/gui/Base/HTileDelegate.qml +++ b/src/gui/Base/HTile/HTileDelegate.qml @@ -2,6 +2,7 @@ import QtQuick 2.12 import QtQuick.Layouts 1.12 +import ".." HTile { id: tile diff --git a/src/gui/Base/HTile/SubtitleLabel.qml b/src/gui/Base/HTile/SubtitleLabel.qml new file mode 100644 index 00000000..2db578c3 --- /dev/null +++ b/src/gui/Base/HTile/SubtitleLabel.qml @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import ".." + +HRichLabel { + textFormat: Text.StyledText + font.pixelSize: theme.fontSize.small + verticalAlignment: Qt.AlignVCenter + elide: Text.ElideRight + color: theme.colors.dimText + visible: Layout.maximumHeight > 0 + + Layout.maximumHeight: ! tile.compact && text ? implicitHeight : 0 + Layout.fillWidth: true + Layout.fillHeight: true + + + property HTile tile + + + Behavior on Layout.maximumHeight { HNumberAnimation {} } +} diff --git a/src/gui/Base/HTile/TitleLabel.qml b/src/gui/Base/HTile/TitleLabel.qml new file mode 100644 index 00000000..bb9f8b95 --- /dev/null +++ b/src/gui/Base/HTile/TitleLabel.qml @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import ".." + +HLabel { + elide: Text.ElideRight + verticalAlignment: Qt.AlignVCenter + + Layout.fillWidth: true + Layout.fillHeight: true +} diff --git a/src/gui/Base/HTile/TitleRightInfoLabel.qml b/src/gui/Base/HTile/TitleRightInfoLabel.qml new file mode 100644 index 00000000..bdd1a6ca --- /dev/null +++ b/src/gui/Base/HTile/TitleRightInfoLabel.qml @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import ".." + +HLabel { + font.pixelSize: theme.fontSize.small + verticalAlignment: Qt.AlignVCenter + color: theme.colors.halfDimText + visible: Layout.maximumWidth > 0 + + Layout.fillHeight: true + Layout.maximumWidth: + text && tile.width >= 200 * theme.uiScale ? + implicitWidth : 0 + + + property HTile tile + + + Behavior on Layout.maximumWidth { HNumberAnimation {} } +} diff --git a/src/gui/MainPane/Account.qml b/src/gui/MainPane/Account.qml index 08f70e0e..cea11ac8 100644 --- a/src/gui/MainPane/Account.qml +++ b/src/gui/MainPane/Account.qml @@ -4,6 +4,7 @@ import QtQuick 2.12 import QtQuick.Layouts 1.12 import Clipboard 0.1 import "../Base" +import "../Base/HTile" HTileDelegate { id: account @@ -12,91 +13,35 @@ HTileDelegate { opacity: collapsed && ! mainPane.filter ? theme.mainPane.listView.account.collapsedOpacity : 1 - title.text: model.display_name || model.id - title.font.pixelSize: theme.fontSize.big - title.color: - hovered ? - utils.nameColor(model.display_name || model.id.substring(1)) : - theme.mainPane.listView.account.name + contentItem: ContentRow { + tile: account - image: HUserAvatar { - userId: model.id - displayName: model.display_name - mxc: model.avatar_url - compact: account.compact + HUserAvatar { + id: avatar + userId: model.id + displayName: model.display_name + mxc: model.avatar_url + compact: account.compact - radius: - mainPane.small ? - theme.mainPane.listView.account.collapsedAvatarRadius : - theme.mainPane.listView.account.avatarRadius + radius: + mainPane.small ? + theme.mainPane.listView.account.collapsedAvatarRadius : + theme.mainPane.listView.account.avatarRadius - Behavior on radius { HNumberAnimation {} } - } - - contextMenu: HMenu { - HMenuItem { - icon.name: "copy-user-id" - text: qsTr("Copy user ID") - onTriggered: Clipboard.text = model.id + Behavior on radius { HNumberAnimation {} } } - HMenuItemPopupSpawner { - icon.name: "sign-out" - icon.color: theme.colors.negativeBackground - text: qsTr("Sign out") + TitleLabel { + text: model.display_name || model.id + font.pixelSize: theme.fontSize.big + color: + hovered ? + utils.nameColor(model.display_name || model.id.substring(1)) : + theme.mainPane.listView.account.name - popup: "Popups/SignOutPopup.qml" - properties: { "userId": model.id } + Behavior on color { HColorAnimation {} } } - } - onActivated: { - pageLoader.showPage( - "AccountSettings/AccountSettings", { "userId": model.id } - ) - mainPaneList.detachedCurrentIndex = false - mainPaneList.centerToHighlight = false - } - - - readonly property alias addChat: addChat - - readonly property bool collapsed: - (window.uiState.collapseAccounts[model.id] || false) && - ! mainPane.filter - - - function setCollapse(collapse) { - window.uiState.collapseAccounts[model.id] = collapse - window.uiStateChanged() - } - - function toggleCollapse() { - setCollapse(! collapsed) - } - - - Behavior on title.color { HColorAnimation {} } - Behavior on opacity { HNumberAnimation {} } - Behavior on leftPadding { HNumberAnimation {} } - Behavior on topPadding { HNumberAnimation {} } - - Binding on leftPadding { - value: (mainPane.minimumSize - loadedImage.width) / 2 - when: mainPane.small - } - - Binding on topPadding { - value: theme.spacing - when: mainPane.small - } - - Binding on bottomPadding { - value: theme.spacing - when: mainPane.small - } - - HRowLayout { HButton { id: addChat iconItem.small: true @@ -154,4 +99,66 @@ HTileDelegate { Behavior on opacity { HNumberAnimation {} } } } + + contextMenu: HMenu { + HMenuItem { + icon.name: "copy-user-id" + text: qsTr("Copy user ID") + onTriggered: Clipboard.text = model.id + } + + HMenuItemPopupSpawner { + icon.name: "sign-out" + icon.color: theme.colors.negativeBackground + text: qsTr("Sign out") + + popup: "Popups/SignOutPopup.qml" + properties: { "userId": model.id } + } + } + + onActivated: { + pageLoader.showPage( + "AccountSettings/AccountSettings", { "userId": model.id } + ) + mainPaneList.detachedCurrentIndex = false + mainPaneList.centerToHighlight = false + } + + + readonly property alias addChat: addChat + + readonly property bool collapsed: + (window.uiState.collapseAccounts[model.id] || false) && + ! mainPane.filter + + + function setCollapse(collapse) { + window.uiState.collapseAccounts[model.id] = collapse + window.uiStateChanged() + } + + function toggleCollapse() { + setCollapse(! collapsed) + } + + + Behavior on opacity { HNumberAnimation {} } + Behavior on leftPadding { HNumberAnimation {} } + Behavior on topPadding { HNumberAnimation {} } + + Binding on leftPadding { + value: (mainPane.minimumSize - avatar.width) / 2 + when: mainPane.small + } + + Binding on topPadding { + value: theme.spacing + when: mainPane.small + } + + Binding on bottomPadding { + value: theme.spacing + when: mainPane.small + } } diff --git a/src/gui/MainPane/Room.qml b/src/gui/MainPane/Room.qml index 3b504081..0b14bc73 100644 --- a/src/gui/MainPane/Room.qml +++ b/src/gui/MainPane/Room.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts 1.12 import Clipboard 0.1 import ".." import "../Base" +import "../Base/HTile" HTileDelegate { id: room @@ -16,94 +17,118 @@ HTileDelegate { leftPadding: theme.spacing * 2 rightPadding: theme.spacing - image: HRoomAvatar { - roomId: model.id - displayName: model.display_name - mxc: model.avatar_url - compact: room.compact + contentItem: ContentRow { + tile: room - radius: - mainPane.small ? - theme.mainPane.listView.room.collapsedAvatarRadius : - theme.mainPane.listView.room.avatarRadius + HRoomAvatar { + id: avatar + roomId: model.id + displayName: model.display_name + mxc: model.avatar_url + compact: room.compact - Behavior on radius { HNumberAnimation {} } - } + radius: + mainPane.small ? + theme.mainPane.listView.room.collapsedAvatarRadius : + theme.mainPane.listView.room.avatarRadius - title.color: theme.mainPane.listView.room.name - title.text: model.display_name || qsTr("Empty room") - - additionalInfo.children: [ - // HLabel { - // text: model.mentions - // font.pixelSize: theme.fontSize.small - // verticalAlignment: Qt.AlignVCenter - // leftPadding: theme.spacing / 4 - // rightPadding: leftPadding - - // scale: model.mentions === 0 ? 0 : 1 - // visible: scale > 0 - - // background: Rectangle { - // color: theme.colors.alertBackground - // radius: theme.radius / 4 - // } - - // Behavior on scale { HNumberAnimation {} } - // }, - - HIcon { - svgName: "invite-received" - colorize: theme.colors.alertBackground - small: room.compact - visible: invited - - Layout.maximumWidth: invited ? implicitWidth : 0 - - Behavior on Layout.maximumWidth { HNumberAnimation {} } + Behavior on radius { HNumberAnimation {} } } - ] - subtitle.color: theme.mainPane.listView.room.subtitle - subtitle.textFormat: Text.StyledText - subtitle.font.italic: - lastEvent && lastEvent.event_type === "RoomMessageEmote" - subtitle.text: { - if (! lastEvent) return "" + HColumnLayout { + HRowLayout { + spacing: room.spacing - const isEmote = lastEvent.event_type === "RoomMessageEmote" - const isMsg = lastEvent.event_type.startsWith("RoomMessage") - const isUnknownMsg = lastEvent.event_type === "RoomMessageUnknown" - const isCryptMedia = lastEvent.event_type.startsWith("RoomEncrypted") + TitleLabel { + text: model.display_name || qsTr("Empty room") + color: theme.mainPane.listView.room.name + } - // If it's a general event - if (isEmote || isUnknownMsg || (! isMsg && ! isCryptMedia)) - return utils.processedEventText(lastEvent) + // HLabel { + // text: model.mentions + // font.pixelSize: theme.fontSize.small + // verticalAlignment: Qt.AlignVCenter + // leftPadding: theme.spacing / 4 + // rightPadding: leftPadding - const text = utils.coloredNameHtml( - lastEvent.sender_name, lastEvent.sender_id - ) + ": " + lastEvent.inline_content + // scale: model.mentions === 0 ? 0 : 1 + // visible: scale > 0 - return text.replace( - /< *span +class=['"]?quote['"]? *>(.+?)<\/ *span *>/g, - `` + - `$1`, - ) - } + // background: Rectangle { + // color: theme.colors.alertBackground + // radius: theme.radius / 4 + // } - rightInfo.color: theme.mainPane.listView.room.lastEventDate - rightInfo.text: { - model.last_event_date < new Date(1) ? - "" : + // Behavior on scale { HNumberAnimation {} } + // }, - utils.dateIsToday(model.last_event_date) ? - utils.formatTime(model.last_event_date, false) : // no seconds + HIcon { + svgName: "invite-received" + colorize: theme.colors.alertBackground + small: room.compact + visible: invited - model.last_event_date.getFullYear() === new Date().getFullYear() ? - Qt.formatDate(model.last_event_date, "d MMM") : // e.g. "5 Dec" + Layout.maximumWidth: invited ? implicitWidth : 0 - // model.last_event_date.getFullYear() ? - Qt.formatDate(model.last_event_date, "MMM yyyy") // e.g. "Jan 2020" + Behavior on Layout.maximumWidth { HNumberAnimation {} } + } + + TitleRightInfoLabel { + tile: room + color: theme.mainPane.listView.room.lastEventDate + text: { + model.last_event_date < new Date(1) ? + "" : + + // e.g. "03:24" + utils.dateIsToday(model.last_event_date) ? + utils.formatTime(model.last_event_date, false) : + + // e.g. "5 Dec" + model.last_event_date.getFullYear() === + new Date().getFullYear() ? + Qt.formatDate(model.last_event_date, "d MMM") : + + // e.g. "Jan 2020" + Qt.formatDate(model.last_event_date, "MMM yyyy") + } + } + } + + SubtitleLabel { + tile: room + color: theme.mainPane.listView.room.subtitle + textFormat: Text.StyledText + font.italic: + lastEvent && lastEvent.event_type === "RoomMessageEmote" + + text: { + if (! lastEvent) return "" + + const ev_type = lastEvent.event_type + const isEmote = ev_type === "RoomMessageEmote" + const isMsg = ev_type.startsWith("RoomMessage") + const isUnknownMsg = ev_type === "RoomMessageUnknown" + const isCryptMedia = ev_type.startsWith("RoomEncrypted") + + // If it's a general event + if (isEmote || isUnknownMsg || (! isMsg && ! isCryptMedia)) + return utils.processedEventText(lastEvent) + + const text = utils.coloredNameHtml( + lastEvent.sender_name, lastEvent.sender_id + ) + ": " + lastEvent.inline_content + + const subColor = theme.mainPane.listView.room.subtitleQuote + + return text.replace( + /< *span +class=['"]?quote['"]? *>(.+?)<\/ *span *>/g, + `` + + `$1`, + ) + } + } + } } contextMenu: HMenu { @@ -194,7 +219,7 @@ HTileDelegate { Behavior on leftPadding { HNumberAnimation {} } Binding on leftPadding { - value: (mainPane.minimumSize - loadedImage.width) / 2 + value: (mainPane.minimumSize - avatar.width) / 2 when: mainPane.small } diff --git a/src/gui/Pages/Chat/RoomPane/MemberDelegate.qml b/src/gui/Pages/Chat/RoomPane/MemberDelegate.qml index bdc8776c..f8d43bee 100644 --- a/src/gui/Pages/Chat/RoomPane/MemberDelegate.qml +++ b/src/gui/Pages/Chat/RoomPane/MemberDelegate.qml @@ -3,32 +3,49 @@ import QtQuick 2.12 import Clipboard 0.1 import "../../../Base" +import "../../../Base/HTile" HTileDelegate { - id: memberDelegate + id: member backgroundColor: theme.chat.roomPane.listView.member.background contentOpacity: model.invited ? theme.chat.roomPane.listView.member.invitedOpacity : 1 - image: HUserAvatar { - userId: model.id - displayName: model.display_name - mxc: model.avatar_url - powerLevel: model.power_level - shiftMembershipIconPosition: ! roomPane.collapsed - invited: model.invited - compact: memberDelegate.compact + contentItem: ContentRow { + tile: member + + HUserAvatar { + id: avatar + userId: model.id + displayName: model.display_name + mxc: model.avatar_url + powerLevel: model.power_level + shiftMembershipIconPosition: ! roomPane.collapsed + invited: model.invited + compact: member.compact + } + + HColumnLayout { + TitleLabel { + text: model.display_name || model.id + color: + member.hovered ? + utils.nameColor( + model.display_name || model.id.substring(1) + ) : + theme.chat.roomPane.listView.member.name + + Behavior on color { HColorAnimation {} } + } + + SubtitleLabel { + tile: member + text: model.display_name ? model.id : "" + color: theme.chat.roomPane.listView.member.subtitle + } + } } - title.text: model.display_name || model.id - title.color: - memberDelegate.hovered ? - utils.nameColor(model.display_name || model.id.substring(1)) : - theme.chat.roomPane.listView.member.name - - subtitle.text: model.display_name ? model.id : "" - subtitle.color: theme.chat.roomPane.listView.member.subtitle - contextMenu: HMenu { HMenuItem { icon.name: "copy-user-id" @@ -38,13 +55,11 @@ HTileDelegate { } - Behavior on title.color { HColorAnimation {} } Behavior on contentOpacity { HNumberAnimation {} } Behavior on spacing { HNumberAnimation {} } Binding on spacing { - value: (roomPane.minimumSize - loadedImage.width) / 2 - when: loadedImage && - roomPane.width < loadedImage.width + theme.spacing * 2 + value: (roomPane.minimumSize - avatar.width) / 2 + when: avatar && roomPane.width < avatar.width + theme.spacing * 2 } } diff --git a/src/gui/Pages/Chat/Timeline/EventFile.qml b/src/gui/Pages/Chat/Timeline/EventFile.qml index 6742af3f..950b15f2 100644 --- a/src/gui/Pages/Chat/Timeline/EventFile.qml +++ b/src/gui/Pages/Chat/Timeline/EventFile.qml @@ -4,6 +4,7 @@ import QtQuick 2.12 import QtQuick.Layouts 1.12 import CppUtils 0.1 import "../../../Base" +import "../../../Base/HTile" HTile { id: file @@ -14,12 +15,27 @@ HTile { ) height: Math.max(theme.chat.message.avatarSize, implicitHeight) - title.text: loader.singleMediaInfo.media_title || qsTr("Untitled file") - title.elide: Text.ElideMiddle - subtitle.text: CppUtils.formattedBytes(loader.singleMediaInfo.media_size) + contentItem: ContentRow { + tile: file - image: HIcon { - svgName: "download" + HIcon { + svgName: "download" + } + + HColumnLayout { + TitleLabel { + elide: Text.ElideMiddle + text: loader.singleMediaInfo.media_title || + qsTr("Untitled file") + } + + SubtitleLabel { + tile: file + text: CppUtils.formattedBytes( + loader.singleMediaInfo.media_size, + ) + } + } } onRightClicked: eventDelegate.openContextMenu()