Yeet HoverActions into the sun
Replace HoverActions with an inline action component that appears on hover. There are only actions for reply and react if there is space the overflow button opens the normal message menu.
NOTE: the most recent update changes things slightly, from the images below the buttons are now top aligned because of potentially hige messages. The actions are also now disabled for compact mode as they never really made sense there anyway. The menu now has all options so no one is missing out.
For normal messages
{width=419 height=138}
When space is limited
{width=411 height=130}
User messages
{width=296 height=114}
BUG: 503784
This commit is contained in:
@@ -101,7 +101,7 @@ parts:
|
|||||||
- olm
|
- olm
|
||||||
- qtkeychain
|
- qtkeychain
|
||||||
source: https://github.com/quotient-im/libQuotient.git
|
source: https://github.com/quotient-im/libQuotient.git
|
||||||
source-tag: 0.9.1
|
source-tag: 0.9.2
|
||||||
source-depth: 1
|
source-depth: 1
|
||||||
plugin: cmake
|
plugin: cmake
|
||||||
build-environment:
|
build-environment:
|
||||||
|
|||||||
@@ -37,6 +37,9 @@
|
|||||||
#include <Quotient/events/simplestateevents.h>
|
#include <Quotient/events/simplestateevents.h>
|
||||||
#include <Quotient/jobs/downloadfilejob.h>
|
#include <Quotient/jobs/downloadfilejob.h>
|
||||||
#include <Quotient/qt_connection_util.h>
|
#include <Quotient/qt_connection_util.h>
|
||||||
|
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||||
|
#include <Quotient/thread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "chatbarcache.h"
|
#include "chatbarcache.h"
|
||||||
#include "clipboard.h"
|
#include "clipboard.h"
|
||||||
@@ -1708,4 +1711,34 @@ bool NeoChatRoom::isEventPinned(const QString &eventId) const
|
|||||||
return pinnedEventIds().contains(eventId);
|
return pinnedEventIds().contains(eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NeoChatRoom::eventIsThreaded(const QString &eventId) const
|
||||||
|
{
|
||||||
|
const auto event = eventCast<const RoomMessageEvent>(getEvent(eventId).first);
|
||||||
|
if (event == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||||
|
return event->isThreaded() || threads().contains(eventId);
|
||||||
|
#else
|
||||||
|
return event->isThreaded();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString NeoChatRoom::rootIdForThread(const QString &eventId) const
|
||||||
|
{
|
||||||
|
const auto event = eventCast<const RoomMessageEvent>(getEvent(eventId).first);
|
||||||
|
if (event == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rootId = event->threadRootEventId();
|
||||||
|
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||||
|
if (rootId.isEmpty() && threads().contains(eventId)) {
|
||||||
|
rootId = event->id();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return rootId;
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_neochatroom.cpp"
|
#include "moc_neochatroom.cpp"
|
||||||
|
|||||||
@@ -576,6 +576,17 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE bool isEventPinned(const QString &eventId) const;
|
Q_INVOKABLE bool isEventPinned(const QString &eventId) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if the given @p eventId is threaded.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE bool eventIsThreaded(const QString &eventId) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the thread root ID for @p eventId as a string. The string
|
||||||
|
* is empty if the event is not part of a thread.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE QString rootIdForThread(const QString &eventId) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_visible = false;
|
bool m_visible = false;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ ecm_add_qml_module(Timeline GENERATE_PLUGIN_SOURCE
|
|||||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/timeline
|
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/timeline
|
||||||
QML_FILES
|
QML_FILES
|
||||||
TimelineView.qml
|
TimelineView.qml
|
||||||
HoverActions.qml
|
|
||||||
EventDelegate.qml
|
EventDelegate.qml
|
||||||
HiddenDelegate.qml
|
HiddenDelegate.qml
|
||||||
MessageDelegate.qml
|
MessageDelegate.qml
|
||||||
@@ -20,6 +19,7 @@ ecm_add_qml_module(Timeline GENERATE_PLUGIN_SOURCE
|
|||||||
Bubble.qml
|
Bubble.qml
|
||||||
AvatarFlow.qml
|
AvatarFlow.qml
|
||||||
SectionDelegate.qml
|
SectionDelegate.qml
|
||||||
|
QuickActions.qml
|
||||||
BaseMessageComponentChooser.qml
|
BaseMessageComponentChooser.qml
|
||||||
MessageComponentChooser.qml
|
MessageComponentChooser.qml
|
||||||
ReplyMessageComponentChooser.qml
|
ReplyMessageComponentChooser.qml
|
||||||
|
|||||||
@@ -104,6 +104,19 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component ReplyThreadMessageAction: QQC2.Action {
|
||||||
|
text: i18nc("@action:button", "Reply in Thread")
|
||||||
|
icon.name: "dialog-messages"
|
||||||
|
onTriggered: {
|
||||||
|
currentRoom.threadCache.replyId = "";
|
||||||
|
currentRoom.threadCache.threadId = currentRoom.eventIsThreaded(root.eventId) ? currentRoom.rootIdForThread(root.eventId) : root.eventId;
|
||||||
|
currentRoom.mainCache.clearRelations();
|
||||||
|
currentRoom.editCache.clearRelations();
|
||||||
|
RoomManager.requestFullScreenClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
component ReportMessageAction: Kirigami.Action {
|
component ReportMessageAction: Kirigami.Action {
|
||||||
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
|
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
|
||||||
icon.name: "dialog-warning-symbolic"
|
icon.name: "dialog-warning-symbolic"
|
||||||
|
|||||||
@@ -1,181 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls as QQC2
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
|
||||||
|
|
||||||
import org.kde.neochat
|
|
||||||
import org.kde.neochat.chatbar
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A component that provides a set of actions when a message is hovered in the timeline.
|
|
||||||
*
|
|
||||||
* There is also an icon to show that a message has come from a verified device in
|
|
||||||
* encrypted chats.
|
|
||||||
*/
|
|
||||||
QQC2.Control {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The current message delegate the actions are being shown on.
|
|
||||||
*/
|
|
||||||
property var delegate: null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The current room that user is viewing.
|
|
||||||
*/
|
|
||||||
required property NeoChatRoom currentRoom
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Whether the actions should be shown.
|
|
||||||
*/
|
|
||||||
readonly property bool showActions: delegate && delegate.hovered
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Request that the chat bar be focussed.
|
|
||||||
*/
|
|
||||||
signal focusChatBar
|
|
||||||
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
|
|
||||||
visible: (root.hovered || root.showActions || showActionsTimer.running) && !Kirigami.Settings.isMobile && (!root.delegate.isThreaded || !NeoChatConfig.threads)
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) {
|
|
||||||
// HACK: delay disapearing by 200ms, otherwise this can create some glitches
|
|
||||||
// See https://invent.kde.org/network/neochat/-/issues/333
|
|
||||||
showActionsTimer.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Timer {
|
|
||||||
id: showActionsTimer
|
|
||||||
interval: 200
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePosition(): void {
|
|
||||||
if (delegate) {
|
|
||||||
root.x = delegate.contentItem.x + delegate.bubbleWidth - root.implicitWidth - Kirigami.Units.largeSpacing;
|
|
||||||
root.y = delegate.mapToItem(parent, 0, 0).y + delegate.bubbleY - height + Kirigami.Units.smallSpacing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onDelegateChanged: updatePosition()
|
|
||||||
onWidthChanged: updatePosition()
|
|
||||||
|
|
||||||
contentItem: RowLayout {
|
|
||||||
id: actionsLayout
|
|
||||||
|
|
||||||
spacing: Kirigami.Units.smallSpacing
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Kirigami.Icon {
|
|
||||||
source: "security-high"
|
|
||||||
width: height
|
|
||||||
height: root.height
|
|
||||||
visible: root.delegate && root.delegate.verified
|
|
||||||
HoverHandler {
|
|
||||||
id: hover
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.ToolTip.text: i18n("This message was sent from a verified device")
|
|
||||||
QQC2.ToolTip.visible: hover.hovered
|
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.Button {
|
|
||||||
text: i18n("React")
|
|
||||||
icon.name: "preferences-desktop-emoticons"
|
|
||||||
onClicked: emojiDialog.open()
|
|
||||||
display: QQC2.ToolButton.IconOnly
|
|
||||||
|
|
||||||
QQC2.ToolTip.text: text
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.Button {
|
|
||||||
visible: root.delegate && root.delegate.isEditable && !root.currentRoom.readOnly
|
|
||||||
text: i18n("Edit")
|
|
||||||
icon.name: "document-edit"
|
|
||||||
display: QQC2.Button.IconOnly
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.currentRoom.editCache.editId = root.delegate.eventId;
|
|
||||||
root.currentRoom.mainCache.replyId = "";
|
|
||||||
root.currentRoom.mainCache.threadId = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.ToolTip.text: text
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.Button {
|
|
||||||
visible: !root.currentRoom.readOnly
|
|
||||||
text: i18n("Reply")
|
|
||||||
icon.name: "mail-replied-symbolic"
|
|
||||||
display: QQC2.Button.IconOnly
|
|
||||||
onClicked: {
|
|
||||||
root.currentRoom.mainCache.replyId = root.delegate.eventId;
|
|
||||||
root.currentRoom.editCache.editId = "";
|
|
||||||
root.currentRoom.mainCache.threadId = "";
|
|
||||||
root.focusChatBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.ToolTip.text: text
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.Button {
|
|
||||||
visible: NeoChatConfig.threads && !root.currentRoom.readOnly && !root.delegate?.isPoll
|
|
||||||
text: i18n("Reply in Thread")
|
|
||||||
icon.name: "dialog-messages"
|
|
||||||
display: QQC2.Button.IconOnly
|
|
||||||
onClicked: {
|
|
||||||
root.currentRoom.threadCache.replyId = "";
|
|
||||||
root.currentRoom.threadCache.threadId = root.delegate.isThreaded ? root.delegate.threadRoot : root.delegate.eventId;
|
|
||||||
root.currentRoom.mainCache.clearRelations();
|
|
||||||
root.currentRoom.editCache.clearRelations();
|
|
||||||
root.focusChatBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.ToolTip.text: text
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.Button {
|
|
||||||
visible: (root.delegate?.isPoll ?? false) && !ContentProvider.handlerForPoll(root.currentRoom, root.delegate.eventId).hasEnded
|
|
||||||
text: i18n("End Poll")
|
|
||||||
icon.name: "gtk-stop"
|
|
||||||
display: QQC2.ToolButton.IconOnly
|
|
||||||
onClicked: root.currentRoom.poll(root.delegate.eventId).endPoll()
|
|
||||||
|
|
||||||
QQC2.ToolTip.text: text
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
EmojiDialog {
|
|
||||||
id: emojiDialog
|
|
||||||
currentRoom: root.currentRoom
|
|
||||||
showQuickReaction: true
|
|
||||||
showStickers: false
|
|
||||||
onChosen: emoji => {
|
|
||||||
root.currentRoom.toggleReaction(root.delegate.eventId, emoji);
|
|
||||||
if (!Kirigami.Settings.isMobile) {
|
|
||||||
root.focusChatBar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -89,20 +89,6 @@ MessageDelegateBase {
|
|||||||
*/
|
*/
|
||||||
required property bool verified
|
required property bool verified
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The y position of the message bubble.
|
|
||||||
*
|
|
||||||
* @note Used for positioning the hover actions.
|
|
||||||
*/
|
|
||||||
readonly property alias bubbleY: bubble.y
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The width of the message bubble.
|
|
||||||
*
|
|
||||||
* @note Used for sizing the hover actions.
|
|
||||||
*/
|
|
||||||
readonly property alias bubbleWidth: bubble.width
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Open the any message media externally.
|
* @brief Open the any message media externally.
|
||||||
*/
|
*/
|
||||||
@@ -202,17 +188,9 @@ MessageDelegateBase {
|
|||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
}
|
}
|
||||||
|
|
||||||
// show hover actions
|
quickActionComponent: QuickActions {
|
||||||
onHoveredChanged: {
|
room: root.room
|
||||||
if (hovered && !Kirigami.Settings.isMobile) {
|
eventId: root.eventId
|
||||||
root.setHoverActionsToDelegate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setHoverActionsToDelegate() {
|
|
||||||
if (ListView.view.setHoverActionsToDelegate) {
|
|
||||||
ListView.view.setHoverActionsToDelegate(root);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ DelegateContextMenu {
|
|||||||
|
|
||||||
DelegateContextMenu.ReplyMessageAction {}
|
DelegateContextMenu.ReplyMessageAction {}
|
||||||
|
|
||||||
|
DelegateContextMenu.ReplyThreadMessageAction {}
|
||||||
|
|
||||||
QQC2.Action {
|
QQC2.Action {
|
||||||
text: i18nc("@action:inmenu As in 'Forward this message'", "Forward…")
|
text: i18nc("@action:inmenu As in 'Forward this message'", "Forward…")
|
||||||
icon.name: "mail-forward-symbolic"
|
icon.name: "mail-forward-symbolic"
|
||||||
|
|||||||
90
src/timeline/QuickActions.qml
Normal file
90
src/timeline/QuickActions.qml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls as QQC2
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The NeoChatRoom the delegate is being displayed in.
|
||||||
|
*/
|
||||||
|
required property NeoChatRoom room
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The matrix ID of the message event.
|
||||||
|
*/
|
||||||
|
required property string eventId
|
||||||
|
|
||||||
|
property real availableWidth: 0.0
|
||||||
|
|
||||||
|
property bool reacting: false
|
||||||
|
|
||||||
|
QQC2.Button {
|
||||||
|
id: reactButton
|
||||||
|
visible: root.availableWidth > overflowButton.implicitWidth + root.spacing + reactButton.implicitWidth
|
||||||
|
text: i18n("React")
|
||||||
|
icon.name: "preferences-desktop-emoticons"
|
||||||
|
display: QQC2.ToolButton.IconOnly
|
||||||
|
onClicked: {
|
||||||
|
var dialog = emojiDialog.createObject(reactButton);
|
||||||
|
dialog.chosen.connect(emoji => {
|
||||||
|
root.reacting = false;
|
||||||
|
root.room.toggleReaction(root.eventId, emoji);
|
||||||
|
if (!Kirigami.Settings.isMobile) {
|
||||||
|
// root.focusChatBar();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialog.closed.connect(() => {
|
||||||
|
root.reacting = false;
|
||||||
|
})
|
||||||
|
root.reacting = true;
|
||||||
|
dialog.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.ToolTip.text: text
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: emojiDialog
|
||||||
|
EmojiDialog {
|
||||||
|
currentRoom: root.room
|
||||||
|
showQuickReaction: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QQC2.Button {
|
||||||
|
id: replyButton
|
||||||
|
visible: !root.room.readOnly && root.availableWidth > overflowButton.implicitWidth + reactButton.implicitWidth + replyButton.implicitWidth + root.spacing
|
||||||
|
text: i18n("Reply")
|
||||||
|
icon.name: "mail-replied-symbolic"
|
||||||
|
display: QQC2.Button.IconOnly
|
||||||
|
onClicked: {
|
||||||
|
root.room.mainCache.replyId = root.eventId;
|
||||||
|
root.room.editCache.editId = "";
|
||||||
|
root.room.mainCache.threadId = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.ToolTip.text: text
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
}
|
||||||
|
QQC2.Button {
|
||||||
|
id: overflowButton
|
||||||
|
text: i18n("Message menu")
|
||||||
|
icon.name: "overflow-menu"
|
||||||
|
onClicked: _private.showMessageMenu()
|
||||||
|
display: QQC2.ToolButton.IconOnly
|
||||||
|
|
||||||
|
QQC2.ToolTip.text: text
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -288,14 +288,6 @@ QQC2.ScrollView {
|
|||||||
itemAtIndex(index).isTemporaryHighlighted = true;
|
itemAtIndex(index).isTemporaryHighlighted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HoverActions {
|
|
||||||
id: hoverActions
|
|
||||||
currentRoom: root.currentRoom
|
|
||||||
onFocusChatBar: root.focusChatBar()
|
|
||||||
}
|
|
||||||
|
|
||||||
onContentYChanged: hoverActions.updatePosition()
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.timelineModel
|
target: root.timelineModel
|
||||||
|
|
||||||
@@ -360,10 +352,6 @@ QQC2.ScrollView {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setHoverActionsToDelegate(delegate) {
|
|
||||||
hoverActions.delegate = delegate;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function goToLastMessage() {
|
function goToLastMessage() {
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ void MessageDelegateBase::setPercentageValues(bool fillWidth)
|
|||||||
m_contentSizeHelper.setStartPercentWidth(100);
|
m_contentSizeHelper.setStartPercentWidth(100);
|
||||||
m_contentSizeHelper.setEndPercentWidth(100);
|
m_contentSizeHelper.setEndPercentWidth(100);
|
||||||
} else {
|
} else {
|
||||||
m_contentSizeHelper.setStartPercentWidth(90);
|
m_contentSizeHelper.setStartPercentWidth(85);
|
||||||
m_contentSizeHelper.setEndPercentWidth(60);
|
m_contentSizeHelper.setEndPercentWidth(60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,6 +402,7 @@ void MessageDelegateBase::setCompactMode(bool compactMode)
|
|||||||
Q_EMIT maxContentWidthChanged();
|
Q_EMIT maxContentWidthChanged();
|
||||||
|
|
||||||
updateBackground();
|
updateBackground();
|
||||||
|
updateQuickAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageDelegateBase::updateBackground()
|
void MessageDelegateBase::updateBackground()
|
||||||
@@ -435,6 +436,54 @@ void MessageDelegateBase::updateBackground()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_quickActionComponent && !m_compactMode && m_hovered && !m_quickActionItem && !m_quickActionIncubating) {
|
||||||
|
const auto quickActionIncubator = new MessageObjectIncubator(
|
||||||
|
m_objectInitialCallback,
|
||||||
|
[this](MessageObjectIncubator *incubator) {
|
||||||
|
if (!incubator) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto quickActionObject = qobject_cast<QQuickItem *>(incubator->object());
|
||||||
|
if (quickActionObject) {
|
||||||
|
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_quickActionComponent->create(*quickActionIncubator, qmlContext(m_quickActionComponent));
|
||||||
|
m_quickActionIncubating = true;
|
||||||
|
} else if (m_quickActionItem && !m_hovered && !m_quickActionItem->property("reacting").toBool()) {
|
||||||
|
cleanupItem(m_quickActionItem);
|
||||||
|
markAsDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool MessageDelegateBase::showLocalMessagesOnRight() const
|
bool MessageDelegateBase::showLocalMessagesOnRight() const
|
||||||
{
|
{
|
||||||
return m_showLocalMessagesOnRight;
|
return m_showLocalMessagesOnRight;
|
||||||
@@ -462,6 +511,7 @@ void MessageDelegateBase::updateImplicitHeight()
|
|||||||
}
|
}
|
||||||
qreal avatarHeight = 0.0;
|
qreal avatarHeight = 0.0;
|
||||||
qreal contentHeight = 0.0;
|
qreal contentHeight = 0.0;
|
||||||
|
qreal quickActionHeight = 0.0;
|
||||||
if (showAvatar() && m_avatarItem) {
|
if (showAvatar() && m_avatarItem) {
|
||||||
m_avatarItem->setImplicitWidth(m_avatarSize);
|
m_avatarItem->setImplicitWidth(m_avatarSize);
|
||||||
m_avatarItem->setImplicitHeight(m_avatarSize);
|
m_avatarItem->setImplicitHeight(m_avatarSize);
|
||||||
@@ -470,7 +520,10 @@ void MessageDelegateBase::updateImplicitHeight()
|
|||||||
if (m_contentItem) {
|
if (m_contentItem) {
|
||||||
contentHeight = m_contentItem->implicitHeight();
|
contentHeight = m_contentItem->implicitHeight();
|
||||||
}
|
}
|
||||||
implicitHeight += std::max(avatarHeight, contentHeight);
|
if (m_quickActionItem) {
|
||||||
|
quickActionHeight = m_quickActionItem->implicitHeight();
|
||||||
|
}
|
||||||
|
implicitHeight += std::max({avatarHeight, contentHeight, quickActionHeight});
|
||||||
if (avatarHeight > 0 || contentHeight > 0) {
|
if (avatarHeight > 0 || contentHeight > 0) {
|
||||||
numObj++;
|
numObj++;
|
||||||
}
|
}
|
||||||
@@ -528,6 +581,17 @@ void MessageDelegateBase::resizeContent()
|
|||||||
m_contentItem->setSize(QSizeF(contentItemWidth, m_contentItem->implicitHeight()));
|
m_contentItem->setSize(QSizeF(contentItemWidth, m_contentItem->implicitHeight()));
|
||||||
yAdd = std::max(yAdd, 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;
|
nextY += yAdd + m_spacing;
|
||||||
if (m_showReadMarkers && m_readMarkerItem) {
|
if (m_showReadMarkers && m_readMarkerItem) {
|
||||||
qreal extraSpacing = m_readMarkerItem->implicitWidth() < m_sizeHelper.availableWidth() - m_spacing ? m_spacing : 0;
|
qreal extraSpacing = m_readMarkerItem->implicitWidth() < m_sizeHelper.availableWidth() - m_spacing ? m_spacing : 0;
|
||||||
@@ -545,6 +609,7 @@ void MessageDelegateBase::hoverEnterEvent(QHoverEvent *event)
|
|||||||
Q_EMIT hoveredChanged();
|
Q_EMIT hoveredChanged();
|
||||||
event->setAccepted(true);
|
event->setAccepted(true);
|
||||||
updateBackground();
|
updateBackground();
|
||||||
|
updateQuickAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageDelegateBase::hoverMoveEvent(QHoverEvent *event)
|
void MessageDelegateBase::hoverMoveEvent(QHoverEvent *event)
|
||||||
@@ -556,6 +621,7 @@ void MessageDelegateBase::hoverMoveEvent(QHoverEvent *event)
|
|||||||
}
|
}
|
||||||
event->setAccepted(true);
|
event->setAccepted(true);
|
||||||
updateBackground();
|
updateBackground();
|
||||||
|
updateQuickAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageDelegateBase::hoverLeaveEvent(QHoverEvent *event)
|
void MessageDelegateBase::hoverLeaveEvent(QHoverEvent *event)
|
||||||
@@ -564,6 +630,7 @@ void MessageDelegateBase::hoverLeaveEvent(QHoverEvent *event)
|
|||||||
Q_EMIT hoveredChanged();
|
Q_EMIT hoveredChanged();
|
||||||
event->setAccepted(true);
|
event->setAccepted(true);
|
||||||
updateBackground();
|
updateBackground();
|
||||||
|
updateQuickAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MessageDelegateBase::isTemporaryHighlighted() const
|
bool MessageDelegateBase::isTemporaryHighlighted() const
|
||||||
|
|||||||
@@ -96,6 +96,11 @@ class MessageDelegateBase : public TimelineDelegate
|
|||||||
Q_PROPERTY(QQmlComponent *compactBackgroundComponent READ compactBackgroundComponent WRITE setCompactBackgroundComponentt NOTIFY
|
Q_PROPERTY(QQmlComponent *compactBackgroundComponent READ compactBackgroundComponent WRITE setCompactBackgroundComponentt NOTIFY
|
||||||
compactBackgroundComponentChanged FINAL)
|
compactBackgroundComponentChanged FINAL)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The component to use to visualize quick actions.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QQmlComponent *quickActionComponent READ quickActionComponent WRITE setQuickActionComponent NOTIFY quickActionComponentChanged FINAL)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether to use the compact mode appearance.
|
* @brief Whether to use the compact mode appearance.
|
||||||
*/
|
*/
|
||||||
@@ -152,6 +157,9 @@ public:
|
|||||||
bool compactMode() const;
|
bool compactMode() const;
|
||||||
void setCompactMode(bool compactMode);
|
void setCompactMode(bool compactMode);
|
||||||
|
|
||||||
|
QQmlComponent *quickActionComponent() const;
|
||||||
|
void setQuickActionComponent(QQmlComponent *quickActionComponent);
|
||||||
|
|
||||||
bool showLocalMessagesOnRight() const;
|
bool showLocalMessagesOnRight() const;
|
||||||
void setShowLocalMessagesOnRight(bool showLocalMessagesOnRight);
|
void setShowLocalMessagesOnRight(bool showLocalMessagesOnRight);
|
||||||
|
|
||||||
@@ -172,6 +180,7 @@ Q_SIGNALS:
|
|||||||
void readMarkerComponentChanged();
|
void readMarkerComponentChanged();
|
||||||
void showReadMarkersChanged();
|
void showReadMarkersChanged();
|
||||||
void compactBackgroundComponentChanged();
|
void compactBackgroundComponentChanged();
|
||||||
|
void quickActionComponentChanged();
|
||||||
void compactModeChanged();
|
void compactModeChanged();
|
||||||
void showLocalMessagesOnRightChanged();
|
void showLocalMessagesOnRightChanged();
|
||||||
void isTemporaryHighlightedChanged();
|
void isTemporaryHighlightedChanged();
|
||||||
@@ -211,6 +220,10 @@ private:
|
|||||||
bool m_compactMode = false;
|
bool m_compactMode = false;
|
||||||
void updateBackground();
|
void updateBackground();
|
||||||
|
|
||||||
|
QPointer<QQmlComponent> m_quickActionComponent;
|
||||||
|
bool m_quickActionIncubating = false;
|
||||||
|
QPointer<QQuickItem> m_quickActionItem;
|
||||||
|
|
||||||
bool m_showLocalMessagesOnRight = true;
|
bool m_showLocalMessagesOnRight = true;
|
||||||
|
|
||||||
bool m_hovered = false;
|
bool m_hovered = false;
|
||||||
@@ -245,4 +258,7 @@ private:
|
|||||||
void resizeContent() override;
|
void resizeContent() override;
|
||||||
|
|
||||||
QPointer<QTimer> m_temporaryHighlightTimer;
|
QPointer<QTimer> m_temporaryHighlightTimer;
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void updateQuickAction();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user