Better message bubbles.
This commit is contained in:
parent
927a0aa017
commit
50445bccf1
|
@ -15,7 +15,7 @@ import Spectral.Font 0.1
|
||||||
import Spectral.Effect 2.0
|
import Spectral.Effect 2.0
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
readonly property bool avatarVisible: !sentByMe && (aboveAuthor !== author || aboveSection !== section || aboveEventType === "state" || aboveEventType === "emote" || aboveEventType === "other")
|
readonly property bool avatarVisible: !sentByMe && showAuthor
|
||||||
readonly property bool sentByMe: author === currentRoom.localUser
|
readonly property bool sentByMe: author === currentRoom.localUser
|
||||||
|
|
||||||
property bool openOnFinished: false
|
property bool openOnFinished: false
|
||||||
|
@ -39,8 +39,6 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.alignment: sentByMe ? Qt.AlignRight : Qt.AlignLeft
|
|
||||||
|
|
||||||
z: -5
|
z: -5
|
||||||
|
|
||||||
id: messageRow
|
id: messageRow
|
||||||
|
@ -111,7 +109,7 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: progressInfo.active ? (progressInfo.progress + "/" + progressInfo.total) : content.info ? content.info.size : "Unknown"
|
text: progressInfo.active ? (humanSize(progressInfo.progress) + "/" + humanSize(progressInfo.total)) : humanSize(content.info ? content.info.size : 0)
|
||||||
color: MPalette.lighter
|
color: MPalette.lighter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,4 +193,20 @@ ColumnLayout {
|
||||||
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
||||||
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function humanSize(bytes)
|
||||||
|
{
|
||||||
|
if (!bytes)
|
||||||
|
return qsTr("Unknown", "Unknown attachment size")
|
||||||
|
if (bytes < 4000)
|
||||||
|
return qsTr("%1 bytes").arg(bytes)
|
||||||
|
bytes = Math.round(bytes / 100) / 10
|
||||||
|
if (bytes < 2000)
|
||||||
|
return qsTr("%1 KB").arg(bytes)
|
||||||
|
bytes = Math.round(bytes / 100) / 10
|
||||||
|
if (bytes < 2000)
|
||||||
|
return qsTr("%1 MB").arg(bytes)
|
||||||
|
return qsTr("%1 GB").arg(Math.round(bytes / 100) / 10)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import Spectral.Effect 2.0
|
||||||
import Spectral.Font 0.1
|
import Spectral.Font 0.1
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
readonly property bool avatarVisible: !sentByMe && (aboveAuthor !== author || aboveSection !== section || aboveEventType === "state" || aboveEventType === "emote" || aboveEventType === "other")
|
readonly property bool avatarVisible: !sentByMe && showAuthor
|
||||||
readonly property bool sentByMe: author === currentRoom.localUser
|
readonly property bool sentByMe: author === currentRoom.localUser
|
||||||
|
|
||||||
property bool openOnFinished: false
|
property bool openOnFinished: false
|
||||||
|
@ -44,8 +44,6 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.alignment: sentByMe ? Qt.AlignRight : Qt.AlignLeft
|
|
||||||
|
|
||||||
z: -5
|
z: -5
|
||||||
|
|
||||||
id: messageRow
|
id: messageRow
|
||||||
|
@ -121,17 +119,6 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
radius: 24
|
|
||||||
antialiasing: true
|
|
||||||
|
|
||||||
border.width: 4
|
|
||||||
border.color: MPalette.background
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import Spectral.Menu.Timeline 2.0
|
||||||
import Spectral.Effect 2.0
|
import Spectral.Effect 2.0
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
readonly property bool avatarVisible: !sentByMe && (aboveAuthor !== author || aboveSection !== section || aboveEventType === "state" || aboveEventType === "emote" || aboveEventType === "other")
|
readonly property bool avatarVisible: !sentByMe && showAuthor
|
||||||
readonly property bool sentByMe: author === currentRoom.localUser
|
readonly property bool sentByMe: author === currentRoom.localUser
|
||||||
readonly property bool darkBackground: !sentByMe
|
readonly property bool darkBackground: !sentByMe
|
||||||
readonly property bool replyVisible: replyEventId || false
|
readonly property bool replyVisible: replyEventId || false
|
||||||
|
@ -68,6 +68,58 @@ RowLayout {
|
||||||
radius: 18
|
radius: 18
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
visible: !sentByMe && (bubbleShape == 3 || bubbleShape == 2)
|
||||||
|
|
||||||
|
color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
|
||||||
|
radius: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
visible: sentByMe && (bubbleShape == 3 || bubbleShape == 2)
|
||||||
|
|
||||||
|
color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
|
||||||
|
radius: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
visible: !sentByMe && (bubbleShape == 1 || bubbleShape == 2)
|
||||||
|
|
||||||
|
color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
|
||||||
|
radius: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
visible: sentByMe && (bubbleShape == 1 || bubbleShape == 2)
|
||||||
|
|
||||||
|
color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
|
||||||
|
radius: 2
|
||||||
|
}
|
||||||
|
|
||||||
AutoMouseArea {
|
AutoMouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ Item {
|
||||||
|
|
||||||
id: messageListView
|
id: messageListView
|
||||||
|
|
||||||
spacing: 4
|
spacing: 2
|
||||||
|
|
||||||
displayMarginBeginning: 100
|
displayMarginBeginning: 100
|
||||||
displayMarginEnd: 100
|
displayMarginEnd: 100
|
||||||
|
@ -186,6 +186,8 @@ Item {
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "image"
|
roleValue: "image"
|
||||||
delegate: ImageDelegate {
|
delegate: ImageDelegate {
|
||||||
|
anchors.right: sentByMe ? parent.right : undefined
|
||||||
|
|
||||||
Layout.maximumWidth: parent.width
|
Layout.maximumWidth: parent.width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,6 +195,8 @@ Item {
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "file"
|
roleValue: "file"
|
||||||
delegate: FileDelegate {
|
delegate: FileDelegate {
|
||||||
|
anchors.right: sentByMe ? parent.right : undefined
|
||||||
|
|
||||||
Layout.maximumWidth: parent.width
|
Layout.maximumWidth: parent.width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,10 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const {
|
||||||
QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
|
QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
|
||||||
roles[EventTypeRole] = "eventType";
|
roles[EventTypeRole] = "eventType";
|
||||||
roles[MessageRole] = "message";
|
roles[MessageRole] = "message";
|
||||||
roles[AboveEventTypeRole] = "aboveEventType";
|
|
||||||
roles[EventIdRole] = "eventId";
|
roles[EventIdRole] = "eventId";
|
||||||
roles[TimeRole] = "time";
|
roles[TimeRole] = "time";
|
||||||
roles[AboveTimeRole] = "aboveTime";
|
|
||||||
roles[SectionRole] = "section";
|
roles[SectionRole] = "section";
|
||||||
roles[AboveSectionRole] = "aboveSection";
|
|
||||||
roles[AuthorRole] = "author";
|
roles[AuthorRole] = "author";
|
||||||
roles[AboveAuthorRole] = "aboveAuthor";
|
|
||||||
roles[ContentRole] = "content";
|
roles[ContentRole] = "content";
|
||||||
roles[ContentTypeRole] = "contentType";
|
roles[ContentTypeRole] = "contentType";
|
||||||
roles[HighlightRole] = "highlight";
|
roles[HighlightRole] = "highlight";
|
||||||
|
@ -38,6 +34,9 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const {
|
||||||
roles[ReplyAuthorRole] = "replyAuthor";
|
roles[ReplyAuthorRole] = "replyAuthor";
|
||||||
roles[ReplyDisplayRole] = "replyDisplay";
|
roles[ReplyDisplayRole] = "replyDisplay";
|
||||||
roles[UserMarkerRole] = "userMarker";
|
roles[UserMarkerRole] = "userMarker";
|
||||||
|
roles[ShowTimestampRole] = "showTimestamp";
|
||||||
|
roles[ShowAuthorRole] = "showAuthor";
|
||||||
|
roles[BubbleShapeRole] = "bubbleShape";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,9 +83,9 @@ void MessageEventModel::setRoom(SpectralRoom* room) {
|
||||||
if (biggest < m_currentRoom->maxTimelineIndex()) {
|
if (biggest < m_currentRoom->maxTimelineIndex()) {
|
||||||
auto rowBelowInserted = m_currentRoom->maxTimelineIndex() -
|
auto rowBelowInserted = m_currentRoom->maxTimelineIndex() -
|
||||||
biggest + timelineBaseIndex() - 1;
|
biggest + timelineBaseIndex() - 1;
|
||||||
refreshEventRoles(rowBelowInserted,
|
refreshEventRoles(
|
||||||
{AboveEventTypeRole, AboveAuthorRole,
|
rowBelowInserted,
|
||||||
AboveSectionRole, AboveTimeRole});
|
{ShowTimestampRole, ShowAuthorRole, BubbleShapeRole});
|
||||||
}
|
}
|
||||||
for (auto i = m_currentRoom->maxTimelineIndex() - biggest;
|
for (auto i = m_currentRoom->maxTimelineIndex() - biggest;
|
||||||
i <= m_currentRoom->maxTimelineIndex() - lowest; ++i)
|
i <= m_currentRoom->maxTimelineIndex() - lowest; ++i)
|
||||||
|
@ -117,8 +116,7 @@ void MessageEventModel::setRoom(SpectralRoom* room) {
|
||||||
refreshEventRoles(timelineBaseIndex() + 1, {ReadMarkerRole});
|
refreshEventRoles(timelineBaseIndex() + 1, {ReadMarkerRole});
|
||||||
if (timelineBaseIndex() > 0) // Refresh below, see #312
|
if (timelineBaseIndex() > 0) // Refresh below, see #312
|
||||||
refreshEventRoles(timelineBaseIndex() - 1,
|
refreshEventRoles(timelineBaseIndex() - 1,
|
||||||
{AboveEventTypeRole, AboveAuthorRole,
|
{ShowTimestampRole, ShowAuthorRole, BubbleShapeRole});
|
||||||
AboveSectionRole, AboveTimeRole});
|
|
||||||
});
|
});
|
||||||
connect(m_currentRoom, &Room::pendingEventChanged, this,
|
connect(m_currentRoom, &Room::pendingEventChanged, this,
|
||||||
&MessageEventModel::refreshRow);
|
&MessageEventModel::refreshRow);
|
||||||
|
@ -417,22 +415,49 @@ QVariant MessageEventModel::data(const QModelIndex& idx, int role) const {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == AboveEventTypeRole || role == AboveSectionRole ||
|
if (role == ShowTimestampRole || role == ShowAuthorRole)
|
||||||
role == AboveAuthorRole || role == AboveTimeRole)
|
|
||||||
for (auto r = row + 1; r < rowCount(); ++r) {
|
for (auto r = row + 1; r < rowCount(); ++r) {
|
||||||
auto i = index(r);
|
auto i = index(r);
|
||||||
if (data(i, SpecialMarksRole) != EventStatus::Hidden)
|
if (data(i, SpecialMarksRole) != EventStatus::Hidden) {
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case AboveEventTypeRole:
|
case ShowTimestampRole:
|
||||||
return data(i, EventTypeRole);
|
return data(i, TimeRole)
|
||||||
case AboveSectionRole:
|
.toDateTime()
|
||||||
return data(i, SectionRole);
|
.msecsTo(data(idx, TimeRole).toDateTime()) > 600000;
|
||||||
case AboveAuthorRole:
|
case ShowAuthorRole:
|
||||||
return data(i, AuthorRole);
|
return data(i, AuthorRole) != data(idx, AuthorRole);
|
||||||
case AboveTimeRole:
|
|
||||||
return data(i, TimeRole);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == BubbleShapeRole) { // TODO: Convoluted logic.
|
||||||
|
int belowRow = -1; // Invalid
|
||||||
|
|
||||||
|
for (auto r = row - 1; r >= 0; --r) {
|
||||||
|
auto i = index(r);
|
||||||
|
if (data(i, SpecialMarksRole) != EventStatus::Hidden) {
|
||||||
|
belowRow = r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool aboveShow, belowShow;
|
||||||
|
aboveShow = data(idx, ShowAuthorRole).toBool() ||
|
||||||
|
data(idx, ShowTimestampRole).toBool();
|
||||||
|
if (belowRow == -1)
|
||||||
|
belowShow = true;
|
||||||
|
else
|
||||||
|
belowShow = data(index(belowRow), ShowAuthorRole).toBool() ||
|
||||||
|
data(index(belowRow), ShowTimestampRole).toBool();
|
||||||
|
|
||||||
|
if (aboveShow && belowShow)
|
||||||
|
return BubbleShapes::NoShape;
|
||||||
|
if (aboveShow && !belowShow)
|
||||||
|
return BubbleShapes::BeginShape;
|
||||||
|
if (belowShow)
|
||||||
|
return BubbleShapes::EndShape;
|
||||||
|
return BubbleShapes::MiddleShape;
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,10 @@ class MessageEventModel : public QAbstractListModel {
|
||||||
enum EventRoles {
|
enum EventRoles {
|
||||||
EventTypeRole = Qt::UserRole + 1,
|
EventTypeRole = Qt::UserRole + 1,
|
||||||
MessageRole,
|
MessageRole,
|
||||||
AboveEventTypeRole,
|
|
||||||
EventIdRole,
|
EventIdRole,
|
||||||
TimeRole,
|
TimeRole,
|
||||||
AboveTimeRole,
|
|
||||||
SectionRole,
|
SectionRole,
|
||||||
AboveSectionRole,
|
|
||||||
AuthorRole,
|
AuthorRole,
|
||||||
AboveAuthorRole,
|
|
||||||
ContentRole,
|
ContentRole,
|
||||||
ContentTypeRole,
|
ContentTypeRole,
|
||||||
HighlightRole,
|
HighlightRole,
|
||||||
|
@ -30,13 +26,25 @@ class MessageEventModel : public QAbstractListModel {
|
||||||
LongOperationRole,
|
LongOperationRole,
|
||||||
AnnotationRole,
|
AnnotationRole,
|
||||||
UserMarkerRole,
|
UserMarkerRole,
|
||||||
|
// For reply
|
||||||
ReplyEventIdRole,
|
ReplyEventIdRole,
|
||||||
ReplyAuthorRole,
|
ReplyAuthorRole,
|
||||||
ReplyDisplayRole,
|
ReplyDisplayRole,
|
||||||
|
|
||||||
|
ShowTimestampRole,
|
||||||
|
ShowAuthorRole,
|
||||||
|
BubbleShapeRole,
|
||||||
// For debugging
|
// For debugging
|
||||||
EventResolvedTypeRole,
|
EventResolvedTypeRole,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum BubbleShapes {
|
||||||
|
NoShape = 0,
|
||||||
|
BeginShape,
|
||||||
|
MiddleShape,
|
||||||
|
EndShape,
|
||||||
|
};
|
||||||
|
|
||||||
explicit MessageEventModel(QObject* parent = nullptr);
|
explicit MessageEventModel(QObject* parent = nullptr);
|
||||||
~MessageEventModel();
|
~MessageEventModel();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue