File indexing completed on 2024-10-06 12:53:59

0001 // SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
0002 // SPDX-License-Identifier: GPL-2.0-or-later
0003 
0004 #pragma once
0005 
0006 #include <QObject>
0007 #include <QQuickTextDocument>
0008 #include <QTextCursor>
0009 
0010 #include "models/completionmodel.h"
0011 #include "neochatroom.h"
0012 
0013 class QTextDocument;
0014 class NeoChatRoom;
0015 class SyntaxHighlighter;
0016 
0017 /**
0018  * @class ChatDocumentHandler
0019  *
0020  * Handle the QQuickTextDocument of a qml text item.
0021  *
0022  * The class provides functionality to highlight text in the text document as well
0023  * as providing completion functionality via a CompletionModel.
0024  *
0025  * The ChatDocumentHandler is also linked to a NeoChatRoom to provide functionality
0026  * to save the chat document text when switching between rooms.
0027  *
0028  * To get the full functionality the cursor position and text selection information
0029  * need to be passed in. For example:
0030  *
0031  * @code{.qml}
0032  * import QtQuick 2.0
0033  * import QtQuick.Controls 2.15 as QQC2
0034  *
0035  * import org.kde.kirigami 2.12 as Kirigami
0036  * import org.kde.neochat 1.0
0037  *
0038  * QQC2.TextArea {
0039  *      id: textField
0040  *
0041  *      // Set this to a NeoChatRoom object.
0042  *      property var room
0043  *
0044  *      ChatDocumentHandler {
0045  *          id: documentHandler
0046  *          document: textField.textDocument
0047  *          cursorPosition: textField.cursorPosition
0048  *          selectionStart: textField.selectionStart
0049  *          selectionEnd: textField.selectionEnd
0050  *          mentionColor: Kirigami.Theme.linkColor
0051  *          errorColor: Kirigami.Theme.negativeTextColor
0052  *          room: textField.room
0053  *      }
0054  * }
0055  * @endcode
0056  *
0057  * @sa QQuickTextDocument, CompletionModel, NeoChatRoom
0058  */
0059 class ChatDocumentHandler : public QObject
0060 {
0061     Q_OBJECT
0062 
0063     /**
0064      * @brief Is the instance being used to handle an edit message.
0065      *
0066      * This is needed to ensure that the text and mentions are saved and retrieved
0067      * from the correct parameters in the assigned room.
0068      */
0069     Q_PROPERTY(bool isEdit READ isEdit WRITE setIsEdit NOTIFY isEditChanged)
0070 
0071     /**
0072      * @brief The QQuickTextDocument that is being handled.
0073      */
0074     Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
0075 
0076     /**
0077      * @brief The current saved cursor position.
0078      */
0079     Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
0080 
0081     /**
0082      * @brief The start position of any currently selected text.
0083      */
0084     Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
0085 
0086     /**
0087      * @brief The end position of any currently selected text.
0088      */
0089     Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
0090 
0091     /**
0092      * @brief The current CompletionModel.
0093      *
0094      * This is typically provided to a qml component to visualise the current
0095      * completion results.
0096      */
0097     Q_PROPERTY(CompletionModel *completionModel READ completionModel NOTIFY completionModelChanged)
0098 
0099     /**
0100      * @brief The current room that the the text document is being handled for.
0101      */
0102     Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
0103 
0104     /**
0105      * @brief The color to highlight user mentions.
0106      */
0107     Q_PROPERTY(QColor mentionColor READ mentionColor WRITE setMentionColor NOTIFY mentionColorChanged);
0108 
0109     /**
0110      * @brief The color to highlight spelling errors.
0111      */
0112     Q_PROPERTY(QColor errorColor READ errorColor WRITE setErrorColor NOTIFY errorColorChanged);
0113 
0114 public:
0115     explicit ChatDocumentHandler(QObject *parent = nullptr);
0116 
0117     [[nodiscard]] bool isEdit() const;
0118     void setIsEdit(bool edit);
0119 
0120     [[nodiscard]] QQuickTextDocument *document() const;
0121     void setDocument(QQuickTextDocument *document);
0122 
0123     [[nodiscard]] int cursorPosition() const;
0124     void setCursorPosition(int position);
0125 
0126     [[nodiscard]] int selectionStart() const;
0127     void setSelectionStart(int position);
0128 
0129     [[nodiscard]] int selectionEnd() const;
0130     void setSelectionEnd(int position);
0131 
0132     [[nodiscard]] NeoChatRoom *room() const;
0133     void setRoom(NeoChatRoom *room);
0134 
0135     Q_INVOKABLE void complete(int index);
0136 
0137     void updateCompletions();
0138     CompletionModel *completionModel() const;
0139 
0140     [[nodiscard]] QColor mentionColor() const;
0141     void setMentionColor(const QColor &color);
0142 
0143     [[nodiscard]] QColor errorColor() const;
0144     void setErrorColor(const QColor &color);
0145 
0146 Q_SIGNALS:
0147     void isEditChanged();
0148     void documentChanged();
0149     void cursorPositionChanged();
0150     void roomChanged();
0151     void completionModelChanged();
0152     void selectionStartChanged();
0153     void selectionEndChanged();
0154     void errorColorChanged();
0155     void mentionColorChanged();
0156 
0157 private:
0158     int completionStartIndex() const;
0159 
0160     bool m_isEdit = false;
0161 
0162     QPointer<QQuickTextDocument> m_document;
0163 
0164     QPointer<NeoChatRoom> m_room;
0165     bool completionVisible = false;
0166 
0167     QColor m_mentionColor;
0168     QColor m_errorColor;
0169 
0170     int m_cursorPosition;
0171     int m_selectionStart;
0172     int m_selectionEnd;
0173 
0174     QString getText() const;
0175     void pushMention(const Mention mention) const;
0176 
0177     SyntaxHighlighter *m_highlighter = nullptr;
0178 
0179     CompletionModel::AutoCompletionType m_completionType = CompletionModel::None;
0180 
0181     CompletionModel *m_completionModel = nullptr;
0182 };