Restore missing functionality
This commit is contained in:
@@ -39,7 +39,7 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_textItem = textItem;
|
m_textItem = textItem;
|
||||||
m_keyHelper->setTextItem(textItem);
|
m_keyHelper->textItem = textItem;
|
||||||
Q_EMIT textItemChanged();
|
Q_EMIT textItemChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
text: i18nc("@action:button", "Mention")
|
text: i18nc("@action:button", "Mention")
|
||||||
icon.name: "username-copy-symbolic"
|
icon.name: "username-copy-symbolic"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
RoomManager.currentRoom.mainCache.mentionAdded(root.author.id);
|
RoomManager.currentRoom.mainCache.mentionAdded(root.author.disambiguatedName, "https://matrix.to/#/" + root.author.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,12 +40,45 @@ QQC2.Control {
|
|||||||
onCurrentRoomChanged: {
|
onCurrentRoomChanged: {
|
||||||
_private.chatBarCache = currentRoom.mainCache
|
_private.chatBarCache = currentRoom.mainCache
|
||||||
if (ShareHandler.text.length > 0 && ShareHandler.room === root.currentRoom.id) {
|
if (ShareHandler.text.length > 0 && ShareHandler.room === root.currentRoom.id) {
|
||||||
|
contentModel.focusedTextItem.
|
||||||
textField.text = ShareHandler.text;
|
textField.text = ShareHandler.text;
|
||||||
ShareHandler.text = "";
|
ShareHandler.text = "";
|
||||||
ShareHandler.room = "";
|
ShareHandler.room = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: contentModel.keyHelper
|
||||||
|
|
||||||
|
function onUnhandledUp(isCompleting: bool): void {
|
||||||
|
if (!isCompleting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
completionMenu.decrementIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUnhandledDown(isCompleting: bool): void {
|
||||||
|
if (!isCompleting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
completionMenu.incrementIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUnhandledTab(isCompleting: bool): void {
|
||||||
|
if (!isCompleting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
completionMenu.completeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUnhandledReturn(isCompleting: bool): void {
|
||||||
|
if (!isCompleting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
completionMenu.completeCurrent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ShareHandler
|
target: ShareHandler
|
||||||
function onRoomChanged(): void {
|
function onRoomChanged(): void {
|
||||||
@@ -60,13 +93,10 @@ QQC2.Control {
|
|||||||
Connections {
|
Connections {
|
||||||
target: root.currentRoom.mainCache
|
target: root.currentRoom.mainCache
|
||||||
|
|
||||||
function onMentionAdded(mention: string): void {
|
function onMentionAdded(text: string, hRef: string): void {
|
||||||
// add mention text
|
completionMenu.complete(text, hRef);
|
||||||
textField.append(mention + " ");
|
|
||||||
// move cursor to the end
|
|
||||||
textField.cursorPosition = textField.text.length;
|
|
||||||
// move the focus back to the chat bar
|
// move the focus back to the chat bar
|
||||||
textField.forceActiveFocus(Qt.OtherFocusReason);
|
contentModel.refocusCurrentComponent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,30 +123,39 @@ QQC2.Control {
|
|||||||
topPadding: Kirigami.Units.smallSpacing
|
topPadding: Kirigami.Units.smallSpacing
|
||||||
bottomPadding: Kirigami.Units.smallSpacing
|
bottomPadding: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
contentItem: QQC2.ScrollView {
|
contentItem: ColumnLayout {
|
||||||
id: chatScrollView
|
QQC2.ScrollView {
|
||||||
ColumnLayout {
|
id: chatScrollView
|
||||||
spacing: Kirigami.Units.smallSpacing
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumHeight: Kirigami.Units.gridUnit * 8
|
||||||
|
|
||||||
Repeater {
|
clip: true
|
||||||
id: chatContentView
|
|
||||||
model: ChatBarMessageContentModel {
|
ColumnLayout {
|
||||||
id: contentModel
|
width: chatScrollView.width
|
||||||
type: ChatBarType.Room
|
spacing: Kirigami.Units.smallSpacing
|
||||||
room: root.currentRoom
|
|
||||||
|
Repeater {
|
||||||
|
id: chatContentView
|
||||||
|
model: ChatBarMessageContentModel {
|
||||||
|
id: contentModel
|
||||||
|
type: ChatBarType.Room
|
||||||
|
room: root.currentRoom
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: MessageComponentChooser {}
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: MessageComponentChooser {}
|
|
||||||
}
|
}
|
||||||
RichEditBar {
|
}
|
||||||
id: richEditBar
|
RichEditBar {
|
||||||
maxAvailableWidth: chatBarSizeHelper.availableWidth - Kirigami.Units.largeSpacing * 2
|
id: richEditBar
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
maxAvailableWidth: chatBarSizeHelper.availableWidth - Kirigami.Units.largeSpacing * 2
|
||||||
|
|
||||||
room: root.currentRoom
|
room: root.currentRoom
|
||||||
contentModel: chatContentView.model
|
contentModel: chatContentView.model
|
||||||
|
|
||||||
onClicked: contentModel.refocusCurrentComponent()
|
onClicked: contentModel.refocusCurrentComponent()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +190,7 @@ QQC2.Control {
|
|||||||
id: completionMenu
|
id: completionMenu
|
||||||
room: root.currentRoom
|
room: root.currentRoom
|
||||||
type: LibNeoChat.ChatBarType.Room
|
type: LibNeoChat.ChatBarType.Room
|
||||||
textItem: chatContentView.model.focusedTextItem
|
textItem: contentModel.focusedTextItem
|
||||||
|
|
||||||
x: 1
|
x: 1
|
||||||
y: -height
|
y: -height
|
||||||
|
|||||||
@@ -46,8 +46,12 @@ QQC2.Popup {
|
|||||||
completions.decrementCurrentIndex();
|
completions.decrementCurrentIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
function complete() {
|
function complete(text: string, hRef: string) {
|
||||||
completionModel.insertCompletion(completions.currentItem.replacedText, completions.currentItem.hRef)
|
completionModel.insertCompletion(text, hRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeCurrent() {
|
||||||
|
completionModel.insertCompletion(completions.currentItem.replacedText, completions.currentItem.hRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ Q_SIGNALS:
|
|||||||
void relationIdChanged(const QString &oldEventId, const QString &newEventId);
|
void relationIdChanged(const QString &oldEventId, const QString &newEventId);
|
||||||
void threadIdChanged(const QString &oldThreadId, const QString &newThreadId);
|
void threadIdChanged(const QString &oldThreadId, const QString &newThreadId);
|
||||||
void attachmentPathChanged();
|
void attachmentPathChanged();
|
||||||
void mentionAdded(const QString &mention);
|
void mentionAdded(const QString &text, const QString &hRef);
|
||||||
void relationAuthorIsPresentChanged();
|
void relationAuthorIsPresentChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -3,120 +3,191 @@
|
|||||||
|
|
||||||
#include "chatkeyhelper.h"
|
#include "chatkeyhelper.h"
|
||||||
|
|
||||||
|
#include "chattextitemhelper.h"
|
||||||
|
#include "clipboard.h"
|
||||||
|
#include "neochatroom.h"
|
||||||
|
|
||||||
ChatKeyHelper::ChatKeyHelper(QObject *parent)
|
ChatKeyHelper::ChatKeyHelper(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatTextItemHelper *ChatKeyHelper::textItem() const
|
bool ChatKeyHelper::handleKey(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||||
{
|
{
|
||||||
return m_textItem;
|
switch (key) {
|
||||||
|
case Qt::Key_V:
|
||||||
|
return vKey(modifiers);
|
||||||
|
case Qt::Key_Up:
|
||||||
|
return up(modifiers);
|
||||||
|
case Qt::Key_Down:
|
||||||
|
return down();
|
||||||
|
case Qt::Key_Tab:
|
||||||
|
return tab();
|
||||||
|
case Qt::Key_Delete:
|
||||||
|
return deleteChar();
|
||||||
|
case Qt::Key_Backspace:
|
||||||
|
return backspace();
|
||||||
|
case Qt::Key_Enter:
|
||||||
|
case Qt::Key_Return:
|
||||||
|
return insertReturn();
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatKeyHelper::setTextItem(ChatTextItemHelper *textItem)
|
bool ChatKeyHelper::vKey(Qt::KeyboardModifiers modifiers)
|
||||||
{
|
{
|
||||||
if (textItem == m_textItem) {
|
if (!textItem) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_textItem) {
|
if (modifiers.testFlag(Qt::ControlModifier)) {
|
||||||
m_textItem->disconnect(this);
|
return pasteImage();
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
m_textItem = textItem;
|
|
||||||
|
|
||||||
if (m_textItem) {
|
|
||||||
connect(m_textItem, &ChatTextItemHelper::textItemChanged, this, &ChatKeyHelper::textItemChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_EMIT textItemChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatKeyHelper::up()
|
bool ChatKeyHelper::up(Qt::KeyboardModifiers modifiers)
|
||||||
{
|
{
|
||||||
if (!m_textItem) {
|
if (!textItem) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
QTextCursor cursor = m_textItem->textCursor();
|
|
||||||
|
if (modifiers.testFlag(Qt::ControlModifier)) {
|
||||||
|
room->replyLastMessage();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textItem->isEmpty()) {
|
||||||
|
room->editLastMessage();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textItem->isCompleting) {
|
||||||
|
Q_EMIT unhandledUp(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor cursor = textItem->textCursor();
|
||||||
if (cursor.isNull()) {
|
if (cursor.isNull()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (cursor.blockNumber() == 0 && cursor.block().layout()->lineForTextPosition(cursor.positionInBlock()).lineNumber() == 0) {
|
if (cursor.blockNumber() == 0 && cursor.block().layout()->lineForTextPosition(cursor.positionInBlock()).lineNumber() == 0) {
|
||||||
Q_EMIT unhandledUp();
|
Q_EMIT unhandledUp(false);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
cursor.movePosition(QTextCursor::Up);
|
return false;
|
||||||
m_textItem->setCursorPosition(cursor.position());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatKeyHelper::down()
|
bool ChatKeyHelper::down()
|
||||||
{
|
{
|
||||||
if (!m_textItem) {
|
if (!textItem) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
QTextCursor cursor = m_textItem->textCursor();
|
if (textItem->isCompleting) {
|
||||||
|
Q_EMIT unhandledDown(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor cursor = textItem->textCursor();
|
||||||
if (cursor.isNull()) {
|
if (cursor.isNull()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (cursor.blockNumber() == cursor.document()->blockCount() - 1
|
if (cursor.blockNumber() == cursor.document()->blockCount() - 1
|
||||||
&& cursor.block().layout()->lineForTextPosition(cursor.positionInBlock()).lineNumber() == (cursor.block().layout()->lineCount() - 1)) {
|
&& cursor.block().layout()->lineForTextPosition(cursor.positionInBlock()).lineNumber() == (cursor.block().layout()->lineCount() - 1)) {
|
||||||
Q_EMIT unhandledDown();
|
Q_EMIT unhandledDown(false);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
cursor.movePosition(QTextCursor::Down);
|
return false;
|
||||||
m_textItem->setCursorPosition(cursor.position());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatKeyHelper::tab()
|
bool ChatKeyHelper::tab()
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_textItem->textCursor();
|
if (!textItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (textItem->isCompleting) {
|
||||||
|
Q_EMIT unhandledTab(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor cursor = textItem->textCursor();
|
||||||
if (cursor.isNull()) {
|
if (cursor.isNull()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (cursor.currentList() && m_textItem->canIndentListMoreAtCursor()) {
|
if (cursor.currentList() && textItem->canIndentListMoreAtCursor()) {
|
||||||
m_textItem->indentListMoreAtCursor();
|
textItem->indentListMoreAtCursor();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
cursor.insertText(u" "_s);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatKeyHelper::deleteChar()
|
bool ChatKeyHelper::deleteChar()
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_textItem->textCursor();
|
if (!textItem) {
|
||||||
if (cursor.isNull()) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (cursor.position() >= m_textItem->document()->characterCount() - m_textItem->fixedEndChars().length() - 1) {
|
|
||||||
|
QTextCursor cursor = textItem->textCursor();
|
||||||
|
if (cursor.isNull()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cursor.position() >= textItem->document()->characterCount() - textItem->fixedEndChars().length() - 1) {
|
||||||
Q_EMIT unhandledDelete();
|
Q_EMIT unhandledDelete();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
cursor.deleteChar();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatKeyHelper::backspace()
|
bool ChatKeyHelper::backspace()
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_textItem->textCursor();
|
if (!textItem) {
|
||||||
if (cursor.isNull()) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (cursor.position() <= m_textItem->fixedStartChars().length()) {
|
|
||||||
if (cursor.currentList() && m_textItem->canIndentListLessAtCursor()) {
|
QTextCursor cursor = textItem->textCursor();
|
||||||
m_textItem->indentListLessAtCursor();
|
if (cursor.isNull()) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
if (cursor.position() <= textItem->fixedStartChars().length()) {
|
||||||
|
if (cursor.currentList() && textItem->canIndentListLessAtCursor()) {
|
||||||
|
textItem->indentListLessAtCursor();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
Q_EMIT unhandledBackspace();
|
Q_EMIT unhandledBackspace();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
cursor.deletePreviousChar();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatKeyHelper::insertReturn()
|
bool ChatKeyHelper::insertReturn()
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_textItem->textCursor();
|
if (!textItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (textItem->isCompleting) {
|
||||||
|
Q_EMIT unhandledReturn(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor cursor = textItem->textCursor();
|
||||||
if (cursor.isNull()) {
|
if (cursor.isNull()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
cursor.insertBlock();
|
cursor.insertBlock();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatKeyHelper::pasteImage()
|
||||||
|
{
|
||||||
|
if (!textItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto savePath = Clipboard().saveImage();
|
||||||
|
if (!savePath.isEmpty()) {
|
||||||
|
Q_EMIT imagePasted(savePath);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_chatkeyhelper.cpp"
|
#include "moc_chatkeyhelper.cpp"
|
||||||
|
|||||||
@@ -6,8 +6,18 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
|
||||||
#include "chattextitemhelper.h"
|
class NeoChatRoom;
|
||||||
|
class ChatTextItemHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class ChatKeyHelper
|
||||||
|
*
|
||||||
|
* A class to handle some key presses on behalf of a ChatTextItemHelper.
|
||||||
|
*
|
||||||
|
* This is used to manage complex rich text interactions.
|
||||||
|
*
|
||||||
|
* @sa ChatTextItemHelper
|
||||||
|
*/
|
||||||
class ChatKeyHelper : public QObject
|
class ChatKeyHelper : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -16,72 +26,94 @@ class ChatKeyHelper : public QObject
|
|||||||
public:
|
public:
|
||||||
explicit ChatKeyHelper(QObject *parent = nullptr);
|
explicit ChatKeyHelper(QObject *parent = nullptr);
|
||||||
|
|
||||||
ChatTextItemHelper *textItem() const;
|
/**
|
||||||
void setTextItem(ChatTextItemHelper *textItem);
|
* @brief The ChatTextItemHelper that ChatKeyHelper is handling key presses for.
|
||||||
|
*
|
||||||
|
* @sa ChatTextItemHelper
|
||||||
|
*/
|
||||||
|
QPointer<NeoChatRoom> room;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handle up key at current cursor location.
|
* @brief The ChatTextItemHelper that ChatKeyHelper is handling key presses for.
|
||||||
|
*
|
||||||
|
* @sa ChatTextItemHelper
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void up();
|
QPointer<ChatTextItemHelper> textItem;
|
||||||
|
|
||||||
/**
|
Q_INVOKABLE bool handleKey(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
||||||
* @brief Handle down key at current cursor location.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void down();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handle tab key at current cursor location.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void tab();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handle delete key at current cursor location.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void deleteChar();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handle backspace key at current cursor location.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void backspace();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handle return key at current cursor location.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void insertReturn();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void textItemChanged();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief There is an unhandled up key press.
|
* @brief There is an unhandled up key press.
|
||||||
*
|
*
|
||||||
* i.e. up is pressed on the first line of the first block of the text item.
|
* Current trigger conditions:
|
||||||
|
* - Up is pressed on the first line of the first block of the text item.
|
||||||
|
* - Return clicked when a completion has been started.
|
||||||
*/
|
*/
|
||||||
void unhandledUp();
|
void unhandledUp(bool isCompleting);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief There is an unhandled down key press.
|
* @brief There is an unhandled down key press.
|
||||||
*
|
*
|
||||||
* i.e. down is pressed on the last line of the last block of the text item.
|
* Current trigger conditions:
|
||||||
|
* - Down is pressed on the last line of the last block of the text item.
|
||||||
|
* - Return clicked when a completion has been started.
|
||||||
*/
|
*/
|
||||||
void unhandledDown();
|
void unhandledDown(bool isCompleting);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief There is an unhandled tab key press.
|
||||||
|
*
|
||||||
|
* Current trigger conditions:
|
||||||
|
* - Tab clicked when a completion has been started.
|
||||||
|
*/
|
||||||
|
void unhandledTab(bool isCompleting);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief There is an unhandled delete key press.
|
* @brief There is an unhandled delete key press.
|
||||||
*
|
*
|
||||||
* i.e. delete is pressed at the end of the last line of the last block of the
|
* Current trigger conditions:
|
||||||
* text item.
|
* - Delete is pressed at the end of the last line of the last block of the
|
||||||
|
* text item.
|
||||||
*/
|
*/
|
||||||
void unhandledDelete();
|
void unhandledDelete();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief There is an unhandled backspace key press.
|
* @brief There is an unhandled backspace key press.
|
||||||
*
|
*
|
||||||
* i.e. backspace is pressed at the beginning of the first line of the first
|
* Current trigger conditions:
|
||||||
* block of the text item.
|
* - Backspace is pressed at the beginning of the first line of the first
|
||||||
|
* block of the text item.
|
||||||
*/
|
*/
|
||||||
void unhandledBackspace();
|
void unhandledBackspace();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief There is an unhandled return key press.
|
||||||
|
*
|
||||||
|
* Current trigger conditions:
|
||||||
|
* - Return clicked when a completion has been started.
|
||||||
|
*/
|
||||||
|
void unhandledReturn(bool isCompleting);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An image has been pasted.
|
||||||
|
*/
|
||||||
|
void imagePasted(const QString &filePath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPointer<ChatTextItemHelper> m_textItem;
|
bool vKey(Qt::KeyboardModifiers modifiers);
|
||||||
|
|
||||||
|
bool up(Qt::KeyboardModifiers modifiers);
|
||||||
|
|
||||||
|
bool down();
|
||||||
|
|
||||||
|
bool tab();
|
||||||
|
|
||||||
|
bool deleteChar();
|
||||||
|
|
||||||
|
bool backspace();
|
||||||
|
|
||||||
|
bool insertReturn();
|
||||||
|
|
||||||
|
bool pasteImage();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ public:
|
|||||||
QQuickItem *textItem() const;
|
QQuickItem *textItem() const;
|
||||||
void setTextItem(QQuickItem *textItem);
|
void setTextItem(QQuickItem *textItem);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether a completion has started based on recent text entry.
|
||||||
|
*/
|
||||||
|
bool isCompleting = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The fixed characters that will always be at the beginning of the text item.
|
* @brief The fixed characters that will always be at the beginning of the text item.
|
||||||
*/
|
*/
|
||||||
@@ -98,6 +103,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
QTextDocument *document() const;
|
QTextDocument *document() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whetehr the underlying QTextDocument is empty.
|
||||||
|
*
|
||||||
|
* @sa QTextDocument
|
||||||
|
*/
|
||||||
|
bool isEmpty() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The line count of the text item.
|
* @brief The line count of the text item.
|
||||||
*/
|
*/
|
||||||
@@ -238,7 +250,6 @@ private:
|
|||||||
void initializeChars();
|
void initializeChars();
|
||||||
bool m_initializingChars = false;
|
bool m_initializingChars = false;
|
||||||
|
|
||||||
bool isEmpty() const;
|
|
||||||
std::optional<int> lineLength(int lineNumber) const;
|
std::optional<int> lineLength(int lineNumber) const;
|
||||||
|
|
||||||
int selectionStart() const;
|
int selectionStart() const;
|
||||||
|
|||||||
@@ -240,6 +240,8 @@ void CompletionModel::updateCompletion()
|
|||||||
}
|
}
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
|
||||||
|
m_textItem->isCompleting = rowCount() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletionModel::AutoCompletionType CompletionModel::autoCompletionType() const
|
CompletionModel::AutoCompletionType CompletionModel::autoCompletionType() const
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ QString QmlUtils::nameForPowerLevelValue(const int value)
|
|||||||
|
|
||||||
bool Utils::isEmoji(const QString &text)
|
bool Utils::isEmoji(const QString &text)
|
||||||
{
|
{
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_ICU
|
#ifdef HAVE_ICU
|
||||||
QTextBoundaryFinder finder(QTextBoundaryFinder::Grapheme, text);
|
QTextBoundaryFinder finder(QTextBoundaryFinder::Grapheme, text);
|
||||||
int from = 0;
|
int from = 0;
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ QQC2.Control {
|
|||||||
|
|
||||||
Keys.onEnterPressed: event => {
|
Keys.onEnterPressed: event => {
|
||||||
if (completionMenu.visible) {
|
if (completionMenu.visible) {
|
||||||
completionMenu.complete();
|
completionMenu.completeCurrent();
|
||||||
} else if (event.modifiers & Qt.ShiftModifier) {
|
} else if (event.modifiers & Qt.ShiftModifier) {
|
||||||
textArea.insert(cursorPosition, "\n");
|
textArea.insert(cursorPosition, "\n");
|
||||||
} else {
|
} else {
|
||||||
@@ -84,7 +84,7 @@ QQC2.Control {
|
|||||||
}
|
}
|
||||||
Keys.onReturnPressed: event => {
|
Keys.onReturnPressed: event => {
|
||||||
if (completionMenu.visible) {
|
if (completionMenu.visible) {
|
||||||
completionMenu.complete();
|
completionMenu.completeCurrent();
|
||||||
} else if (event.modifiers & Qt.ShiftModifier) {
|
} else if (event.modifiers & Qt.ShiftModifier) {
|
||||||
textArea.insert(cursorPosition, "\n");
|
textArea.insert(cursorPosition, "\n");
|
||||||
} else {
|
} else {
|
||||||
@@ -93,7 +93,7 @@ QQC2.Control {
|
|||||||
}
|
}
|
||||||
Keys.onTabPressed: {
|
Keys.onTabPressed: {
|
||||||
if (completionMenu.visible) {
|
if (completionMenu.visible) {
|
||||||
completionMenu.complete();
|
completionMenu.completeCurrent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Keys.onPressed: event => {
|
Keys.onPressed: event => {
|
||||||
|
|||||||
@@ -91,27 +91,8 @@ QQC2.Control {
|
|||||||
QQC2.TextArea {
|
QQC2.TextArea {
|
||||||
id: codeText
|
id: codeText
|
||||||
|
|
||||||
Keys.onUpPressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.up();
|
|
||||||
}
|
|
||||||
Keys.onDownPressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.down();
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDeletePressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
root.Message.contentModel.keyHelper.deleteChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: (event) => {
|
||||||
if (event.key == Qt.Key_Backspace && cursorPosition == 0) {
|
event.accepted = Message.contentModel.keyHelper.handleKey(event.key, event.modifiers);
|
||||||
event.accepted = true;
|
|
||||||
root.Message.contentModel.keyHelper.backspace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.accepted = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onFocusChanged: if (focus && !root.currentFocus) {
|
onFocusChanged: if (focus && !root.currentFocus) {
|
||||||
|
|||||||
@@ -63,26 +63,8 @@ QQC2.TextArea {
|
|||||||
*/
|
*/
|
||||||
signal selectedTextChanged(string selectedText)
|
signal selectedTextChanged(string selectedText)
|
||||||
|
|
||||||
Keys.onUpPressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.up();
|
|
||||||
}
|
|
||||||
Keys.onDownPressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.down();
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDeletePressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.deleteChar();
|
|
||||||
}
|
|
||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: (event) => {
|
||||||
if (event.key == Qt.Key_Backspace) {
|
event.accepted = Message.contentModel.keyHelper.handleKey(event.key, event.modifiers);
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.backspace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.accepted = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|||||||
@@ -75,41 +75,8 @@ TextEdit {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumWidth: Message.maxContentWidth
|
Layout.maximumWidth: Message.maxContentWidth
|
||||||
|
|
||||||
Keys.onUpPressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.up();
|
|
||||||
}
|
|
||||||
Keys.onDownPressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.down();
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onTabPressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.tab();
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDeletePressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.deleteChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: (event) => {
|
||||||
if (event.key == Qt.Key_Backspace && cursorPosition == 0) {
|
event.accepted = Message.contentModel.keyHelper.handleKey(event.key, event.modifiers);
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.backspace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.accepted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.insertReturn();
|
|
||||||
}
|
|
||||||
Keys.onReturnPressed: (event) => {
|
|
||||||
event.accepted = true;
|
|
||||||
Message.contentModel.keyHelper.insertReturn();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onFocusChanged: if (focus && !root.currentFocus) {
|
onFocusChanged: if (focus && !root.currentFocus) {
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent)
|
|||||||
, m_keyHelper(new ChatKeyHelper(this))
|
, m_keyHelper(new ChatKeyHelper(this))
|
||||||
{
|
{
|
||||||
m_editableActive = true;
|
m_editableActive = true;
|
||||||
connectKeyHelper();
|
|
||||||
initializeModel();
|
|
||||||
|
|
||||||
connect(this, &ChatBarMessageContentModel::roomChanged, this, [this]() {
|
connect(this, &ChatBarMessageContentModel::roomChanged, this, [this]() {
|
||||||
if (m_type == ChatBarType::None || !m_room) {
|
if (m_type == ChatBarType::None || !m_room) {
|
||||||
@@ -34,15 +32,23 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
connect(m_room->cacheForType(m_type), &ChatBarCache::relationIdChanged, this, &ChatBarMessageContentModel::updateReplyModel);
|
connect(m_room->cacheForType(m_type), &ChatBarCache::relationIdChanged, this, &ChatBarMessageContentModel::updateReplyModel);
|
||||||
clearModel();
|
connect(m_room->cacheForType(m_type), &ChatBarCache::attachmentPathChanged, this, [this]() {
|
||||||
|
if (m_room->cacheForType(m_type)->attachmentPath().length() > 0) {
|
||||||
beginResetModel();
|
addAttachment(QUrl(m_room->cacheForType(m_type)->attachmentPath()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (m_room->cacheForType(m_type)->attachmentPath().length() > 0) {
|
if (m_room->cacheForType(m_type)->attachmentPath().length() > 0) {
|
||||||
addAttachment(QUrl(m_room->cacheForType(m_type)->attachmentPath()));
|
addAttachment(QUrl(m_room->cacheForType(m_type)->attachmentPath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto textSections = m_room->cacheForType(m_type)->text().split(u"\n\n"_s);
|
const auto textSections = m_room->cacheForType(m_type)->text().split(u"\n\n"_s);
|
||||||
|
if (textSections.length() == 1 && textSections[0].isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearModel();
|
||||||
|
beginResetModel();
|
||||||
for (const auto §ion : textSections) {
|
for (const auto §ion : textSections) {
|
||||||
const auto type = MessageComponentType::typeForString(section);
|
const auto type = MessageComponentType::typeForString(section);
|
||||||
auto cleanText = section;
|
auto cleanText = section;
|
||||||
@@ -54,14 +60,14 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent)
|
|||||||
}
|
}
|
||||||
insertComponent(rowCount(), type, {}, cleanText);
|
insertComponent(rowCount(), type, {}, cleanText);
|
||||||
}
|
}
|
||||||
m_currentFocusComponent = QPersistentModelIndex(index(rowCount() - 1));
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
|
||||||
|
m_currentFocusComponent = QPersistentModelIndex(index(rowCount() - 1));
|
||||||
Q_EMIT focusRowChanged();
|
Q_EMIT focusRowChanged();
|
||||||
});
|
});
|
||||||
connect(this, &ChatBarMessageContentModel::focusRowChanged, this, [this]() {
|
connect(this, &ChatBarMessageContentModel::focusRowChanged, this, [this]() {
|
||||||
m_markdownHelper->setTextItem(focusedTextItem());
|
m_markdownHelper->setTextItem(focusedTextItem());
|
||||||
m_keyHelper->setTextItem(focusedTextItem());
|
m_keyHelper->textItem = focusedTextItem();
|
||||||
});
|
});
|
||||||
connect(this, &ChatBarMessageContentModel::roomChanged, this, [this]() {
|
connect(this, &ChatBarMessageContentModel::roomChanged, this, [this]() {
|
||||||
for (const auto &component : m_components) {
|
for (const auto &component : m_components) {
|
||||||
@@ -69,6 +75,7 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent)
|
|||||||
textItem->setRoom(m_room);
|
textItem->setRoom(m_room);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_keyHelper->room = m_room;
|
||||||
});
|
});
|
||||||
connect(this, &ChatBarMessageContentModel::typeChanged, this, [this]() {
|
connect(this, &ChatBarMessageContentModel::typeChanged, this, [this]() {
|
||||||
for (const auto &component : m_components) {
|
for (const auto &component : m_components) {
|
||||||
@@ -78,6 +85,9 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(m_markdownHelper, &ChatMarkdownHelper::unhandledBlockFormat, this, &ChatBarMessageContentModel::insertStyleAtCursor);
|
connect(m_markdownHelper, &ChatMarkdownHelper::unhandledBlockFormat, this, &ChatBarMessageContentModel::insertStyleAtCursor);
|
||||||
|
|
||||||
|
connectKeyHelper();
|
||||||
|
initializeModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatBarMessageContentModel::initializeModel()
|
void ChatBarMessageContentModel::initializeModel()
|
||||||
@@ -92,9 +102,9 @@ void ChatBarMessageContentModel::initializeModel()
|
|||||||
.display = {},
|
.display = {},
|
||||||
.attributes = {{TextItemKey, QVariant::fromValue<ChatTextItemHelper *>(textItem)}},
|
.attributes = {{TextItemKey, QVariant::fromValue<ChatTextItemHelper *>(textItem)}},
|
||||||
};
|
};
|
||||||
m_currentFocusComponent = QPersistentModelIndex(index(0));
|
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
|
|
||||||
|
m_currentFocusComponent = QPersistentModelIndex(index(0));
|
||||||
Q_EMIT focusRowChanged();
|
Q_EMIT focusRowChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,10 +129,16 @@ ChatKeyHelper *ChatBarMessageContentModel::keyHelper() const
|
|||||||
|
|
||||||
void ChatBarMessageContentModel::connectKeyHelper()
|
void ChatBarMessageContentModel::connectKeyHelper()
|
||||||
{
|
{
|
||||||
connect(m_keyHelper, &ChatKeyHelper::unhandledUp, this, [this]() {
|
connect(m_keyHelper, &ChatKeyHelper::unhandledUp, this, [this](bool isCompleting) {
|
||||||
|
if (isCompleting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setFocusRow(m_currentFocusComponent.row() - 1);
|
setFocusRow(m_currentFocusComponent.row() - 1);
|
||||||
});
|
});
|
||||||
connect(m_keyHelper, &ChatKeyHelper::unhandledDown, this, [this]() {
|
connect(m_keyHelper, &ChatKeyHelper::unhandledDown, this, [this](bool isCompleting) {
|
||||||
|
if (isCompleting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setFocusRow(m_currentFocusComponent.row() + 1);
|
setFocusRow(m_currentFocusComponent.row() + 1);
|
||||||
});
|
});
|
||||||
connect(m_keyHelper, &ChatKeyHelper::unhandledDelete, this, [this]() {
|
connect(m_keyHelper, &ChatKeyHelper::unhandledDelete, this, [this]() {
|
||||||
@@ -150,6 +166,9 @@ void ChatBarMessageContentModel::connectKeyHelper()
|
|||||||
insertComponentAtCursor(MessageComponentType::Text);
|
insertComponentAtCursor(MessageComponentType::Text);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(m_keyHelper, &ChatKeyHelper::imagePasted, this, [this](const QString &filePath) {
|
||||||
|
m_room->cacheForType(m_type)->setAttachmentPath(filePath);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int ChatBarMessageContentModel::focusRow() const
|
int ChatBarMessageContentModel::focusRow() const
|
||||||
@@ -508,7 +527,6 @@ void ChatBarMessageContentModel::postMessage()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qWarning() << m_room->cacheForType(m_type)->text();
|
|
||||||
m_room->cacheForType(m_type)->postMessage();
|
m_room->cacheForType(m_type)->postMessage();
|
||||||
clearModel();
|
clearModel();
|
||||||
initializeModel();
|
initializeModel();
|
||||||
|
|||||||
Reference in New Issue
Block a user