Add support for copying & deleting multiple messages at once
BUG: 496458
This commit is contained in:
committed by
Joshua Goins
parent
0f634ff795
commit
f5d726989f
@@ -229,6 +229,58 @@ Kirigami.Page {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
id: selectedMessagesControl
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
showCloseButton: false
|
||||
visible: root.currentRoom?.selectedMessageCount > 0
|
||||
position: Kirigami.InlineMessage.Position.Header
|
||||
type: Kirigami.MessageType.Positive
|
||||
icon.name: "edit-select-all-symbolic"
|
||||
|
||||
text: i18nc("@info", "Selected Messages: %1", root.currentRoom?.selectedMessageCount)
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action:button", "Copy Conversation")
|
||||
icon.name: "edit-copy"
|
||||
onTriggered: {
|
||||
Clipboard.saveText(root.currentRoom.getFormattedSelectedMessages())
|
||||
showPassiveNotification(i18nc("@info", "Conversation copied to clipboard"));
|
||||
}
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action:button", "Delete Messages")
|
||||
icon.name: "trash-empty-symbolic"
|
||||
icon.color: Kirigami.Theme.negativeTextColor
|
||||
enabled: root.currentRoom?.canDeleteSelectedMessages
|
||||
onTriggered: {
|
||||
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ReasonDialog'), {
|
||||
title: i18nc("@title:dialog", "Remove Messages"),
|
||||
placeholder: i18nc("@info:placeholder", "Optionally give a reason for removing these messages"),
|
||||
actionText: i18nc("@action:button 'Remove' as in 'Remove these messages'", "Remove"),
|
||||
icon: "delete",
|
||||
reporting: false,
|
||||
connection: root.currentRoom.connection,
|
||||
}, {
|
||||
title: i18nc("@title:dialog", "Remove Messages"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
}) as ReasonDialog;
|
||||
dialog.accepted.connect(reason => {
|
||||
root.currentRoom.deleteSelectedMessages(reason);
|
||||
});
|
||||
}
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "dialog-close"
|
||||
icon.color: Kirigami.Theme.negativeTextColor
|
||||
onTriggered: root.currentRoom.clearSelectedMessages()
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
id: banner
|
||||
|
||||
|
||||
@@ -604,6 +604,7 @@ QString RoomManager::findSpaceIdForCurrentRoom() const
|
||||
void RoomManager::setCurrentRoom(const QString &roomId)
|
||||
{
|
||||
if (m_currentRoom != nullptr) {
|
||||
m_currentRoom->clearSelectedMessages();
|
||||
m_currentRoom->disconnect(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@
|
||||
#include <KJobTrackerInterface>
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <ranges>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
std::function<bool(const Quotient::RoomEvent *)> NeoChatRoom::m_hiddenFilter = [](const Quotient::RoomEvent *) -> bool {
|
||||
@@ -630,7 +632,14 @@ bool NeoChatRoom::isUserBanned(const QString &user) const
|
||||
|
||||
void NeoChatRoom::deleteMessagesByUser(const QString &user, const QString &reason)
|
||||
{
|
||||
doDeleteMessagesByUser(user, reason);
|
||||
QStringList events;
|
||||
for (const auto &event : messageEvents()) {
|
||||
if (event->senderId() == user && !event->isRedacted() && !event.viewAs<RedactionEvent>() && !event->isStateEvent()) {
|
||||
events += event->id();
|
||||
}
|
||||
}
|
||||
|
||||
doDeleteMessageIds(events, reason);
|
||||
}
|
||||
|
||||
QString NeoChatRoom::historyVisibility() const
|
||||
@@ -761,16 +770,10 @@ void NeoChatRoom::setUserPowerLevel(const QString &userID, const int &powerLevel
|
||||
}
|
||||
}
|
||||
|
||||
QCoro::Task<void> NeoChatRoom::doDeleteMessagesByUser(const QString &user, QString reason)
|
||||
QCoro::Task<void> NeoChatRoom::doDeleteMessageIds(const QStringList eventIds, QString reason)
|
||||
{
|
||||
QStringList events;
|
||||
for (const auto &event : messageEvents()) {
|
||||
if (event->senderId() == user && !event->isRedacted() && !event.viewAs<RedactionEvent>() && !event->isStateEvent()) {
|
||||
events += event->id();
|
||||
}
|
||||
}
|
||||
for (const auto &e : events) {
|
||||
auto job = connection()->callApi<RedactEventJob>(id(), QString::fromLatin1(QUrl::toPercentEncoding(e)), connection()->generateTxnId(), reason);
|
||||
for (const auto &eventId : eventIds) {
|
||||
auto job = connection()->callApi<RedactEventJob>(id(), eventId, connection()->generateTxnId(), reason);
|
||||
co_await qCoro(job.get(), &BaseJob::finished);
|
||||
if (job->error() != BaseJob::Success) {
|
||||
qWarning() << "Error: \"" << job->error() << "\" while deleting messages. Aborting";
|
||||
@@ -1963,4 +1966,96 @@ QList<QString> NeoChatRoom::sortedMemberIds() const
|
||||
return m_sortedMemberIds;
|
||||
}
|
||||
|
||||
int NeoChatRoom::selectedMessageCount() const
|
||||
{
|
||||
return m_selectedMessageIds.size();
|
||||
}
|
||||
|
||||
bool NeoChatRoom::canDeleteSelectedMessages() const
|
||||
{
|
||||
if (canSendState("redact"_L1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const QString localUserId = connection()->userId();
|
||||
return std::ranges::all_of(m_selectedMessageIds, [this, localUserId](const QString &eventId) {
|
||||
const auto eventIt = findInTimeline(eventId);
|
||||
if (eventIt == historyEdge()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const RoomEvent *event = eventIt->event();
|
||||
return event && (event->senderId() == localUserId);
|
||||
});
|
||||
}
|
||||
|
||||
bool NeoChatRoom::isMessageSelected(const QString &eventId) const
|
||||
{
|
||||
return m_selectedMessageIds.contains(eventId);
|
||||
}
|
||||
|
||||
void NeoChatRoom::toggleMessageSelection(const QString &eventId)
|
||||
{
|
||||
if (!m_selectedMessageIds.remove(eventId)) {
|
||||
m_selectedMessageIds.insert(eventId);
|
||||
}
|
||||
|
||||
Q_EMIT selectionChanged();
|
||||
}
|
||||
|
||||
QString NeoChatRoom::getFormattedSelectedMessages() const
|
||||
{
|
||||
QVector<const RoomEvent *> events;
|
||||
events.reserve(m_selectedMessageIds.size());
|
||||
|
||||
std::ranges::copy(m_selectedMessageIds | std::views::transform([this](const QString &eventId) -> const RoomEvent * {
|
||||
const auto eventIt = findInTimeline(eventId);
|
||||
return eventIt != historyEdge() ? eventIt->event() : nullptr;
|
||||
}) | std::views::filter([](const RoomEvent *event) {
|
||||
return event != nullptr;
|
||||
}),
|
||||
std::back_inserter(events));
|
||||
|
||||
std::ranges::sort(events, {}, &RoomEvent::originTimestamp);
|
||||
|
||||
QString formattedContent;
|
||||
formattedContent.reserve(events.size() * 256); // estimate an average of 256 characters per message
|
||||
|
||||
for (const RoomEvent *event : events) {
|
||||
formattedContent += EventHandler::authorDisplayName(this, event);
|
||||
formattedContent += u" — "_s;
|
||||
formattedContent += EventHandler::dateTime(this, event).shortDateTime();
|
||||
formattedContent += u'\n';
|
||||
formattedContent += EventHandler::plainBody(this, event);
|
||||
formattedContent += u"\n\n"_s;
|
||||
}
|
||||
|
||||
return formattedContent.trimmed();
|
||||
}
|
||||
|
||||
void NeoChatRoom::deleteSelectedMessages(const QString &reason)
|
||||
{
|
||||
QStringList events;
|
||||
for (const auto &eventId : m_selectedMessageIds) {
|
||||
const auto eventIt = findInTimeline(eventId);
|
||||
if (eventIt == historyEdge()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const RoomEvent *event = eventIt->event();
|
||||
if (event && !event->isRedacted() && !is<RedactionEvent>(*event)) {
|
||||
events += eventId;
|
||||
}
|
||||
}
|
||||
|
||||
doDeleteMessageIds(events, reason);
|
||||
clearSelectedMessages();
|
||||
}
|
||||
|
||||
void NeoChatRoom::clearSelectedMessages()
|
||||
{
|
||||
m_selectedMessageIds.clear();
|
||||
Q_EMIT selectionChanged();
|
||||
}
|
||||
|
||||
#include "moc_neochatroom.cpp"
|
||||
|
||||
@@ -220,6 +220,16 @@ class NeoChatRoom : public Quotient::Room
|
||||
*/
|
||||
Q_PROPERTY(bool spaceHasUnreadMessages READ spaceHasUnreadMessages NOTIFY spaceHasUnreadMessagesChanged)
|
||||
|
||||
/**
|
||||
* @brief The number of selected messages in the room.
|
||||
*/
|
||||
Q_PROPERTY(int selectedMessageCount READ selectedMessageCount NOTIFY selectionChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the user can delete the selected messages.
|
||||
*/
|
||||
Q_PROPERTY(bool canDeleteSelectedMessages READ canDeleteSelectedMessages NOTIFY selectionChanged)
|
||||
|
||||
public:
|
||||
explicit NeoChatRoom(Quotient::Connection *connection, QString roomId, Quotient::JoinState joinState = {});
|
||||
|
||||
@@ -676,6 +686,41 @@ public:
|
||||
*/
|
||||
QList<QString> sortedMemberIds() const;
|
||||
|
||||
/**
|
||||
* @brief The number of selected messages in the room.
|
||||
*/
|
||||
int selectedMessageCount() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the user can delete the selected messages.
|
||||
*/
|
||||
bool canDeleteSelectedMessages() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the given message is selected.
|
||||
*/
|
||||
Q_INVOKABLE bool isMessageSelected(const QString &eventId) const;
|
||||
|
||||
/**
|
||||
* @brief Toggle the selection state of the given message.
|
||||
*/
|
||||
Q_INVOKABLE void toggleMessageSelection(const QString &eventId);
|
||||
|
||||
/**
|
||||
* @brief Get the content of the selected messages formatted as a single string.
|
||||
*/
|
||||
Q_INVOKABLE QString getFormattedSelectedMessages() const;
|
||||
|
||||
/**
|
||||
* @brief Delete the selected messages with an optional reason.
|
||||
*/
|
||||
Q_INVOKABLE void deleteSelectedMessages(const QString &reason = QString());
|
||||
|
||||
/**
|
||||
* @brief Clear the selection of messages.
|
||||
*/
|
||||
Q_INVOKABLE void clearSelectedMessages();
|
||||
|
||||
private:
|
||||
bool m_visible = false;
|
||||
|
||||
@@ -693,7 +738,7 @@ private:
|
||||
void onAddHistoricalTimelineEvents(rev_iter_t from) override;
|
||||
void onRedaction(const Quotient::RoomEvent &prevEvent, const Quotient::RoomEvent &after) override;
|
||||
|
||||
QCoro::Task<void> doDeleteMessagesByUser(const QString &user, QString reason);
|
||||
QCoro::Task<void> doDeleteMessageIds(const QStringList eventIds, QString reason);
|
||||
QCoro::Task<void> doUploadFile(QUrl url, QString body = QString(), std::optional<Quotient::EventRelation> relatesTo = std::nullopt);
|
||||
|
||||
std::unique_ptr<Quotient::RoomEvent> m_cachedEvent;
|
||||
@@ -713,6 +758,7 @@ private:
|
||||
|
||||
QString m_lastUnreadHighlightId;
|
||||
QList<QString> m_sortedMemberIds;
|
||||
QSet<QString> m_selectedMessageIds;
|
||||
|
||||
private Q_SLOTS:
|
||||
void updatePushNotificationState(QString type);
|
||||
@@ -752,6 +798,7 @@ Q_SIGNALS:
|
||||
void pinnedMessageChanged();
|
||||
void highlightCycleStartedChanged();
|
||||
void spaceHasUnreadMessagesChanged();
|
||||
void selectionChanged();
|
||||
|
||||
/**
|
||||
* @brief Request a message be shown to the user of the given type.
|
||||
|
||||
@@ -407,6 +407,13 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
onTriggered: pinned ? root.room.unpinEvent(root.eventId) : root.room.pinEvent(root.eventId)
|
||||
}
|
||||
|
||||
Kirigami.Action {
|
||||
visible: root.messageComponentType !== MessageComponentType.Other
|
||||
text: root.room.selectedMessageCount > 0 && root.room.isMessageSelected(root.eventId) ? i18nc("@action:inmenu", "Deselect Message") : i18nc("@action:inmenu", "Select Message")
|
||||
icon.name: "edit-select-all-symbolic"
|
||||
onTriggered: root.room.toggleMessageSelection(root.eventId)
|
||||
}
|
||||
|
||||
Kirigami.Action {
|
||||
separator: true
|
||||
visible: viewSourceAction.visible
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
|
||||
import Qt.labs.qmlmodels
|
||||
@@ -16,6 +18,11 @@ DelegateChooser {
|
||||
*/
|
||||
required property NeoChatRoom room
|
||||
|
||||
/**
|
||||
* @brief Whether to show selection controls for message delegate.
|
||||
*/
|
||||
property bool showSelectionControl: false
|
||||
|
||||
role: "delegateType"
|
||||
|
||||
DelegateChoice {
|
||||
@@ -25,7 +32,9 @@ DelegateChooser {
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: DelegateType.Message
|
||||
delegate: MessageDelegate {}
|
||||
delegate: MessageDelegate {
|
||||
showSelectionControl: root.showSelectionControl
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
|
||||
@@ -5,6 +5,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||
@@ -109,6 +110,16 @@ MessageDelegateBase {
|
||||
*/
|
||||
property bool showHighlight: root.isHighlighted || isTemporaryHighlighted
|
||||
|
||||
/**
|
||||
* @brief Whether the message is selected.
|
||||
*/
|
||||
property bool selected: root.room.selectedMessageCount > 0 && room.isMessageSelected(eventId)
|
||||
|
||||
/**
|
||||
* @brief Whether to show selection controls for this message.
|
||||
*/
|
||||
property bool showSelectionControl: false
|
||||
|
||||
Message.room: root.room
|
||||
Message.timeline: root.ListView.view
|
||||
Message.contentModel: root.contentModel
|
||||
@@ -120,6 +131,7 @@ MessageDelegateBase {
|
||||
enableAvatars: NeoChatConfig?.showAvatarInTimeline ?? false
|
||||
compactMode: NeoChatConfig?.compactLayout ?? false
|
||||
showLocalMessagesOnRight: NeoChatConfig.showLocalMessagesOnRight
|
||||
showSelection: root.showSelectionControl && room.selectedMessageCount > 0
|
||||
|
||||
contentItem: Bubble {
|
||||
id: bubble
|
||||
@@ -230,6 +242,20 @@ MessageDelegateBase {
|
||||
author: root.author
|
||||
}
|
||||
|
||||
selectionComponent: RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
implicitHeight: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2
|
||||
|
||||
QQC2.CheckBox {
|
||||
checked: root.selected
|
||||
onClicked: root.room.toggleMessageSelection(root.eventId)
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: _private
|
||||
|
||||
|
||||
@@ -213,6 +213,7 @@ QQC2.ScrollView {
|
||||
model: root.messageFilterModel
|
||||
delegate: EventDelegate {
|
||||
room: _private.room
|
||||
showSelectionControl: true
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
@@ -138,7 +138,10 @@ void MessageDelegateBase::setPercentageValues(bool fillWidth)
|
||||
|
||||
void MessageDelegateBase::setContentPadding()
|
||||
{
|
||||
m_contentSizeHelper.setLeftPadding(m_sizeHelper.leftX() + (leaveAvatarSpace() ? m_avatarSize + m_spacing : 0));
|
||||
qreal selectionOffset = (m_showSelection && m_selectionItem) ? m_selectionItem->implicitWidth() + (m_spacing * 2) : 0;
|
||||
qreal avatarOffset = (leaveAvatarSpace() ? m_avatarSize + m_spacing : 0);
|
||||
|
||||
m_contentSizeHelper.setLeftPadding(m_sizeHelper.leftX() + selectionOffset + avatarOffset);
|
||||
m_contentSizeHelper.setRightPadding(m_sizeHelper.rightPadding());
|
||||
}
|
||||
|
||||
@@ -539,6 +542,77 @@ void MessageDelegateBase::updateQuickAction()
|
||||
m_quickActionComponent->create(*quickActionIncubator, qmlContext(m_quickActionComponent));
|
||||
}
|
||||
|
||||
QQmlComponent *MessageDelegateBase::selectionComponent() const
|
||||
{
|
||||
return m_selectionComponent;
|
||||
}
|
||||
|
||||
void MessageDelegateBase::setSelectionComponent(QQmlComponent *selectionComponent)
|
||||
{
|
||||
if (selectionComponent == m_selectionComponent) {
|
||||
return;
|
||||
}
|
||||
m_selectionComponent = selectionComponent;
|
||||
Q_EMIT selectionComponentChanged();
|
||||
|
||||
updateSelection();
|
||||
}
|
||||
|
||||
bool MessageDelegateBase::showSelection() const
|
||||
{
|
||||
return m_showSelection;
|
||||
}
|
||||
|
||||
void MessageDelegateBase::setShowSelection(bool showSelection)
|
||||
{
|
||||
if (showSelection == m_showSelection) {
|
||||
return;
|
||||
}
|
||||
m_showSelection = showSelection;
|
||||
Q_EMIT showSelectionChanged();
|
||||
|
||||
updateSelection();
|
||||
}
|
||||
|
||||
void MessageDelegateBase::updateSelection()
|
||||
{
|
||||
if (m_selectionComponent && showSelection() && !m_selectionItem && !m_selectionIncubating) {
|
||||
const auto selectionIncubator = new MessageObjectIncubator(
|
||||
m_objectInitialCallback,
|
||||
[this](MessageObjectIncubator *incubator) {
|
||||
if (!incubator) {
|
||||
return;
|
||||
}
|
||||
const auto selectionObject = qobject_cast<QQuickItem *>(incubator->object());
|
||||
if (selectionObject) {
|
||||
// The setting may have changed during the incubation period.
|
||||
if (showSelection()) {
|
||||
m_selectionItem = selectionObject;
|
||||
} else {
|
||||
cleanupItem(selectionObject);
|
||||
}
|
||||
setContentPadding();
|
||||
markAsDirty();
|
||||
}
|
||||
m_selectionIncubating = false;
|
||||
// We can't cleanup the incubator in the completedCallback otherwise
|
||||
// we use after free when we return to the status changed function
|
||||
// of that incubator
|
||||
QTimer::singleShot(0, this, [this, incubator]() {
|
||||
cleanupIncubator(incubator);
|
||||
});
|
||||
},
|
||||
m_errorCallback);
|
||||
m_activeIncubators.push_back(selectionIncubator);
|
||||
m_selectionComponent->create(*selectionIncubator, qmlContext(m_selectionComponent));
|
||||
m_selectionIncubating = true;
|
||||
} else if (!showSelection() && m_selectionItem) {
|
||||
cleanupItem(m_selectionItem);
|
||||
setContentPadding();
|
||||
markAsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageDelegateBase::showLocalMessagesOnRight() const
|
||||
{
|
||||
return m_showLocalMessagesOnRight;
|
||||
@@ -623,10 +697,17 @@ void MessageDelegateBase::resizeContent()
|
||||
nextY += m_sectionItem->implicitHeight() + m_spacing;
|
||||
}
|
||||
qreal yAdd = 0.0;
|
||||
if (m_showSelection && m_selectionItem) {
|
||||
m_selectionItem->setPosition(QPointF(m_sizeHelper.leftX(), nextY));
|
||||
m_selectionItem->setSize(QSizeF(m_selectionItem->implicitWidth(), m_selectionItem->implicitHeight()));
|
||||
yAdd = m_selectionItem->implicitHeight();
|
||||
}
|
||||
if (showAvatar() && m_avatarItem) {
|
||||
m_avatarItem->setPosition(QPointF(m_sizeHelper.leftX(), nextY));
|
||||
m_avatarItem->setPosition(
|
||||
QPointF(m_showSelection && m_selectionItem ? m_sizeHelper.leftX() + m_selectionItem->implicitWidth() + (m_spacing * 2) : m_sizeHelper.leftX(),
|
||||
nextY));
|
||||
m_avatarItem->setSize(QSizeF(m_avatarItem->implicitWidth(), m_avatarItem->implicitHeight()));
|
||||
yAdd = m_avatarItem->implicitWidth();
|
||||
yAdd = std::max(yAdd, m_avatarItem->implicitHeight());
|
||||
}
|
||||
if (m_contentItem) {
|
||||
const auto contentItemWidth =
|
||||
|
||||
@@ -101,6 +101,16 @@ class MessageDelegateBase : public TimelineDelegate
|
||||
*/
|
||||
Q_PROPERTY(QQmlComponent *quickActionComponent READ quickActionComponent WRITE setQuickActionComponent NOTIFY quickActionComponentChanged FINAL)
|
||||
|
||||
/**
|
||||
* @brief The component to use to visualize message selection.
|
||||
*/
|
||||
Q_PROPERTY(QQmlComponent *selectionComponent READ selectionComponent WRITE setSelectionComponent NOTIFY selectionComponentChanged FINAL)
|
||||
|
||||
/**
|
||||
* @brief Whether to show the selection component.
|
||||
*/
|
||||
Q_PROPERTY(bool showSelection READ showSelection WRITE setShowSelection NOTIFY showSelectionChanged FINAL REQUIRED)
|
||||
|
||||
/**
|
||||
* @brief Whether to use the compact mode appearance.
|
||||
*/
|
||||
@@ -161,6 +171,11 @@ public:
|
||||
QQmlComponent *quickActionComponent() const;
|
||||
void setQuickActionComponent(QQmlComponent *quickActionComponent);
|
||||
|
||||
QQmlComponent *selectionComponent() const;
|
||||
void setSelectionComponent(QQmlComponent *selectionComponent);
|
||||
bool showSelection() const;
|
||||
void setShowSelection(bool showSelection);
|
||||
|
||||
bool showLocalMessagesOnRight() const;
|
||||
void setShowLocalMessagesOnRight(bool showLocalMessagesOnRight);
|
||||
|
||||
@@ -182,6 +197,8 @@ Q_SIGNALS:
|
||||
void showReadMarkersChanged();
|
||||
void compactBackgroundComponentChanged();
|
||||
void quickActionComponentChanged();
|
||||
void selectionComponentChanged();
|
||||
void showSelectionChanged();
|
||||
void compactModeChanged();
|
||||
void showLocalMessagesOnRightChanged();
|
||||
void isTemporaryHighlightedChanged();
|
||||
@@ -227,6 +244,12 @@ private:
|
||||
bool m_quickActionIncubating = false;
|
||||
QPointer<QQuickItem> m_quickActionItem;
|
||||
|
||||
QPointer<QQmlComponent> m_selectionComponent;
|
||||
bool m_selectionIncubating = false;
|
||||
QPointer<QQuickItem> m_selectionItem;
|
||||
bool m_showSelection = false;
|
||||
void updateSelection();
|
||||
|
||||
bool m_showLocalMessagesOnRight = true;
|
||||
|
||||
bool m_hovered = false;
|
||||
|
||||
Reference in New Issue
Block a user