Hopefully this stops any crashes around QuickActions.qml and EmojiDialog.qml. Best I can guess this is some race condition where QuickActions are deleted in the time it takes to instnatiate the EmojiDialog popup. I've also rearranged the updateQuickActions function to stop a possible race condition there. BUG: 509484
697 lines
22 KiB
C++
697 lines
22 KiB
C++
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
|
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
|
|
#include "messagedelegate.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
MessageObjectIncubator::MessageObjectIncubator(std::function<void(QQuickItem *)> initialCallback,
|
|
std::function<void(MessageObjectIncubator *)> completedCallback,
|
|
std::function<void(MessageObjectIncubator *)> errorCallback)
|
|
: QQmlIncubator(QQmlIncubator::Asynchronous)
|
|
, m_initialCallback(initialCallback)
|
|
, m_completedCallback(completedCallback)
|
|
, m_errorCallback(errorCallback)
|
|
{
|
|
}
|
|
|
|
void MessageObjectIncubator::setInitialState(QObject *object)
|
|
{
|
|
auto item = qobject_cast<QQuickItem *>(object);
|
|
if (item) {
|
|
m_initialCallback(item);
|
|
}
|
|
}
|
|
|
|
void MessageObjectIncubator::statusChanged(QQmlIncubator::Status status)
|
|
{
|
|
if (status == QQmlIncubator::Error && m_errorCallback) {
|
|
m_errorCallback(this);
|
|
}
|
|
if (status == QQmlIncubator::Ready && m_completedCallback) {
|
|
m_completedCallback(this);
|
|
}
|
|
}
|
|
|
|
MessageDelegateBase::MessageDelegateBase(QQuickItem *parent)
|
|
: TimelineDelegate(parent)
|
|
{
|
|
m_contentSizeHelper.setParentItem(this);
|
|
setAcceptHoverEvents(true);
|
|
setPercentageValues();
|
|
|
|
connect(this, &MessageDelegateBase::leftPaddingChanged, this, &MessageDelegateBase::setContentPadding);
|
|
connect(this, &MessageDelegateBase::rightPaddingChanged, this, &MessageDelegateBase::setContentPadding);
|
|
connect(&m_contentSizeHelper, &DelegateSizeHelper::availableWidthChanged, this, &MessageDelegateBase::setContentPadding);
|
|
connect(&m_contentSizeHelper, &DelegateSizeHelper::availableWidthChanged, this, &MessageDelegateBase::maxContentWidthChanged);
|
|
connect(&m_contentSizeHelper, &DelegateSizeHelper::availableWidthChanged, this, &MessageDelegateBase::markAsDirty);
|
|
}
|
|
|
|
MessageDelegateBase::~MessageDelegateBase()
|
|
{
|
|
for (const auto &incubator : m_activeIncubators) {
|
|
incubator->clear();
|
|
delete incubator;
|
|
}
|
|
}
|
|
|
|
NeochatRoomMember *MessageDelegateBase::author() const
|
|
{
|
|
return m_author;
|
|
}
|
|
|
|
void MessageDelegateBase::setAuthor(NeochatRoomMember *author)
|
|
{
|
|
if (author == m_author) {
|
|
return;
|
|
}
|
|
m_author = author;
|
|
Q_EMIT authorChanged();
|
|
|
|
setContentPadding();
|
|
markAsDirty();
|
|
}
|
|
|
|
bool MessageDelegateBase::isThreaded() const
|
|
{
|
|
return m_isThreaded;
|
|
}
|
|
|
|
void MessageDelegateBase::setIsThreaded(bool isThreaded)
|
|
{
|
|
if (isThreaded == m_isThreaded) {
|
|
return;
|
|
}
|
|
m_isThreaded = isThreaded;
|
|
setAlwaysFillWidth(m_isThreaded || m_compactMode);
|
|
setPercentageValues(m_isThreaded || m_compactMode);
|
|
updateAvatar();
|
|
Q_EMIT isThreadedChanged();
|
|
}
|
|
|
|
void MessageDelegateBase::setCurveValues()
|
|
{
|
|
m_spacing = qreal(m_units->smallSpacing());
|
|
m_avatarSize = qreal(m_units->gridUnit() + m_units->largeSpacing() * 2);
|
|
|
|
m_sizeHelper.setLeftPadding(qreal(m_units->largeSpacing()));
|
|
setBaseRightPadding();
|
|
|
|
m_sizeHelper.setStartBreakpoint(qreal(m_units->gridUnit() * 46));
|
|
m_sizeHelper.setEndBreakpoint(qreal(m_units->gridUnit() * 66));
|
|
m_sizeHelper.setMaxWidth(qreal(m_units->gridUnit() * 60));
|
|
|
|
m_contentSizeHelper.setStartBreakpoint(qreal(m_units->gridUnit() * 25));
|
|
m_contentSizeHelper.setEndBreakpoint(qreal(m_units->gridUnit() * 40));
|
|
m_contentSizeHelper.setMaxWidth(qreal(m_units->gridUnit() * 60));
|
|
|
|
setContentPadding();
|
|
}
|
|
|
|
void MessageDelegateBase::setBaseRightPadding()
|
|
{
|
|
if (!m_units) {
|
|
return;
|
|
}
|
|
|
|
if (m_compactMode && width() > m_units->gridUnit() * 30) {
|
|
m_sizeHelper.setRightPadding(qreal(m_units->gridUnit() * 2 + m_units->largeSpacing()));
|
|
} else {
|
|
m_sizeHelper.setRightPadding(qreal(m_units->largeSpacing()));
|
|
}
|
|
}
|
|
|
|
void MessageDelegateBase::setPercentageValues(bool fillWidth)
|
|
{
|
|
if (fillWidth) {
|
|
m_contentSizeHelper.setStartPercentWidth(100);
|
|
m_contentSizeHelper.setEndPercentWidth(100);
|
|
} else {
|
|
m_contentSizeHelper.setStartPercentWidth(85);
|
|
m_contentSizeHelper.setEndPercentWidth(60);
|
|
}
|
|
}
|
|
|
|
void MessageDelegateBase::setContentPadding()
|
|
{
|
|
m_contentSizeHelper.setLeftPadding(m_sizeHelper.leftX() + (leaveAvatarSpace() ? m_avatarSize + m_spacing : 0));
|
|
m_contentSizeHelper.setRightPadding(m_sizeHelper.rightPadding());
|
|
}
|
|
|
|
qreal MessageDelegateBase::maxContentWidth() const
|
|
{
|
|
return m_isThreaded || m_alwaysFillWidth ? m_contentSizeHelper.maxAvailableWidth() : m_contentSizeHelper.availableWidth();
|
|
}
|
|
|
|
void MessageDelegateBase::cleanupIncubator(MessageObjectIncubator *incubator)
|
|
{
|
|
if (!incubator) {
|
|
return;
|
|
}
|
|
|
|
incubator->clear();
|
|
const auto it = std::find(m_activeIncubators.begin(), m_activeIncubators.end(), incubator);
|
|
if (it != m_activeIncubators.end()) {
|
|
m_activeIncubators.erase(it);
|
|
}
|
|
delete incubator;
|
|
}
|
|
|
|
void MessageDelegateBase::cleanupItem(QQuickItem *item)
|
|
{
|
|
if (!item) {
|
|
return;
|
|
}
|
|
item->setParentItem(nullptr);
|
|
item->disconnect(this);
|
|
item->deleteLater();
|
|
}
|
|
|
|
QQmlComponent *MessageDelegateBase::avatarComponent() const
|
|
{
|
|
return m_avatarComponent;
|
|
}
|
|
|
|
void MessageDelegateBase::setAvatarComponent(QQmlComponent *avatarComponent)
|
|
{
|
|
if (avatarComponent == m_avatarComponent) {
|
|
return;
|
|
}
|
|
m_avatarComponent = avatarComponent;
|
|
Q_EMIT avatarComponentChanged();
|
|
|
|
updateAvatar();
|
|
}
|
|
|
|
bool MessageDelegateBase::showAuthor() const
|
|
{
|
|
return m_showAuthor;
|
|
}
|
|
|
|
void MessageDelegateBase::setShowAuthor(bool showAuthor)
|
|
{
|
|
if (showAuthor == m_showAuthor) {
|
|
return;
|
|
}
|
|
m_showAuthor = showAuthor;
|
|
Q_EMIT showAuthorChanged();
|
|
|
|
updateAvatar();
|
|
}
|
|
|
|
bool MessageDelegateBase::enableAvatars() const
|
|
{
|
|
return m_enableAvatars;
|
|
}
|
|
|
|
void MessageDelegateBase::setEnableAvatars(bool enableAvatars)
|
|
{
|
|
if (enableAvatars == m_enableAvatars) {
|
|
return;
|
|
}
|
|
m_enableAvatars = enableAvatars;
|
|
Q_EMIT enableAvatarsChanged();
|
|
|
|
updateAvatar();
|
|
}
|
|
|
|
bool MessageDelegateBase::leaveAvatarSpace() const
|
|
{
|
|
return m_enableAvatars && !showMessageOnRight();
|
|
}
|
|
|
|
bool MessageDelegateBase::showAvatar() const
|
|
{
|
|
return m_enableAvatars && m_showAuthor && !showMessageOnRight();
|
|
}
|
|
|
|
void MessageDelegateBase::updateAvatar()
|
|
{
|
|
if (m_avatarComponent && showAvatar() && !m_avatarItem && !m_avatarIncubating) {
|
|
const auto avatarIncubator = new MessageObjectIncubator(
|
|
m_objectInitialCallback,
|
|
[this](MessageObjectIncubator *incubator) {
|
|
if (!incubator) {
|
|
return;
|
|
}
|
|
const auto avatarObject = qobject_cast<QQuickItem *>(incubator->object());
|
|
if (avatarObject) {
|
|
// The setting may have changed during the incubation period.
|
|
if (showAvatar()) {
|
|
m_avatarItem = avatarObject;
|
|
} else {
|
|
cleanupItem(avatarObject);
|
|
}
|
|
markAsDirty();
|
|
}
|
|
m_avatarIncubating = false;
|
|
cleanupIncubator(incubator);
|
|
},
|
|
m_errorCallback);
|
|
m_activeIncubators.push_back(avatarIncubator);
|
|
m_avatarComponent->create(*avatarIncubator, qmlContext(m_avatarComponent));
|
|
m_avatarIncubating = true;
|
|
} else if (!showAvatar() && m_avatarItem) {
|
|
cleanupItem(m_avatarItem);
|
|
markAsDirty();
|
|
}
|
|
}
|
|
|
|
QQmlComponent *MessageDelegateBase::sectionComponent() const
|
|
{
|
|
return m_sectionComponent;
|
|
}
|
|
|
|
void MessageDelegateBase::setSectionComponent(QQmlComponent *sectionComponent)
|
|
{
|
|
if (sectionComponent == m_sectionComponent) {
|
|
return;
|
|
}
|
|
m_sectionComponent = sectionComponent;
|
|
Q_EMIT sectionComponentChanged();
|
|
|
|
updateSection();
|
|
}
|
|
|
|
bool MessageDelegateBase::showSection() const
|
|
{
|
|
return m_showSection;
|
|
}
|
|
|
|
void MessageDelegateBase::setShowSection(bool showSection)
|
|
{
|
|
if (showSection == m_showSection) {
|
|
return;
|
|
}
|
|
m_showSection = showSection;
|
|
Q_EMIT showSectionChanged();
|
|
|
|
updateSection();
|
|
}
|
|
|
|
void MessageDelegateBase::updateSection()
|
|
{
|
|
if (m_sectionComponent && m_showSection && !m_sectionItem && !m_sectionIncubating) {
|
|
const auto sectionIncubator = new MessageObjectIncubator(
|
|
m_objectInitialCallback,
|
|
[this](MessageObjectIncubator *incubator) {
|
|
if (!incubator) {
|
|
return;
|
|
}
|
|
const auto sectionObject = qobject_cast<QQuickItem *>(incubator->object());
|
|
if (sectionObject) {
|
|
// The setting may have changed during the incubation period.
|
|
if (m_showSection) {
|
|
m_sectionItem = sectionObject;
|
|
} else {
|
|
cleanupItem(sectionObject);
|
|
}
|
|
markAsDirty();
|
|
}
|
|
m_sectionIncubating = false;
|
|
cleanupIncubator(incubator);
|
|
},
|
|
m_errorCallback);
|
|
m_activeIncubators.push_back(sectionIncubator);
|
|
m_sectionComponent->create(*sectionIncubator, qmlContext(m_sectionComponent));
|
|
m_sectionIncubating = true;
|
|
} else if (!m_showSection && m_sectionItem) {
|
|
cleanupItem(m_sectionItem);
|
|
markAsDirty();
|
|
}
|
|
}
|
|
|
|
QQmlComponent *MessageDelegateBase::readMarkerComponent() const
|
|
{
|
|
return m_readMarkerComponent;
|
|
}
|
|
|
|
void MessageDelegateBase::setReadMarkerComponent(QQmlComponent *readMarkerComponent)
|
|
{
|
|
if (readMarkerComponent == m_readMarkerComponent) {
|
|
return;
|
|
}
|
|
m_readMarkerComponent = readMarkerComponent;
|
|
Q_EMIT readMarkerComponentChanged();
|
|
|
|
updateReadMarker();
|
|
}
|
|
|
|
bool MessageDelegateBase::showReadMarkers() const
|
|
{
|
|
return m_showReadMarkers;
|
|
}
|
|
|
|
void MessageDelegateBase::setShowReadMarkers(bool showReadMarkers)
|
|
{
|
|
if (showReadMarkers == m_showReadMarkers) {
|
|
return;
|
|
}
|
|
m_showReadMarkers = showReadMarkers;
|
|
Q_EMIT showReadMarkersChanged();
|
|
|
|
updateReadMarker();
|
|
}
|
|
|
|
void MessageDelegateBase::updateReadMarker()
|
|
{
|
|
if (m_readMarkerComponent && m_showReadMarkers && !m_readMarkerItem && !m_readMarkerIncubating) {
|
|
const auto readMarkerIncubator = new MessageObjectIncubator(
|
|
m_objectInitialCallback,
|
|
[this](MessageObjectIncubator *incubator) {
|
|
if (!incubator) {
|
|
return;
|
|
}
|
|
|
|
const auto readMarkerObject = qobject_cast<QQuickItem *>(incubator->object());
|
|
if (readMarkerObject) {
|
|
if (m_showReadMarkers) {
|
|
m_readMarkerItem = readMarkerObject;
|
|
} else {
|
|
cleanupItem(readMarkerObject);
|
|
}
|
|
markAsDirty();
|
|
}
|
|
m_readMarkerIncubating = false;
|
|
cleanupIncubator(incubator);
|
|
},
|
|
m_errorCallback);
|
|
m_activeIncubators.push_back(readMarkerIncubator);
|
|
m_readMarkerComponent->create(*readMarkerIncubator, qmlContext(m_readMarkerComponent));
|
|
m_readMarkerIncubating = true;
|
|
} else if (!m_showReadMarkers && m_readMarkerItem) {
|
|
cleanupItem(m_readMarkerItem);
|
|
markAsDirty();
|
|
}
|
|
}
|
|
|
|
QQmlComponent *MessageDelegateBase::compactBackgroundComponent() const
|
|
{
|
|
return m_compactBackgroundComponent;
|
|
}
|
|
|
|
void MessageDelegateBase::setCompactBackgroundComponentt(QQmlComponent *compactBackgroundComponent)
|
|
{
|
|
if (compactBackgroundComponent == m_compactBackgroundComponent) {
|
|
return;
|
|
}
|
|
m_compactBackgroundComponent = compactBackgroundComponent;
|
|
Q_EMIT compactBackgroundComponentChanged();
|
|
|
|
updateBackground();
|
|
}
|
|
|
|
bool MessageDelegateBase::compactMode() const
|
|
{
|
|
return m_compactMode;
|
|
}
|
|
|
|
void MessageDelegateBase::setCompactMode(bool compactMode)
|
|
{
|
|
if (compactMode == m_compactMode) {
|
|
return;
|
|
}
|
|
m_compactMode = compactMode;
|
|
setAlwaysFillWidth(m_isThreaded || m_compactMode);
|
|
setPercentageValues(m_isThreaded || m_compactMode);
|
|
setBaseRightPadding();
|
|
|
|
Q_EMIT compactModeChanged();
|
|
Q_EMIT maxContentWidthChanged();
|
|
|
|
updateBackground();
|
|
updateQuickAction();
|
|
updateAvatar();
|
|
}
|
|
|
|
void MessageDelegateBase::updateBackground()
|
|
{
|
|
if (m_compactBackgroundComponent && m_compactMode && m_hovered && !m_compactBackgroundItem && !m_compactBackgroundIncubating) {
|
|
const auto compactBackgroundIncubator = new MessageObjectIncubator(
|
|
m_objectInitialCallback,
|
|
[this](MessageObjectIncubator *incubator) {
|
|
if (!incubator) {
|
|
return;
|
|
}
|
|
|
|
const auto compactBackgroundObject = qobject_cast<QQuickItem *>(incubator->object());
|
|
if (compactBackgroundObject) {
|
|
if (m_compactMode) {
|
|
m_compactBackgroundItem = compactBackgroundObject;
|
|
} else {
|
|
cleanupItem(compactBackgroundObject);
|
|
}
|
|
markAsDirty();
|
|
}
|
|
m_compactBackgroundIncubating = false;
|
|
cleanupIncubator(incubator);
|
|
},
|
|
m_errorCallback);
|
|
m_activeIncubators.push_back(compactBackgroundIncubator);
|
|
m_compactBackgroundComponent->create(*compactBackgroundIncubator, qmlContext(m_compactBackgroundComponent));
|
|
m_compactBackgroundIncubating = true;
|
|
} else if (m_compactBackgroundItem && !m_hovered) {
|
|
cleanupItem(m_compactBackgroundItem);
|
|
markAsDirty();
|
|
}
|
|
}
|
|
|
|
QQmlComponent *MessageDelegateBase::quickActionComponent() const
|
|
{
|
|
return m_quickActionComponent;
|
|
}
|
|
|
|
void MessageDelegateBase::setQuickActionComponent(QQmlComponent *quickActionComponent)
|
|
{
|
|
if (quickActionComponent == m_quickActionComponent) {
|
|
return;
|
|
}
|
|
m_quickActionComponent = quickActionComponent;
|
|
Q_EMIT quickActionComponentChanged();
|
|
|
|
updateQuickAction();
|
|
}
|
|
|
|
void MessageDelegateBase::updateQuickAction()
|
|
{
|
|
if (!m_hovered || m_compactMode) {
|
|
if (m_quickActionItem && (!m_quickActionItem->property("reacting").toBool() || m_compactMode)) {
|
|
cleanupItem(m_quickActionItem);
|
|
markAsDirty();
|
|
}
|
|
return;
|
|
}
|
|
if (!m_quickActionComponent || m_quickActionItem || m_quickActionIncubating) {
|
|
return;
|
|
}
|
|
|
|
m_quickActionIncubating = true;
|
|
const auto quickActionIncubator = new MessageObjectIncubator(
|
|
m_objectInitialCallback,
|
|
[this](MessageObjectIncubator *incubator) {
|
|
if (!incubator) {
|
|
return;
|
|
}
|
|
|
|
if (const auto quickActionObject = qobject_cast<QQuickItem *>(incubator->object())) {
|
|
if (!m_compactMode) {
|
|
m_quickActionItem = quickActionObject;
|
|
connect(m_quickActionItem, SIGNAL(reactingChanged()), this, SLOT(updateQuickAction()));
|
|
} else {
|
|
cleanupItem(quickActionObject);
|
|
}
|
|
markAsDirty();
|
|
}
|
|
cleanupIncubator(incubator);
|
|
m_quickActionIncubating = false;
|
|
},
|
|
m_errorCallback);
|
|
m_activeIncubators.push_back(quickActionIncubator);
|
|
m_quickActionComponent->create(*quickActionIncubator, qmlContext(m_quickActionComponent));
|
|
}
|
|
|
|
bool MessageDelegateBase::showLocalMessagesOnRight() const
|
|
{
|
|
return m_showLocalMessagesOnRight;
|
|
}
|
|
|
|
void MessageDelegateBase::setShowLocalMessagesOnRight(bool showLocalMessagesOnRight)
|
|
{
|
|
if (showLocalMessagesOnRight == m_showLocalMessagesOnRight) {
|
|
return;
|
|
}
|
|
m_showLocalMessagesOnRight = showLocalMessagesOnRight;
|
|
Q_EMIT showLocalMessagesOnRightChanged();
|
|
|
|
setContentPadding();
|
|
updateAvatar();
|
|
}
|
|
|
|
void MessageDelegateBase::updateImplicitHeight()
|
|
{
|
|
qreal implicitHeight = 0.0;
|
|
int numObj = 0;
|
|
if (m_showSection && m_sectionItem) {
|
|
implicitHeight += m_sectionItem->implicitHeight();
|
|
numObj++;
|
|
}
|
|
qreal avatarHeight = 0.0;
|
|
qreal contentHeight = 0.0;
|
|
qreal quickActionHeight = 0.0;
|
|
if (showAvatar() && m_avatarItem) {
|
|
m_avatarItem->setImplicitWidth(m_avatarSize);
|
|
m_avatarItem->setImplicitHeight(m_avatarSize);
|
|
avatarHeight = m_avatarItem->implicitHeight();
|
|
}
|
|
if (m_contentItem) {
|
|
contentHeight = m_contentItem->implicitHeight();
|
|
}
|
|
if (m_quickActionItem) {
|
|
quickActionHeight = m_quickActionItem->implicitHeight();
|
|
}
|
|
implicitHeight += std::max({avatarHeight, contentHeight, quickActionHeight});
|
|
if (avatarHeight > 0 || contentHeight > 0) {
|
|
numObj++;
|
|
}
|
|
if (m_showReadMarkers && m_readMarkerItem) {
|
|
implicitHeight += m_readMarkerItem->implicitHeight();
|
|
numObj++;
|
|
}
|
|
implicitHeight += (numObj - 1) * m_spacing;
|
|
implicitHeight += m_showAuthor ? m_spacing * 2 : m_spacing;
|
|
implicitHeight = std::ceil(implicitHeight);
|
|
setImplicitWidth(m_alwaysFillWidth ? m_sizeHelper.maxAvailableWidth() : m_sizeHelper.availableWidth());
|
|
setImplicitHeight(implicitHeight);
|
|
}
|
|
|
|
bool MessageDelegateBase::showMessageOnRight() const
|
|
{
|
|
return m_showLocalMessagesOnRight && !m_alwaysFillWidth && m_author && m_author->isLocalMember();
|
|
}
|
|
|
|
void MessageDelegateBase::resizeContent()
|
|
{
|
|
if (!isComponentComplete() || m_resizingContent) {
|
|
return;
|
|
}
|
|
|
|
m_isDirty = false;
|
|
m_resizingContent = true;
|
|
|
|
updateImplicitHeight();
|
|
|
|
qreal nextY = m_showAuthor ? m_spacing * 2 : m_spacing;
|
|
|
|
if (m_compactMode && m_compactBackgroundItem) {
|
|
m_compactBackgroundItem->setPosition(QPointF(std::ceil(m_sizeHelper.leftX() / 2), std::ceil(nextY / 2)));
|
|
m_compactBackgroundItem->setSize(
|
|
QSizeF(m_sizeHelper.availableWidth() + m_sizeHelper.rightPadding() - std::ceil(m_sizeHelper.leftPadding() / 2), implicitHeight()));
|
|
m_compactBackgroundItem->setZ(-1);
|
|
}
|
|
if (m_showSection && m_sectionItem) {
|
|
m_sectionItem->setPosition(QPointF(m_sizeHelper.leftX(), nextY));
|
|
m_sectionItem->setSize(QSizeF(m_sizeHelper.availableWidth(), m_sectionItem->implicitHeight()));
|
|
nextY += m_sectionItem->implicitHeight() + m_spacing;
|
|
}
|
|
qreal yAdd = 0.0;
|
|
if (showAvatar() && m_avatarItem) {
|
|
m_avatarItem->setPosition(QPointF(m_sizeHelper.leftX(), nextY));
|
|
m_avatarItem->setSize(QSizeF(m_avatarItem->implicitWidth(), m_avatarItem->implicitHeight()));
|
|
yAdd = m_avatarItem->implicitWidth();
|
|
}
|
|
if (m_contentItem) {
|
|
const auto contentItemWidth =
|
|
m_alwaysFillWidth ? m_contentSizeHelper.availableWidth() : std::min(m_contentItem->implicitWidth(), m_contentSizeHelper.availableWidth());
|
|
const auto contentX = showMessageOnRight() ? m_sizeHelper.rightX() - contentItemWidth - 1 : m_contentSizeHelper.leftPadding();
|
|
m_contentItem->setPosition(QPointF(contentX, nextY));
|
|
m_contentItem->setSize(QSizeF(contentItemWidth, m_contentItem->implicitHeight()));
|
|
yAdd = std::max(yAdd, m_contentItem->implicitHeight());
|
|
}
|
|
if (m_quickActionItem) {
|
|
const auto availableWidth = m_contentItem && showMessageOnRight() ? m_contentItem->x() - m_contentSizeHelper.leftPadding()
|
|
: m_sizeHelper.rightX() - m_contentItem->x() - m_contentItem->width() - m_spacing;
|
|
m_quickActionItem->setProperty("availableWidth", availableWidth);
|
|
const auto actionX = showMessageOnRight() && m_contentItem ? m_contentItem->x() - m_quickActionItem->implicitWidth() - m_spacing
|
|
: m_contentItem->x() + m_contentItem->width() + m_spacing;
|
|
const auto actionWidth = std::min(m_quickActionItem->implicitWidth(), availableWidth);
|
|
m_quickActionItem->setPosition(QPointF(actionX, nextY));
|
|
m_quickActionItem->setSize(QSizeF(actionWidth, m_quickActionItem->implicitHeight()));
|
|
yAdd = std::max(yAdd, m_quickActionItem->implicitHeight());
|
|
}
|
|
nextY += yAdd + m_spacing;
|
|
if (m_showReadMarkers && m_readMarkerItem) {
|
|
qreal extraSpacing = m_readMarkerItem->implicitWidth() < m_sizeHelper.availableWidth() - m_spacing ? m_spacing : 0;
|
|
qreal objectWidth = std::min(m_sizeHelper.availableWidth(), m_readMarkerItem->implicitWidth());
|
|
m_readMarkerItem->setPosition(QPointF(m_sizeHelper.rightX() - objectWidth - extraSpacing, nextY));
|
|
m_readMarkerItem->setSize(QSizeF(objectWidth, m_readMarkerItem->implicitHeight()));
|
|
}
|
|
|
|
m_resizingContent = false;
|
|
}
|
|
|
|
void MessageDelegateBase::hoverEnterEvent(QHoverEvent *event)
|
|
{
|
|
m_hovered = true;
|
|
Q_EMIT hoveredChanged();
|
|
event->setAccepted(true);
|
|
updateBackground();
|
|
updateQuickAction();
|
|
}
|
|
|
|
void MessageDelegateBase::hoverMoveEvent(QHoverEvent *event)
|
|
{
|
|
bool oldHovered = m_hovered;
|
|
m_hovered = contains(event->position());
|
|
if (oldHovered != m_hovered) {
|
|
Q_EMIT hoveredChanged();
|
|
}
|
|
event->setAccepted(true);
|
|
updateBackground();
|
|
updateQuickAction();
|
|
}
|
|
|
|
void MessageDelegateBase::hoverLeaveEvent(QHoverEvent *event)
|
|
{
|
|
m_hovered = false;
|
|
Q_EMIT hoveredChanged();
|
|
event->setAccepted(true);
|
|
updateBackground();
|
|
updateQuickAction();
|
|
}
|
|
|
|
bool MessageDelegateBase::isTemporaryHighlighted() const
|
|
{
|
|
return m_temporaryHighlightTimer && m_temporaryHighlightTimer->isActive();
|
|
}
|
|
|
|
void MessageDelegateBase::setIsTemporaryHighlighted(bool isTemporaryHighlighted)
|
|
{
|
|
if (!isTemporaryHighlighted) {
|
|
if (m_temporaryHighlightTimer) {
|
|
m_temporaryHighlightTimer->stop();
|
|
m_temporaryHighlightTimer->deleteLater();
|
|
Q_EMIT isTemporaryHighlightedChanged();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!m_temporaryHighlightTimer) {
|
|
m_temporaryHighlightTimer = new QTimer(this);
|
|
}
|
|
m_temporaryHighlightTimer->start(1500);
|
|
connect(m_temporaryHighlightTimer, &QTimer::timeout, this, [this]() {
|
|
m_temporaryHighlightTimer->stop();
|
|
m_temporaryHighlightTimer->deleteLater();
|
|
Q_EMIT isTemporaryHighlightedChanged();
|
|
});
|
|
Q_EMIT isTemporaryHighlightedChanged();
|
|
}
|
|
|
|
bool MessageDelegateBase::hovered() const
|
|
{
|
|
return m_hovered;
|
|
}
|
|
|
|
#include "moc_messagedelegate.cpp"
|