Try out DelegateChooser

requires Qt5.12+
This commit is contained in:
Nicolas Werner 2019-10-08 20:55:09 +02:00
parent 7f41752165
commit b9076c5c4d
9 changed files with 189 additions and 157 deletions

View file

@ -3,9 +3,12 @@ import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import Qt.labs.qmlmodels 1.0
import com.github.nheko 1.0 import com.github.nheko 1.0
import "./delegates"
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
@ -77,157 +80,46 @@ Rectangle {
onMovementEnded: updatePosition() onMovementEnded: updatePosition()
spacing: 4 spacing: 4
delegate: RowLayout { delegate: DelegateChooser {
anchors.leftMargin: avatarSize + 4 role: "type"
anchors.left: parent.left DelegateChoice {
anchors.right: parent.right roleValue: MtxEvent.TextMessage
anchors.rightMargin: scrollbar.width TimelineRow { view: chat; TextMessage { id: kid } }
function isFullyVisible() {
return (y - chat.contentY - 1) + height < chat.height
} }
function getIndex() { DelegateChoice {
return index; roleValue: MtxEvent.NoticeMessage
TimelineRow { view: chat; NoticeMessage { id: kid } }
} }
DelegateChoice {
Loader { roleValue: MtxEvent.EmoteMessage
id: loader TimelineRow { view: chat; TextMessage { id: kid } }
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
height: item.height
source: switch(model.type) {
//case MtxEvent.Aliases: return "delegates/Aliases.qml"
//case MtxEvent.Avatar: return "delegates/Avatar.qml"
//case MtxEvent.CanonicalAlias: return "delegates/CanonicalAlias.qml"
//case MtxEvent.Create: return "delegates/Create.qml"
//case MtxEvent.GuestAccess: return "delegates/GuestAccess.qml"
//case MtxEvent.HistoryVisibility: return "delegates/HistoryVisibility.qml"
//case MtxEvent.JoinRules: return "delegates/JoinRules.qml"
//case MtxEvent.Member: return "delegates/Member.qml"
//case MtxEvent.Name: return "delegates/Name.qml"
//case MtxEvent.PowerLevels: return "delegates/PowerLevels.qml"
//case MtxEvent.Topic: return "delegates/Topic.qml"
case MtxEvent.NoticeMessage: return "delegates/NoticeMessage.qml"
case MtxEvent.TextMessage: return "delegates/TextMessage.qml"
case MtxEvent.EmoteMessage: return "delegates/TextMessage.qml"
case MtxEvent.ImageMessage: return "delegates/ImageMessage.qml"
case MtxEvent.Sticker: return "delegates/ImageMessage.qml"
case MtxEvent.FileMessage: return "delegates/FileMessage.qml"
case MtxEvent.VideoMessage: return "delegates/PlayableMediaMessage.qml"
case MtxEvent.AudioMessage: return "delegates/PlayableMediaMessage.qml"
case MtxEvent.Redacted: return "delegates/Redacted.qml"
default: return "delegates/placeholder.qml"
} }
property variant eventData: model DelegateChoice {
roleValue: MtxEvent.ImageMessage
TimelineRow { view: chat; ImageMessage { id: kid } }
} }
DelegateChoice {
StatusIndicator { roleValue: MtxEvent.Sticker
state: model.state TimelineRow { view: chat; ImageMessage { id: kid } }
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
} }
DelegateChoice {
EncryptionIndicator { roleValue: MtxEvent.FileMessage
visible: model.isEncrypted TimelineRow { view: chat; FileMessage { id: kid } }
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
} }
DelegateChoice {
Button { roleValue: MtxEvent.VideoMessage
Layout.alignment: Qt.AlignRight | Qt.AlignTop TimelineRow { view: chat; PlayableMediaMessage { id: kid } }
id: replyButton
flat: true
Layout.preferredHeight: 16
ToolTip.visible: hovered
ToolTip.text: qsTr("Reply")
// disable background, because we don't want a border on hover
background: Item {
} }
DelegateChoice {
Image { roleValue: MtxEvent.AudioMessage
id: replyButtonImg TimelineRow { view: chat; PlayableMediaMessage { id: kid } }
// Workaround, can't get icon.source working for now...
anchors.fill: parent
source: "qrc:/icons/icons/ui/mail-reply.png"
}
ColorOverlay {
anchors.fill: replyButtonImg
source: replyButtonImg
color: replyButton.hovered ? colors.highlight : colors.buttonText
}
onClicked: chat.model.replyAction(model.id)
}
Button {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
id: optionsButton
flat: true
Layout.preferredHeight: 16
ToolTip.visible: hovered
ToolTip.text: qsTr("Options")
// disable background, because we don't want a border on hover
background: Item {
}
Image {
id: optionsButtonImg
// Workaround, can't get icon.source working for now...
anchors.fill: parent
source: "qrc:/icons/icons/ui/vertical-ellipsis.png"
}
ColorOverlay {
anchors.fill: optionsButtonImg
source: optionsButtonImg
color: optionsButton.hovered ? colors.highlight : colors.buttonText
}
onClicked: contextMenu.open()
Menu {
y: optionsButton.height
id: contextMenu
MenuItem {
text: qsTr("Read receipts")
onTriggered: chat.model.readReceiptsAction(model.id)
}
MenuItem {
text: qsTr("Mark as read")
}
MenuItem {
text: qsTr("View raw message")
onTriggered: chat.model.viewRawMessage(model.id)
}
MenuItem {
text: qsTr("Redact message")
onTriggered: chat.model.redactEvent(model.id)
}
MenuItem {
visible: model.type == MtxEvent.ImageMessage || model.type == MtxEvent.VideoMessage || model.type == MtxEvent.AudioMessage || model.type == MtxEvent.FileMessage || model.type == MtxEvent.Sticker
text: qsTr("Save as")
onTriggered: timelineManager.saveMedia(model.url, model.filename, model.mimetype, model.type)
} }
DelegateChoice {
roleValue: MtxEvent.Redacted
TimelineRow { view: chat; Redacted { id: kid } }
} }
} }
Text {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
text: model.timestamp.toLocaleTimeString("HH:mm")
color: inactiveColors.text
ToolTip.visible: ma.containsMouse
ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
MouseArea{
id: ma
anchors.fill: parent
hoverEnabled: true
}
}
}
section { section {
property: "section" property: "section"

View file

@ -31,7 +31,7 @@ Rectangle {
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: timelineManager.saveMedia(eventData.url, eventData.filename, eventData.mimetype, eventData.type) onClicked: timelineManager.saveMedia(model.url, model.filename, model.mimetype, model.type)
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
} }
} }
@ -40,14 +40,14 @@ Rectangle {
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
text: eventData.body text: model.body
textFormat: Text.PlainText textFormat: Text.PlainText
elide: Text.ElideRight elide: Text.ElideRight
color: colors.text color: colors.text
} }
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
text: eventData.filesize text: model.filesize
textFormat: Text.PlainText textFormat: Text.PlainText
elide: Text.ElideRight elide: Text.ElideRight
color: colors.text color: colors.text

View file

@ -4,20 +4,20 @@ import com.github.nheko 1.0
Item { Item {
width: 300 width: 300
height: 300 * eventData.proportionalHeight height: 300 * model.proportionalHeight
Image { Image {
id: img id: img
anchors.fill: parent anchors.fill: parent
source: eventData.url.replace("mxc://", "image://MxcImage/") source: model.url.replace("mxc://", "image://MxcImage/")
asynchronous: true asynchronous: true
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
MouseArea { MouseArea {
enabled: eventData.type == MtxEvent.ImageMessage enabled: model.type == MtxEvent.ImageMessage
anchors.fill: parent anchors.fill: parent
onClicked: timelineManager.openImageOverlay(eventData.url, eventData.filename, eventData.mimetype, eventData.type) onClicked: timelineManager.openImageOverlay(model.url, model.filename, model.mimetype, model.type)
} }
} }
} }

View file

@ -1,7 +1,7 @@
import QtQuick 2.5 import QtQuick 2.5
TextEdit { TextEdit {
text: eventData.formattedBody text: model.formattedBody
textFormat: TextEdit.RichText textFormat: TextEdit.RichText
readOnly: true readOnly: true
wrapMode: Text.Wrap wrapMode: Text.Wrap

View file

@ -17,7 +17,7 @@ Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
VideoOutput { VideoOutput {
visible: eventData.type == MtxEvent.VideoMessage visible: model.type == MtxEvent.VideoMessage
Layout.maximumHeight: 300 Layout.maximumHeight: 300
Layout.minimumHeight: 300 Layout.minimumHeight: 300
Layout.maximumWidth: 500 Layout.maximumWidth: 500
@ -85,7 +85,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
switch (button.state) { switch (button.state) {
case "": timelineManager.cacheMedia(eventData.url, eventData.mimetype); break; case "": timelineManager.cacheMedia(model.url, model.mimetype); break;
case "stopped": case "stopped":
media.play(); console.log("play"); media.play(); console.log("play");
button.state = "playing" button.state = "playing"
@ -107,7 +107,7 @@ Rectangle {
Connections { Connections {
target: timelineManager target: timelineManager
onMediaCached: { onMediaCached: {
if (mxcUrl == eventData.url) { if (mxcUrl == model.url) {
media.source = "file://" + cacheUrl media.source = "file://" + cacheUrl
button.state = "stopped" button.state = "stopped"
console.log("media loaded: " + mxcUrl + " at " + cacheUrl) console.log("media loaded: " + mxcUrl + " at " + cacheUrl)
@ -132,14 +132,14 @@ Rectangle {
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
text: eventData.body text: model.body
textFormat: Text.PlainText textFormat: Text.PlainText
elide: Text.ElideRight elide: Text.ElideRight
color: colors.text color: colors.text
} }
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
text: eventData.filesize text: model.filesize
textFormat: Text.PlainText textFormat: Text.PlainText
elide: Text.ElideRight elide: Text.ElideRight
color: colors.text color: colors.text

View file

@ -1,7 +1,7 @@
import QtQuick 2.5 import QtQuick 2.5
TextEdit { TextEdit {
text: eventData.formattedBody text: model.formattedBody
textFormat: TextEdit.RichText textFormat: TextEdit.RichText
readOnly: true readOnly: true
wrapMode: Text.Wrap wrapMode: Text.Wrap

View file

@ -0,0 +1,139 @@
import QtQuick 2.6
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import QtGraphicalEffects 1.0
import QtQuick.Window 2.2
import com.github.nheko 1.0
import ".."
RowLayout {
property var view: undefined
default property alias data: contentItem.data
height: kid.height // TODO: fix this, we shouldn't need to give the child of contentItem this id!
anchors.leftMargin: avatarSize + 4
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: scrollbar.width
function isFullyVisible() {
return (y - view.contentY - 1) + height < view.height
}
function getIndex() {
return index;
}
Item {
id: contentItem
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
}
StatusIndicator {
state: model.state
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
}
EncryptionIndicator {
visible: model.isEncrypted
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: 16
}
Button {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
id: replyButton
flat: true
Layout.preferredHeight: 16
ToolTip.visible: hovered
ToolTip.text: qsTr("Reply")
// disable background, because we don't want a border on hover
background: Item {
}
Image {
id: replyButtonImg
// Workaround, can't get icon.source working for now...
anchors.fill: parent
source: "qrc:/icons/icons/ui/mail-reply.png"
}
ColorOverlay {
anchors.fill: replyButtonImg
source: replyButtonImg
color: replyButton.hovered ? colors.highlight : colors.buttonText
}
onClicked: view.model.replyAction(model.id)
}
Button {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
id: optionsButton
flat: true
Layout.preferredHeight: 16
ToolTip.visible: hovered
ToolTip.text: qsTr("Options")
// disable background, because we don't want a border on hover
background: Item {
}
Image {
id: optionsButtonImg
// Workaround, can't get icon.source working for now...
anchors.fill: parent
source: "qrc:/icons/icons/ui/vertical-ellipsis.png"
}
ColorOverlay {
anchors.fill: optionsButtonImg
source: optionsButtonImg
color: optionsButton.hovered ? colors.highlight : colors.buttonText
}
onClicked: contextMenu.open()
Menu {
y: optionsButton.height
id: contextMenu
MenuItem {
text: qsTr("Read receipts")
onTriggered: view.model.readReceiptsAction(model.id)
}
MenuItem {
text: qsTr("Mark as read")
}
MenuItem {
text: qsTr("View raw message")
onTriggered: view.model.viewRawMessage(model.id)
}
MenuItem {
text: qsTr("Redact message")
onTriggered: view.model.redactEvent(model.id)
}
MenuItem {
visible: model.type == MtxEvent.ImageMessage || model.type == MtxEvent.VideoMessage || model.type == MtxEvent.AudioMessage || model.type == MtxEvent.FileMessage || model.type == MtxEvent.Sticker
text: qsTr("Save as")
onTriggered: timelineManager.saveMedia(model.url, model.filename, model.mimetype, model.type)
}
}
}
Text {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
text: model.timestamp.toLocaleTimeString("HH:mm")
color: inactiveColors.text
ToolTip.visible: ma.containsMouse
ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
MouseArea{
id: ma
anchors.fill: parent
hoverEnabled: true
}
}
}

View file

@ -2,7 +2,7 @@ import QtQuick 2.5
import QtQuick.Controls 2.1 import QtQuick.Controls 2.1
Label { Label {
text: qsTr("unimplemented event: ") + eventData.type text: qsTr("unimplemented event: ") + model.type
textFormat: Text.PlainText textFormat: Text.PlainText
wrapMode: Text.Wrap wrapMode: Text.Wrap
width: parent.width width: parent.width

View file

@ -119,6 +119,7 @@
<file>qml/Avatar.qml</file> <file>qml/Avatar.qml</file>
<file>qml/StatusIndicator.qml</file> <file>qml/StatusIndicator.qml</file>
<file>qml/EncryptionIndicator.qml</file> <file>qml/EncryptionIndicator.qml</file>
<file>qml/delegates/TimelineRow.qml</file>
<file>qml/delegates/TextMessage.qml</file> <file>qml/delegates/TextMessage.qml</file>
<file>qml/delegates/NoticeMessage.qml</file> <file>qml/delegates/NoticeMessage.qml</file>
<file>qml/delegates/ImageMessage.qml</file> <file>qml/delegates/ImageMessage.qml</file>