Reenable message edits
This commit is contained in:
@@ -42,6 +42,8 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property int chatBarType: LibNeoChat.ChatBarType.Room
|
||||||
|
|
||||||
onActiveFocusChanged: chatContentView.itemAt(contentModel.index(contentModel.focusRow, 0)).forceActiveFocus()
|
onActiveFocusChanged: chatContentView.itemAt(contentModel.index(contentModel.focusRow, 0)).forceActiveFocus()
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -127,7 +129,7 @@ Item {
|
|||||||
id: chatContentView
|
id: chatContentView
|
||||||
model: ChatBarMessageContentModel {
|
model: ChatBarMessageContentModel {
|
||||||
id: contentModel
|
id: contentModel
|
||||||
type: ChatBarType.Room
|
type: root.chatBarType
|
||||||
room: root.currentRoom
|
room: root.currentRoom
|
||||||
sendMessageWithEnter: NeoChatConfig.sendMessageWith === 0
|
sendMessageWithEnter: NeoChatConfig.sendMessageWith === 0
|
||||||
}
|
}
|
||||||
@@ -201,7 +203,7 @@ Item {
|
|||||||
|
|
||||||
property LibNeoChat.CompletionModel completionModel: LibNeoChat.CompletionModel {
|
property LibNeoChat.CompletionModel completionModel: LibNeoChat.CompletionModel {
|
||||||
room: root.currentRoom
|
room: root.currentRoom
|
||||||
type: LibNeoChat.ChatBarType.Room
|
type: root.chatBarType
|
||||||
textItem: contentModel.focusedTextItem
|
textItem: contentModel.focusedTextItem
|
||||||
roomListModel: RoomManager.roomListModel
|
roomListModel: RoomManager.roomListModel
|
||||||
userListModel: RoomManager.userListModel
|
userListModel: RoomManager.userListModel
|
||||||
|
|||||||
96
src/chatbar/ChatBarCore.qml
Normal file
96
src/chatbar/ChatBarCore.qml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026 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 QtCore
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls as QQC2
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat.libneochat as LibNeoChat
|
||||||
|
|
||||||
|
QQC2.Control {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property real availableWidth
|
||||||
|
|
||||||
|
width: root.availableWidth - Kirigami.Units.largeSpacing * 2
|
||||||
|
topPadding: Kirigami.Units.smallSpacing
|
||||||
|
bottomPadding: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
contentItem: ColumnLayout {
|
||||||
|
RichEditBar {
|
||||||
|
id: richEditBar
|
||||||
|
visible: NeoChatConfig.sendMessageWith === 1
|
||||||
|
maxAvailableWidth: root.availableWidth - Kirigami.Units.largeSpacing * 2
|
||||||
|
|
||||||
|
room: root.currentRoom
|
||||||
|
contentModel: chatContentView.model
|
||||||
|
|
||||||
|
onClicked: contentModel.refocusCurrentComponent()
|
||||||
|
}
|
||||||
|
Kirigami.Separator {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: NeoChatConfig.sendMessageWith === 1
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
spacing: 0
|
||||||
|
QQC2.ScrollView {
|
||||||
|
id: chatScrollView
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumHeight: Kirigami.Units.gridUnit * 8
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
readonly property real visibleTop: chatScrollView.QQC2.ScrollBar.vertical.position * chatScrollView.contentHeight
|
||||||
|
readonly property real visibleBottom: chatScrollView.QQC2.ScrollBar.vertical.position * chatScrollView.contentHeight + chatScrollView.QQC2.ScrollBar.vertical.size * chatScrollView.contentHeight
|
||||||
|
readonly property rect cursorRectInColumn: mapFromItem(contentModel.focusedTextItem.textItem, contentModel.focusedTextItem.cursorRectangle);
|
||||||
|
onCursorRectInColumnChanged: {
|
||||||
|
if (chatScrollView.QQC2.ScrollBar.vertical.visible) {
|
||||||
|
if (cursorRectInColumn.y < visibleTop) {
|
||||||
|
chatScrollView.QQC2.ScrollBar.vertical.position = cursorRectInColumn.y / chatScrollView.contentHeight
|
||||||
|
} else if (cursorRectInColumn.y + cursorRectInColumn.height > visibleBottom) {
|
||||||
|
chatScrollView.QQC2.ScrollBar.vertical.position = (cursorRectInColumn.y + cursorRectInColumn.height - (chatScrollView.QQC2.ScrollBar.vertical.size * chatScrollView.contentHeight)) / chatScrollView.contentHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
width: chatScrollView.width
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: chatContentView
|
||||||
|
model: ChatBarMessageContentModel {
|
||||||
|
id: contentModel
|
||||||
|
type: root.chatBarType
|
||||||
|
room: root.currentRoom
|
||||||
|
sendMessageWithEnter: NeoChatConfig.sendMessageWith === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: MessageComponentChooser {
|
||||||
|
rightAnchorMargin: chatScrollView.QQC2.ScrollBar.vertical.visible ? chatScrollView.QQC2.ScrollBar.vertical.width : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SendBar {
|
||||||
|
room: root.currentRoom
|
||||||
|
contentModel: chatContentView.model
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Kirigami.ShadowedRectangle {
|
||||||
|
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||||
|
Kirigami.Theme.inherit: false
|
||||||
|
|
||||||
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
color: Kirigami.Theme.backgroundColor
|
||||||
|
border {
|
||||||
|
color: Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, Kirigami.Theme.frameContrast)
|
||||||
|
width: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -104,25 +104,21 @@ QQC2.Control {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletionMenu {
|
// CompletionMenu {
|
||||||
id: completionMenu
|
// id: completionMenu
|
||||||
width: Math.max(350, root.width - 1)
|
// width: Math.max(350, root.width - 1)
|
||||||
height: implicitHeight
|
// height: implicitHeight
|
||||||
y: -height - 5
|
// y: -height - 5
|
||||||
z: 10
|
// z: 10
|
||||||
|
// margins: 0
|
||||||
room: root.Message.room
|
// Behavior on height {
|
||||||
type: root.chatBarCache.isEditing ? ChatBarType.Edit : ChatBarType.Thread
|
// NumberAnimation {
|
||||||
// textItem: textArea
|
// property: "height"
|
||||||
margins: 0
|
// duration: Kirigami.Units.shortDuration
|
||||||
Behavior on height {
|
// easing.type: Easing.OutCubic
|
||||||
NumberAnimation {
|
// }
|
||||||
property: "height"
|
// }
|
||||||
duration: Kirigami.Units.shortDuration
|
// }
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// opt-out of whatever spell checker a styled TextArea might come with
|
// opt-out of whatever spell checker a styled TextArea might come with
|
||||||
Kirigami.SpellCheck.enabled: false
|
Kirigami.SpellCheck.enabled: false
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "enums/messagecomponenttype.h"
|
#include "enums/messagecomponenttype.h"
|
||||||
#include "enums/richformat.h"
|
#include "enums/richformat.h"
|
||||||
#include "messagecontentmodel.h"
|
#include "messagecontentmodel.h"
|
||||||
|
#include "neochatroom.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -27,46 +28,13 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent)
|
|||||||
{
|
{
|
||||||
m_editableActive = true;
|
m_editableActive = true;
|
||||||
|
|
||||||
connect(this, &ChatBarMessageContentModel::roomChanged, this, [this]() {
|
connect(this, &ChatBarMessageContentModel::roomChanged, this, [this](NeoChatRoom *oldRoom) {
|
||||||
if (m_type == ChatBarType::None || !m_room) {
|
if (m_type == ChatBarType::None || !m_room) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(m_room->cacheForType(m_type), &ChatBarCache::relationIdChanged, this, &ChatBarMessageContentModel::updateReplyModel);
|
connectCache(oldRoom ? oldRoom->cacheForType(m_type) : nullptr);
|
||||||
connect(m_room->cacheForType(m_type), &ChatBarCache::attachmentPathChanged, this, [this]() {
|
initializeFromCache();
|
||||||
if (m_room->cacheForType(m_type)->attachmentPath().length() > 0) {
|
|
||||||
addAttachment(QUrl(m_room->cacheForType(m_type)->attachmentPath()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (m_room->cacheForType(m_type)->attachmentPath().length() > 0) {
|
|
||||||
addAttachment(QUrl(m_room->cacheForType(m_type)->attachmentPath()));
|
|
||||||
}
|
|
||||||
|
|
||||||
clearModel();
|
|
||||||
|
|
||||||
const auto textSections = m_room->cacheForType(m_type)->text().split(u"\n\n"_s);
|
|
||||||
if (textSections.length() == 1 && textSections[0].isEmpty()) {
|
|
||||||
initializeModel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
beginResetModel();
|
|
||||||
for (const auto §ion : textSections) {
|
|
||||||
const auto type = MessageComponentType::typeForString(section);
|
|
||||||
auto cleanText = section;
|
|
||||||
if (type == MessageComponentType::Code) {
|
|
||||||
cleanText.remove(0, 4);
|
|
||||||
cleanText.remove(cleanText.length() - 4, 4);
|
|
||||||
} else if (type == MessageComponentType::Quote) {
|
|
||||||
cleanText.remove(0, 2);
|
|
||||||
}
|
|
||||||
insertComponent(rowCount(), type, {}, cleanText);
|
|
||||||
}
|
|
||||||
endResetModel();
|
|
||||||
|
|
||||||
m_currentFocusComponent = QPersistentModelIndex(index(rowCount() - 1));
|
|
||||||
Q_EMIT focusRowChanged();
|
|
||||||
});
|
});
|
||||||
connect(this, &ChatBarMessageContentModel::focusRowChanged, this, [this]() {
|
connect(this, &ChatBarMessageContentModel::focusRowChanged, this, [this]() {
|
||||||
m_markdownHelper->setTextItem(focusedTextItem());
|
m_markdownHelper->setTextItem(focusedTextItem());
|
||||||
@@ -80,19 +48,43 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent)
|
|||||||
}
|
}
|
||||||
m_keyHelper->room = m_room;
|
m_keyHelper->room = m_room;
|
||||||
});
|
});
|
||||||
connect(this, &ChatBarMessageContentModel::typeChanged, this, [this]() {
|
connect(this, &ChatBarMessageContentModel::typeChanged, this, [this](ChatBarType::Type oldType) {
|
||||||
for (const auto &component : m_components) {
|
for (const auto &component : m_components) {
|
||||||
if (const auto textItem = textItemForComponent(component)) {
|
if (const auto textItem = textItemForComponent(component)) {
|
||||||
textItem->setType(m_type);
|
textItem->setType(m_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!m_room) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connectCache(m_room->cacheForType(oldType));
|
||||||
|
initializeFromCache();
|
||||||
});
|
});
|
||||||
connect(m_markdownHelper, &ChatMarkdownHelper::unhandledBlockFormat, this, &ChatBarMessageContentModel::insertStyleAtCursor);
|
connect(m_markdownHelper, &ChatMarkdownHelper::unhandledBlockFormat, this, &ChatBarMessageContentModel::insertStyleAtCursor);
|
||||||
|
|
||||||
|
connectCache();
|
||||||
connectKeyHelper();
|
connectKeyHelper();
|
||||||
initializeModel();
|
initializeModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatBarMessageContentModel::connectCache(ChatBarCache *oldCache)
|
||||||
|
{
|
||||||
|
if (m_type == ChatBarType::None || !m_room) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldCache) {
|
||||||
|
oldCache->disconnect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(m_room->cacheForType(m_type), &ChatBarCache::relationIdChanged, this, &ChatBarMessageContentModel::updateReplyModel);
|
||||||
|
connect(m_room->cacheForType(m_type), &ChatBarCache::attachmentPathChanged, this, [this]() {
|
||||||
|
if (m_room->cacheForType(m_type)->attachmentPath().length() > 0) {
|
||||||
|
addAttachment(QUrl(m_room->cacheForType(m_type)->attachmentPath()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void ChatBarMessageContentModel::initializeModel(const QString &initialText)
|
void ChatBarMessageContentModel::initializeModel(const QString &initialText)
|
||||||
{
|
{
|
||||||
updateReplyModel();
|
updateReplyModel();
|
||||||
@@ -114,6 +106,38 @@ void ChatBarMessageContentModel::initializeModel(const QString &initialText)
|
|||||||
Q_EMIT focusRowChanged();
|
Q_EMIT focusRowChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatBarMessageContentModel::initializeFromCache()
|
||||||
|
{
|
||||||
|
if (m_room->cacheForType(m_type)->attachmentPath().length() > 0) {
|
||||||
|
addAttachment(QUrl(m_room->cacheForType(m_type)->attachmentPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
clearModel();
|
||||||
|
|
||||||
|
const auto textSections = m_room->cacheForType(m_type)->text().split(u"\n\n"_s);
|
||||||
|
if (textSections.length() == 1 && textSections[0].isEmpty()) {
|
||||||
|
initializeModel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginResetModel();
|
||||||
|
for (const auto §ion : textSections) {
|
||||||
|
const auto type = MessageComponentType::typeForString(section);
|
||||||
|
auto cleanText = section;
|
||||||
|
if (type == MessageComponentType::Code) {
|
||||||
|
cleanText.remove(0, 4);
|
||||||
|
cleanText.remove(cleanText.length() - 4, 4);
|
||||||
|
} else if (type == MessageComponentType::Quote) {
|
||||||
|
cleanText.remove(0, 2);
|
||||||
|
}
|
||||||
|
insertComponent(rowCount(), type, {}, cleanText);
|
||||||
|
}
|
||||||
|
endResetModel();
|
||||||
|
|
||||||
|
m_currentFocusComponent = QPersistentModelIndex(index(rowCount() - 1));
|
||||||
|
Q_EMIT focusRowChanged();
|
||||||
|
}
|
||||||
|
|
||||||
ChatBarType::Type ChatBarMessageContentModel::type() const
|
ChatBarType::Type ChatBarMessageContentModel::type() const
|
||||||
{
|
{
|
||||||
return m_type;
|
return m_type;
|
||||||
@@ -124,8 +148,8 @@ void ChatBarMessageContentModel::setType(ChatBarType::Type type)
|
|||||||
if (type == m_type) {
|
if (type == m_type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_type = type;
|
const auto oldType = std::exchange(m_type, type);
|
||||||
Q_EMIT typeChanged();
|
Q_EMIT typeChanged(oldType, m_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatKeyHelper *ChatBarMessageContentModel::keyHelper() const
|
ChatKeyHelper *ChatBarMessageContentModel::keyHelper() const
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
|
||||||
|
#include "chatbarcache.h"
|
||||||
#include "chatkeyhelper.h"
|
#include "chatkeyhelper.h"
|
||||||
#include "chatmarkdownhelper.h"
|
#include "chatmarkdownhelper.h"
|
||||||
#include "chattextitemhelper.h"
|
#include "chattextitemhelper.h"
|
||||||
@@ -100,15 +101,17 @@ public:
|
|||||||
Q_INVOKABLE void postMessage();
|
Q_INVOKABLE void postMessage();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void typeChanged();
|
void typeChanged(ChatBarType::Type oldType, ChatBarType::Type newType);
|
||||||
void focusRowChanged();
|
void focusRowChanged();
|
||||||
void hasRichFormattingChanged();
|
void hasRichFormattingChanged();
|
||||||
void sendMessageWithEnterChanged();
|
void sendMessageWithEnterChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ChatBarType::Type m_type = ChatBarType::None;
|
ChatBarType::Type m_type = ChatBarType::None;
|
||||||
|
void connectCache(ChatBarCache *oldCache = nullptr);
|
||||||
|
|
||||||
void initializeModel(const QString &initialText = {});
|
void initializeModel(const QString &initialText = {});
|
||||||
|
void initializeFromCache();
|
||||||
|
|
||||||
std::optional<QString> getReplyEventId() override;
|
std::optional<QString> getReplyEventId() override;
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ void MessageContentModel::setRoom(NeoChatRoom *room)
|
|||||||
m_room->disconnect(this);
|
m_room->disconnect(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_room = room;
|
const auto oldRoom = std::exchange(m_room, room);
|
||||||
|
|
||||||
if (m_room) {
|
if (m_room) {
|
||||||
connect(m_room, &NeoChatRoom::newFileTransfer, this, [this](const QString &eventId) {
|
connect(m_room, &NeoChatRoom::newFileTransfer, this, [this](const QString &eventId) {
|
||||||
@@ -103,7 +103,7 @@ void MessageContentModel::setRoom(NeoChatRoom *room)
|
|||||||
connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, &MessageContentModel::componentsUpdated);
|
connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, &MessageContentModel::componentsUpdated);
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_EMIT roomChanged();
|
Q_EMIT roomChanged(oldRoom, m_room);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MessageContentModel::eventId() const
|
QString MessageContentModel::eventId() const
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ public:
|
|||||||
Q_INVOKABLE void toggleSpoiler(QModelIndex index);
|
Q_INVOKABLE void toggleSpoiler(QModelIndex index);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void roomChanged();
|
void roomChanged(NeoChatRoom *oldRoom, NeoChatRoom *newRoom);
|
||||||
void authorChanged();
|
void authorChanged();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user