Merge pull request #135 from jgibbon/feature/qml-runtime-optimizations

qml runtime optimizations
This commit is contained in:
Sebastian Wolf 2020-11-09 22:01:32 +01:00 committed by GitHub
commit b4ae9cf0f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 162 additions and 122 deletions

View file

@ -13,7 +13,7 @@ PhotoTextsListItem {
property url emojiBase: "../js/emoji/"
// chat title
primaryText.text: title ? Emoji.emojify(title, Theme.fontSizeMedium, emojiBase) + ( display.notification_settings.mute_for > 0 ? Emoji.emojify(" 🔇", Theme.fontSizeMedium) : "" ) : qsTr("Unknown")
primaryText.text: title ? Emoji.emojify(title + ( display.notification_settings.mute_for > 0 ? " 🔇" : "" ), Theme.fontSizeMedium, emojiBase) : qsTr("Unknown")
// last user
prologSecondaryText.text: is_channel ? "" : ( last_message_sender_id ? ( last_message_sender_id !== ownUserId ? Emoji.emojify(Functions.getUserName(tdLibWrapper.getUserInformation(last_message_sender_id)), primaryText.font.pixelSize) : qsTr("You") ) : qsTr("Unknown") )
// last message

View file

@ -20,76 +20,97 @@ import QtQuick 2.6
import Sailfish.Silica 1.0
import "../js/twemoji.js" as Emoji
import "../js/functions.js" as Functions
import QtQml.Models 2.3
ListItem {
id: messageListItem
contentHeight: messageBackground.height + Theme.paddingMedium
property var myMessage: display
property var userInformation: tdLibWrapper.getUserInformation(display.sender_user_id)
property Page page: chatPage
readonly property var myMessage: display
readonly property var userInformation: tdLibWrapper.getUserInformation(myMessage.sender_user_id)
property QtObject precalculatedValues: ListView.view.precalculatedValues
readonly property color textColor: isOwnMessage ? Theme.highlightColor : Theme.primaryColor
readonly property int textAlign: isOwnMessage ? Text.AlignRight : Text.AlignLeft
readonly property Page page: precalculatedValues.page
readonly property bool isOwnMessage: page.myUserId === myMessage.sender_user_id
readonly property string extraContentComponentName: typeof myMessage.content !== "undefined"
&& typeof chatView.contentComponentNames[myMessage.content['@type']] !== "undefined" ?
chatView.contentComponentNames[myMessage.content['@type']] : ""
readonly property ObjectModel additionalContextItems: ObjectModel {}
onPressAndHold: {
contextMenuLoader.active = true;
}
Loader {
id: contextMenuLoader
active: false
asynchronous: true
onStatusChanged: {
if(status === Loader.Ready) {
messageListItem.menu = item;
messageListItem.openMenu();
}
}
sourceComponent: Component {
ContextMenu {
Repeater {
model: messageListItem.additionalContextItems
}
property bool isOwnMessage: chatPage.myUserId === display.sender_user_id
property string extraContentComponentName: typeof display.content !== "undefined"
&& chatView.contentComponentNames.hasOwnProperty(display.content['@type']) ?
chatView.contentComponentNames[display.content['@type']] : ""
menu: ContextMenu {
MenuItem {
onClicked: {
newMessageInReplyToRow.inReplyToMessage = display;
newMessageInReplyToRow.inReplyToMessage = myMessage;
newMessageTextField.focus = true;
}
text: qsTr("Reply to Message")
}
MenuItem {
onClicked: {
newMessageColumn.editMessageId = display.id;
newMessageTextField.text = Functions.getMessageText(display, false, false);
newMessageColumn.editMessageId = myMessage.id;
newMessageTextField.text = Functions.getMessageText(myMessage, false, false);
newMessageTextField.focus = true;
}
text: qsTr("Edit Message")
visible: display.can_be_edited
visible: myMessage.can_be_edited
}
MenuItem {
onClicked: {
Clipboard.text = Functions.getMessageText(display, true, false);
Clipboard.text = Functions.getMessageText(myMessage, true, false);
}
text: qsTr("Copy Message to Clipboard")
}
MenuItem {
onClicked: {
var chatId = chatInformation.id;
var messageId = display.id;
var chatId = page.chatInformation.id;
var messageId = myMessage.id;
Remorse.itemAction(messageListItem, qsTr("Message deleted"), function() { tdLibWrapper.deleteMessages(chatId, [ messageId]); })
}
text: qsTr("Delete Message")
visible: display.can_be_deleted_for_all_users || (display.can_be_deleted_only_for_self && display.chat_id === chatPage.myUserId)
visible: myMessage.can_be_deleted_for_all_users || (myMessage.can_be_deleted_only_for_self && myMessage.chat_id === page.myUserId)
}
}
}
}
Connections {
target: chatModel
onUnreadCountUpdated: {
messageBackground.color = index > ( chatView.count - unreadCount - 1 ) ? Theme.secondaryHighlightColor : Theme.secondaryColor;
messageBackground.opacity = index > ( chatView.count - unreadCount - 1 ) ? 0.5 : 0.2;
messageBackground.isUnread = index > ( chatView.count - unreadCount - 1 );
}
onNewMessageReceived: {
messageBackground.color = index > ( chatView.count - chatInformation.unreadCount - 1 ) ? Theme.secondaryHighlightColor : Theme.secondaryColor;
messageBackground.opacity = index > ( chatView.count - chatInformation.unreadCount - 1 ) ? 0.5 : 0.2;
messageBackground.isUnread = index > ( chatView.count - page.chatInformation.unread_count - 1 );
}
onLastReadSentMessageUpdated: {
console.log("[ChatModel] Messages in this chat were read, new last read: " + lastReadSentIndex + ", updating description for index " + index + ", status: " + (index <= lastReadSentIndex));
messageDateText.text = getMessageStatusText(display, index, lastReadSentIndex, messageDateText.useElapsed);
messageDateText.text = getMessageStatusText(myMessage, index, lastReadSentIndex, messageDateText.useElapsed);
}
onMessageUpdated: {
if (index === modelIndex) {
console.log("[ChatModel] This message was updated, index " + index + ", updating content...");
messageDateText.text = getMessageStatusText(display, index, chatView.lastReadSentIndex, messageDateText.useElapsed);
messageText.text = Emoji.emojify(Functions.getMessageText(display, false, messageListItem.isOwnMessage), messageText.font.pixelSize);
if(locationPreviewLoader.active && locationPreviewLoader.status === Loader.Ready) {
locationPreviewLoader.item.locationData = display.content.location;
locationPreviewLoader.item.updatePicture()
}
messageDateText.text = getMessageStatusText(myMessage, index, chatView.lastReadSentIndex, messageDateText.useElapsed);
messageText.text = Emoji.emojify(Functions.getMessageText(myMessage, false, messageListItem.isOwnMessage), messageText.font.pixelSize);
}
}
}
@ -97,15 +118,18 @@ ListItem {
Connections {
target: tdLibWrapper
onReceivedMessage: {
if (messageId === display.reply_to_message_id.toString()) {
messageInReplyToRow.inReplyToMessage = message;
messageInReplyToRow.visible = true;
if (messageId === myMessage.reply_to_message_id.toString()) {
messageInReplyToLoader.inReplyToMessage = message;
}
}
}
Component.onCompleted: {
delegateComponentLoadingTimer.start();
if (myMessage.reply_to_message_id !== 0) {
tdLibWrapper.getMessage(page.chatInformation.id, myMessage.reply_to_message_id);
}
}
Timer {
@ -114,7 +138,7 @@ ListItem {
repeat: false
running: false
onTriggered: {
if (typeof display.content !== "undefined") {
if (typeof myMessage.content !== "undefined") {
if (messageListItem.extraContentComponentName !== "") {
extraContentLoader.setSource(
"../components/" +messageListItem.extraContentComponentName +".qml",
@ -122,7 +146,7 @@ ListItem {
messageListItem: messageListItem
})
} else {
if (typeof display.content.web_page !== "undefined") { // only in messageText
if (typeof myMessage.content.web_page !== "undefined") { // only in messageText
webPagePreviewLoader.active = true;
}
}
@ -133,16 +157,15 @@ ListItem {
Row {
id: messageTextRow
spacing: Theme.paddingSmall
width: parent.width - ( 2 * Theme.horizontalPageMargin )
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
width: precalculatedValues.entryWidth
anchors.centerIn: parent
Loader {
id: profileThumbnailLoader
active: (( chatPage.isBasicGroup || chatPage.isSuperGroup ) && !chatPage.isChannel)
active: precalculatedValues.showUserInfo
asynchronous: true
width: active ? Theme.itemSizeSmall : 0
height: active ? Theme.itemSizeSmall : 0
width: precalculatedValues.profileThumbnailDimensions
height: width
anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.paddingSmall
sourceComponent: Component {
@ -150,9 +173,9 @@ ListItem {
id: messagePictureThumbnail
photoData: (typeof messageListItem.userInformation.profile_photo !== "undefined") ? messageListItem.userInformation.profile_photo.small : ""
replacementStringHint: userText.text
width: visible ? Theme.itemSizeSmall : 0
height: visible ? Theme.itemSizeSmall : 0
visible: ( chatPage.isBasicGroup || chatPage.isSuperGroup ) && !chatPage.isChannel
width: Theme.itemSizeSmall
height: Theme.itemSizeSmall
visible: precalculatedValues.showUserInfo
MouseArea {
anchors.fill: parent
onClicked: {
@ -166,24 +189,23 @@ ListItem {
Item {
id: messageTextItem
width: parent.width - profileThumbnailLoader.width - Theme.paddingSmall
width: precalculatedValues.textItemWidth
height: messageBackground.height
Rectangle {
id: messageBackground
anchors {
left: parent.left
leftMargin: messageListItem.isOwnMessage ? 2 * Theme.horizontalPageMargin : 0
right: parent.right
rightMargin: messageListItem.isOwnMessage ? 0 : 2 * Theme.horizontalPageMargin
leftMargin: messageListItem.isOwnMessage ? precalculatedValues.pageMarginDouble : 0
verticalCenter: parent.verticalCenter
}
height: messageTextColumn.height + ( 2 * Theme.paddingMedium )
color: index > ( chatView.count - chatInformation.unread_count - 1 ) ? Theme.secondaryHighlightColor : Theme.secondaryColor
height: messageTextColumn.height + precalculatedValues.paddingMediumDouble
width: precalculatedValues.backgroundWidth
property bool isUnread: index > ( chatView.count - page.chatInformation.unread_count - 1 )
color: isUnread ? Theme.secondaryHighlightColor : Theme.secondaryColor
radius: parent.width / 50
opacity: index > ( chatView.count - chatInformation.unread_count - 1 ) ? 0.5 : 0.2
visible: appSettings.showStickersAsImages || display.content['@type'] !== "messageSticker"
opacity: isUnread ? 0.5 : 0.2
visible: appSettings.showStickersAsImages || myMessage.content['@type'] !== "messageSticker"
Behavior on color { ColorAnimation { duration: 200 } }
Behavior on opacity { FadeAnimation {} }
}
@ -193,29 +215,23 @@ ListItem {
spacing: Theme.paddingSmall
width: messageBackground.width - Theme.horizontalPageMargin
width: precalculatedValues.textColumnWidth
anchors.centerIn: messageBackground
Component.onCompleted: {
if (display.reply_to_message_id !== 0) {
tdLibWrapper.getMessage(chatInformation.id, display.reply_to_message_id);
}
}
Text {
id: userText
width: parent.width
text: !messageListItem.isOwnMessage ? Emoji.emojify(Functions.getUserName(messageListItem.userInformation), font.pixelSize) : qsTr("You")
text: messageListItem.isOwnMessage ? qsTr("You") : Emoji.emojify(Functions.getUserName(messageListItem.userInformation), font.pixelSize)
font.pixelSize: Theme.fontSizeExtraSmall
font.weight: Font.ExtraBold
color: messageListItem.isOwnMessage ? Theme.highlightColor : Theme.primaryColor
color: messageListItem.textColor
maximumLineCount: 1
elide: Text.ElideRight
textFormat: Text.StyledText
horizontalAlignment: messageListItem.isOwnMessage ? Text.AlignRight : Text.AlignLeft
visible: ( chatPage.isBasicGroup || chatPage.isSuperGroup ) && !chatPage.isChannel
horizontalAlignment: messageListItem.textAlign
visible: precalculatedValues.showUserInfo
MouseArea {
anchors.fill: parent
onClicked: {
@ -224,18 +240,29 @@ ListItem {
}
}
Loader {
id: messageInReplyToLoader
active: myMessage.reply_to_message_id !== 0
width: parent.width
// text height ~= 1,28*font.pixelSize
height: active ? precalculatedValues.messageInReplyToHeight : 0
property var inReplyToMessage;
sourceComponent: Component {
InReplyToRow {
id: messageInReplyToRow
myUserId: chatPage.myUserId
visible: false
myUserId: page.myUserId
visible: true
inReplyToMessage: messageInReplyToLoader.inReplyToMessage
}
}
}
Loader {
id: forwardedInformationLoader
active: typeof display.forward_info !== "undefined"
active: typeof myMessage.forward_info !== "undefined"
asynchronous: true
width: parent.width
// height: active ? ( item ? item.height : Theme.itemSizeExtraSmall ) : 0
height: active ? ( item ? item.height : Theme.itemSizeExtraSmall ) : 0
sourceComponent: Component {
Row {
id: forwardedMessageInformationRow
@ -243,17 +270,17 @@ ListItem {
width: parent.width
Component.onCompleted: {
if (display.forward_info.origin["@type"] === "messageForwardOriginChannel") {
var otherChatInformation = tdLibWrapper.getChat(display.forward_info.origin.chat_id);
if (myMessage.forward_info.origin["@type"] === "messageForwardOriginChannel") {
var otherChatInformation = tdLibWrapper.getChat(myMessage.forward_info.origin.chat_id);
forwardedThumbnail.photoData = (typeof otherChatInformation.photo !== "undefined") ? otherChatInformation.photo.small : "";
forwardedChannelText.text = Emoji.emojify(otherChatInformation.title, Theme.fontSizeExtraSmall);
} else if (display.forward_info.origin["@type"] === "messageForwardOriginUser") {
var otherUserInformation = tdLibWrapper.getUserInformation(display.forward_info.origin.sender_user_id);
} else if (myMessage.forward_info.origin["@type"] === "messageForwardOriginUser") {
var otherUserInformation = tdLibWrapper.getUserInformation(myMessage.forward_info.origin.sender_user_id);
forwardedThumbnail.photoData = (typeof otherUserInformation.profile_photo !== "undefined") ? otherUserInformation.profile_photo.small : "";
forwardedChannelText.text = Emoji.emojify(Functions.getUserName(otherUserInformation), Theme.fontSizeExtraSmall);
} else {
forwardedThumbnail.photoData = "";
forwardedChannelText.text = Emoji.emojify(display.forward_info.origin.sender_user_name, Theme.fontSizeExtraSmall);
forwardedChannelText.text = Emoji.emojify(myMessage.forward_info.origin.sender_user_name, Theme.fontSizeExtraSmall);
}
}
@ -310,15 +337,15 @@ ListItem {
Text {
id: messageText
width: parent.width
text: Emoji.emojify(Functions.getMessageText(display, false, messageListItem.isOwnMessage), font.pixelSize)
text: Emoji.emojify(Functions.getMessageText(myMessage, false, messageListItem.isOwnMessage), font.pixelSize)
font.pixelSize: Theme.fontSizeSmall
color: messageListItem.isOwnMessage ? Theme.highlightColor : Theme.primaryColor
color: messageListItem.textColor
wrapMode: Text.Wrap
textFormat: Text.StyledText
onLinkActivated: {
Functions.handleLink(link);
}
horizontalAlignment: messageListItem.isOwnMessage ? Text.AlignRight : Text.AlignLeft
horizontalAlignment: messageListItem.textAlign
linkColor: Theme.highlightColor
visible: (text !== "")
}
@ -328,7 +355,7 @@ ListItem {
active: false
asynchronous: true
width: parent.width
height: typeof display.content.web_page !== "undefined" ? ( (parent.width * 2 / 3) + (6 * Theme.fontSizeExtraSmall) + ( 7 * Theme.paddingSmall) ) : 0
height: typeof myMessage.content.web_page !== "undefined" ? precalculatedValues.webPagePreviewHeight : 0
sourceComponent: Component {
id: webPagePreviewComponent
@ -339,7 +366,7 @@ ListItem {
webPagePreviewLoader.height = webPagePreview.implicitHeight;
}
webPageData: display.content.web_page
webPageData: myMessage.content.web_page
width: parent.width
}
}
@ -348,8 +375,7 @@ ListItem {
id: extraContentLoader
width: parent.width
asynchronous: true
property int heightPreset: messageListItem.extraContentComponentName !== "" ? chatView.getContentComponentHeight(messageListItem.extraContentComponentName, display.content, width) : 0
height: item ? item.height : heightPreset
height: item ? item.height : (messageListItem.extraContentComponentName !== "" ? chatView.getContentComponentHeight(messageListItem.extraContentComponentName, myMessage.content, width) : 0)
}
Timer {
@ -358,7 +384,7 @@ ListItem {
running: true
repeat: true
onTriggered: {
messageDateText.text = getMessageStatusText(display, index, chatView.lastReadSentIndex, messageDateText.useElapsed);
messageDateText.text = getMessageStatusText(myMessage, index, chatView.lastReadSentIndex, messageDateText.useElapsed);
}
}
@ -371,13 +397,13 @@ ListItem {
id: messageDateText
font.pixelSize: Theme.fontSizeTiny
color: messageListItem.isOwnMessage ? Theme.secondaryHighlightColor : Theme.secondaryColor
horizontalAlignment: messageListItem.isOwnMessage ? Text.AlignRight : Text.AlignLeft
text: getMessageStatusText(display, index, chatView.lastReadSentIndex, messageDateText.useElapsed)
horizontalAlignment: messageListItem.textAlign
text: getMessageStatusText(myMessage, index, chatView.lastReadSentIndex, messageDateText.useElapsed)
MouseArea {
anchors.fill: parent
onClicked: {
messageDateText.useElapsed = !messageDateText.useElapsed;
messageDateText.text = getMessageStatusText(display, index, chatView.lastReadSentIndex, messageDateText.useElapsed);
messageDateText.text = getMessageStatusText(myMessage, index, chatView.lastReadSentIndex, messageDateText.useElapsed);
}
}
}

View file

@ -288,9 +288,9 @@ Item {
Component.onCompleted: {
opacity = 1;
if(messageListItem && messageListItem.menu ) { // workaround to add menu entries
closePollMenuItemComponent.createObject(messageListItem.menu._contentColumn);
resetAnswerMenuItemComponent.createObject(messageListItem.menu._contentColumn);
if(messageListItem && messageListItem.additionalContextItems ) {
messageListItem.additionalContextItems.append(closePollMenuItemComponent.createObject());
messageListItem.additionalContextItems.append(resetAnswerMenuItemComponent.createObject());
}
}
}

View file

@ -64,9 +64,11 @@ Item {
Image {
id: singleImage
width: parent.width - Theme.paddingSmall
height: parent.height - Theme.paddingSmall
height: width
anchors.centerIn: parent
source: file.path
sourceSize.width: width
sourceSize.height: height
fillMode: Image.PreserveAspectCrop
autoTransform: true
asynchronous: true

View file

@ -124,6 +124,8 @@ Column {
height: parent.height - Theme.paddingSmall
anchors.centerIn: parent
sourceSize.width: width
sourceSize.height: height
fillMode: Image.PreserveAspectCrop
autoTransform: true
asynchronous: true

View file

@ -32,7 +32,7 @@ Page {
property bool loading: true;
property bool isInitialized: false;
property int myUserId: tdLibWrapper.getUserInformation().id;
readonly property int myUserId: tdLibWrapper.getUserInformation().id;
property var chatInformation;
property bool isPrivateChat: false;
property bool isBasicGroup: false;
@ -42,7 +42,7 @@ Page {
property var chatGroupInformation;
property int chatOnlineMemberCount: 0;
property var emojiProposals;
property bool userIsMember: (isPrivateChat && chatInformation["@type"]) || // should be optimized
readonly property bool userIsMember: (isPrivateChat && chatInformation["@type"]) || // should be optimized
(isBasicGroup || isSuperGroup) && (
(chatGroupInformation.status["@type"] === "chatMemberStatusMember")
|| (chatGroupInformation.status["@type"] === "chatMemberStatusAdministrator")
@ -82,11 +82,11 @@ Page {
chatPartnerInformation = tdLibWrapper.getUserInformation(chatInformation.type.user_id);
updateChatPartnerStatusText();
}
if (isBasicGroup) {
else if (isBasicGroup) {
chatGroupInformation = tdLibWrapper.getBasicGroup(chatInformation.type.basic_group_id);
updateGroupStatusText();
}
if (isSuperGroup) {
else if (isSuperGroup) {
chatGroupInformation = tdLibWrapper.getSuperGroup(chatInformation.type.supergroup_id);
isChannel = chatGroupInformation.is_channel;
updateGroupStatusText();
@ -232,7 +232,6 @@ Page {
}
break;
}
}
Connections {
@ -531,13 +530,23 @@ Page {
clip: true
highlightMoveDuration: 0
highlightResizeDuration: 0
highlightRangeMode: ListView.ApplyRange
preferredHighlightBegin: 0
preferredHighlightEnd: height
highlight: Component {Item { } }
property int lastReadSentIndex: 0
property bool inCooldown: false
property bool manuallyScrolledToBottom
property QtObject precalculatedValues: QtObject {
readonly property alias page: chatPage
readonly property bool showUserInfo: page.isBasicGroup || ( page.isSuperGroup && !page.isChannel)
readonly property int profileThumbnailDimensions: showUserInfo ? Theme.itemSizeSmall : 0
readonly property int pageMarginDouble: 2 * Theme.horizontalPageMargin
readonly property int paddingMediumDouble: 2 * Theme.paddingMedium
readonly property int entryWidth: chatView.width - pageMarginDouble
readonly property int textItemWidth: entryWidth - profileThumbnailDimensions - Theme.paddingSmall
readonly property int backgroundWidth: textItemWidth - pageMarginDouble
readonly property int backgroundRadius: textItemWidth/50
readonly property int textColumnWidth: backgroundWidth - Theme.horizontalPageMargin
readonly property int messageInReplyToHeight: Theme.fontSizeExtraSmall * 2.571428571 + Theme.paddingSmall;
readonly property int webPagePreviewHeight: ( (textColumnWidth * 2 / 3) + (6 * Theme.fontSizeExtraSmall) + ( 7 * Theme.paddingSmall) )
}
function handleScrollPositionChanged() {
console.log("Current position: " + chatView.contentY);
@ -552,7 +561,8 @@ Page {
function scrollToIndex(index) {
if(index > 0 && index < chatView.count) {
currentIndex = index;
positionViewAtIndex(index, ListView.Contain)
// currentIndex = index;
if(index === chatView.count - 1) {
manuallyScrolledToBottom = true;
}
@ -582,7 +592,7 @@ Page {
}
model: chatModel
property var contentComponentNames: ({
readonly property var contentComponentNames: ({
messageSticker: "StickerPreview",
messagePhoto: "ImagePreview",
messageVideo: "VideoPreview",
@ -611,12 +621,14 @@ Page {
}
}
property var simpleDelegateMessages: ["messageBasicGroupChatCreate", "messageChatAddMembers", "messageChatChangePhoto", "messageChatChangeTitle", "messageChatDeleteMember", "messageChatDeletePhoto", "messageChatJoinByLink", "messageChatSetTtl", "messageChatUpgradeFrom", "messageChatUpgradeTo", "messageCustomServiceAction", "messagePinMessage", "messageScreenshotTaken", "messageSupergroupChatCreate", "messageUnsupported"]
readonly property var simpleDelegateMessages: ["messageBasicGroupChatCreate", "messageChatAddMembers", "messageChatChangePhoto", "messageChatChangeTitle", "messageChatDeleteMember", "messageChatDeletePhoto", "messageChatJoinByLink", "messageChatSetTtl", "messageChatUpgradeFrom", "messageChatUpgradeTo", "messageCustomServiceAction", "messagePinMessage", "messageScreenshotTaken", "messageSupergroupChatCreate", "messageUnsupported"]
delegate: Loader {
width: chatView.width
Component {
id: messageListViewItemComponent
MessageListViewItem {}
MessageListViewItem {
precalculatedValues: chatView.precalculatedValues
}
}
Component {
id: messageListViewItemSimpleComponent
@ -663,8 +675,6 @@ Page {
anchors.rightMargin: Theme.paddingMedium
anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.paddingMedium
opacity: viewMessageTimer.running ? 0.5 : 1.0
Behavior on opacity { FadeAnimation {} }
Rectangle {
id: chatUnreadMessagesCountBackground
color: Theme.highlightBackgroundColor