Rewrite HTile and adapt components using it

Rewrite HTile in a more standard way, hopefully fixing the
mysterious segfault on some systems
This commit is contained in:
miruka 2020-03-30 15:03:35 -04:00
parent af57218ac6
commit ad937573cf
11 changed files with 381 additions and 321 deletions

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@
import QtQuick 2.12 import QtQuick 2.12
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import ".."
HTile { HTile {
id: tile id: tile

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@ import QtQuick 2.12
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import Clipboard 0.1 import Clipboard 0.1
import "../Base" import "../Base"
import "../Base/HTile"
HTileDelegate { HTileDelegate {
id: account id: account
@ -12,14 +13,11 @@ HTileDelegate {
opacity: collapsed && ! mainPane.filter ? opacity: collapsed && ! mainPane.filter ?
theme.mainPane.listView.account.collapsedOpacity : 1 theme.mainPane.listView.account.collapsedOpacity : 1
title.text: model.display_name || model.id contentItem: ContentRow {
title.font.pixelSize: theme.fontSize.big tile: account
title.color:
hovered ?
utils.nameColor(model.display_name || model.id.substring(1)) :
theme.mainPane.listView.account.name
image: HUserAvatar { HUserAvatar {
id: avatar
userId: model.id userId: model.id
displayName: model.display_name displayName: model.display_name
mxc: model.avatar_url mxc: model.avatar_url
@ -33,70 +31,17 @@ HTileDelegate {
Behavior on radius { HNumberAnimation {} } Behavior on radius { HNumberAnimation {} }
} }
contextMenu: HMenu { TitleLabel {
HMenuItem { text: model.display_name || model.id
icon.name: "copy-user-id" font.pixelSize: theme.fontSize.big
text: qsTr("Copy user ID") color:
onTriggered: Clipboard.text = model.id hovered ?
utils.nameColor(model.display_name || model.id.substring(1)) :
theme.mainPane.listView.account.name
Behavior on color { HColorAnimation {} }
} }
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 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 { HButton {
id: addChat id: addChat
iconItem.small: true iconItem.small: true
@ -154,4 +99,66 @@ HTileDelegate {
Behavior on opacity { HNumberAnimation {} } 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
}
} }

View File

@ -5,6 +5,7 @@ import QtQuick.Layouts 1.12
import Clipboard 0.1 import Clipboard 0.1
import ".." import ".."
import "../Base" import "../Base"
import "../Base/HTile"
HTileDelegate { HTileDelegate {
id: room id: room
@ -16,7 +17,11 @@ HTileDelegate {
leftPadding: theme.spacing * 2 leftPadding: theme.spacing * 2
rightPadding: theme.spacing rightPadding: theme.spacing
image: HRoomAvatar { contentItem: ContentRow {
tile: room
HRoomAvatar {
id: avatar
roomId: model.id roomId: model.id
displayName: model.display_name displayName: model.display_name
mxc: model.avatar_url mxc: model.avatar_url
@ -30,10 +35,15 @@ HTileDelegate {
Behavior on radius { HNumberAnimation {} } Behavior on radius { HNumberAnimation {} }
} }
title.color: theme.mainPane.listView.room.name HColumnLayout {
title.text: model.display_name || qsTr("Empty room") HRowLayout {
spacing: room.spacing
TitleLabel {
text: model.display_name || qsTr("Empty room")
color: theme.mainPane.listView.room.name
}
additionalInfo.children: [
// HLabel { // HLabel {
// text: model.mentions // text: model.mentions
// font.pixelSize: theme.fontSize.small // font.pixelSize: theme.fontSize.small
@ -62,19 +72,44 @@ HTileDelegate {
Behavior on Layout.maximumWidth { HNumberAnimation {} } Behavior on Layout.maximumWidth { HNumberAnimation {} }
} }
]
subtitle.color: theme.mainPane.listView.room.subtitle TitleRightInfoLabel {
subtitle.textFormat: Text.StyledText tile: room
subtitle.font.italic: 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" lastEvent && lastEvent.event_type === "RoomMessageEmote"
subtitle.text: {
text: {
if (! lastEvent) return "" if (! lastEvent) return ""
const isEmote = lastEvent.event_type === "RoomMessageEmote" const ev_type = lastEvent.event_type
const isMsg = lastEvent.event_type.startsWith("RoomMessage") const isEmote = ev_type === "RoomMessageEmote"
const isUnknownMsg = lastEvent.event_type === "RoomMessageUnknown" const isMsg = ev_type.startsWith("RoomMessage")
const isCryptMedia = lastEvent.event_type.startsWith("RoomEncrypted") const isUnknownMsg = ev_type === "RoomMessageUnknown"
const isCryptMedia = ev_type.startsWith("RoomEncrypted")
// If it's a general event // If it's a general event
if (isEmote || isUnknownMsg || (! isMsg && ! isCryptMedia)) if (isEmote || isUnknownMsg || (! isMsg && ! isCryptMedia))
@ -84,26 +119,16 @@ HTileDelegate {
lastEvent.sender_name, lastEvent.sender_id lastEvent.sender_name, lastEvent.sender_id
) + ": " + lastEvent.inline_content ) + ": " + lastEvent.inline_content
const subColor = theme.mainPane.listView.room.subtitleQuote
return text.replace( return text.replace(
/< *span +class=['"]?quote['"]? *>(.+?)<\/ *span *>/g, /< *span +class=['"]?quote['"]? *>(.+?)<\/ *span *>/g,
`<font color="${theme.mainPane.listView.room.subtitleQuote}">` + `<font color="${subColor}">` +
`$1</font>`, `$1</font>`,
) )
} }
}
rightInfo.color: theme.mainPane.listView.room.lastEventDate }
rightInfo.text: {
model.last_event_date < new Date(1) ?
"" :
utils.dateIsToday(model.last_event_date) ?
utils.formatTime(model.last_event_date, false) : // no seconds
model.last_event_date.getFullYear() === new Date().getFullYear() ?
Qt.formatDate(model.last_event_date, "d MMM") : // e.g. "5 Dec"
// model.last_event_date.getFullYear() ?
Qt.formatDate(model.last_event_date, "MMM yyyy") // e.g. "Jan 2020"
} }
contextMenu: HMenu { contextMenu: HMenu {
@ -194,7 +219,7 @@ HTileDelegate {
Behavior on leftPadding { HNumberAnimation {} } Behavior on leftPadding { HNumberAnimation {} }
Binding on leftPadding { Binding on leftPadding {
value: (mainPane.minimumSize - loadedImage.width) / 2 value: (mainPane.minimumSize - avatar.width) / 2
when: mainPane.small when: mainPane.small
} }

View File

@ -3,31 +3,48 @@
import QtQuick 2.12 import QtQuick 2.12
import Clipboard 0.1 import Clipboard 0.1
import "../../../Base" import "../../../Base"
import "../../../Base/HTile"
HTileDelegate { HTileDelegate {
id: memberDelegate id: member
backgroundColor: theme.chat.roomPane.listView.member.background backgroundColor: theme.chat.roomPane.listView.member.background
contentOpacity: contentOpacity:
model.invited ? theme.chat.roomPane.listView.member.invitedOpacity : 1 model.invited ? theme.chat.roomPane.listView.member.invitedOpacity : 1
image: HUserAvatar { contentItem: ContentRow {
tile: member
HUserAvatar {
id: avatar
userId: model.id userId: model.id
displayName: model.display_name displayName: model.display_name
mxc: model.avatar_url mxc: model.avatar_url
powerLevel: model.power_level powerLevel: model.power_level
shiftMembershipIconPosition: ! roomPane.collapsed shiftMembershipIconPosition: ! roomPane.collapsed
invited: model.invited invited: model.invited
compact: memberDelegate.compact compact: member.compact
} }
title.text: model.display_name || model.id HColumnLayout {
title.color: TitleLabel {
memberDelegate.hovered ? text: model.display_name || model.id
utils.nameColor(model.display_name || model.id.substring(1)) : color:
member.hovered ?
utils.nameColor(
model.display_name || model.id.substring(1)
) :
theme.chat.roomPane.listView.member.name theme.chat.roomPane.listView.member.name
subtitle.text: model.display_name ? model.id : "" Behavior on color { HColorAnimation {} }
subtitle.color: theme.chat.roomPane.listView.member.subtitle }
SubtitleLabel {
tile: member
text: model.display_name ? model.id : ""
color: theme.chat.roomPane.listView.member.subtitle
}
}
}
contextMenu: HMenu { contextMenu: HMenu {
HMenuItem { HMenuItem {
@ -38,13 +55,11 @@ HTileDelegate {
} }
Behavior on title.color { HColorAnimation {} }
Behavior on contentOpacity { HNumberAnimation {} } Behavior on contentOpacity { HNumberAnimation {} }
Behavior on spacing { HNumberAnimation {} } Behavior on spacing { HNumberAnimation {} }
Binding on spacing { Binding on spacing {
value: (roomPane.minimumSize - loadedImage.width) / 2 value: (roomPane.minimumSize - avatar.width) / 2
when: loadedImage && when: avatar && roomPane.width < avatar.width + theme.spacing * 2
roomPane.width < loadedImage.width + theme.spacing * 2
} }
} }

View File

@ -4,6 +4,7 @@ import QtQuick 2.12
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import CppUtils 0.1 import CppUtils 0.1
import "../../../Base" import "../../../Base"
import "../../../Base/HTile"
HTile { HTile {
id: file id: file
@ -14,14 +15,29 @@ HTile {
) )
height: Math.max(theme.chat.message.avatarSize, implicitHeight) height: Math.max(theme.chat.message.avatarSize, implicitHeight)
title.text: loader.singleMediaInfo.media_title || qsTr("Untitled file") contentItem: ContentRow {
title.elide: Text.ElideMiddle tile: file
subtitle.text: CppUtils.formattedBytes(loader.singleMediaInfo.media_size)
image: HIcon { HIcon {
svgName: "download" 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() onRightClicked: eventDelegate.openContextMenu()
onLeftClicked: onLeftClicked:
eventList.selectedCount ? eventList.selectedCount ?