File indexing completed on 2025-02-23 04:35:30
0001 /* 0002 SPDX-FileCopyrightText: 2020 The Qt Company Ltd. 0003 SPDX-FileCopyrightText: 2020-2022 Mladen Milinkovic <max@smoothware.net> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #ifndef RICHDOCUMENTEDITOR_H 0009 #define RICHDOCUMENTEDITOR_H 0010 0011 // based on Qt 5.15.2 private QWidgetLineControl and QLineEdit 0012 0013 #include <QtGlobal> 0014 0015 #include <QClipboard> 0016 #include <QCompleter> 0017 #include <QInputMethod> 0018 #include <QPoint> 0019 #include <QPointer> 0020 #include <QTextLayout> 0021 #include <QTextDocumentFragment> 0022 0023 #include <vector> 0024 0025 #include "core/richtext/richdocument.h" 0026 0027 #ifdef DrawText 0028 # undef DrawText 0029 #endif 0030 0031 namespace SubtitleComposer { 0032 0033 class RichDocumentEditor : public QObject 0034 { 0035 Q_OBJECT 0036 0037 public: 0038 RichDocumentEditor(); 0039 0040 bool isAcceptableInput(const QKeyEvent *event) const; 0041 static bool isCommonTextEditShortcut(const QKeyEvent *ke); 0042 0043 void setAccessibleObject(QObject *object) 0044 { 0045 Q_ASSERT(object); 0046 m_accessibleObject = object; 0047 } 0048 0049 QObject *accessibleObject() 0050 { 0051 if(m_accessibleObject) 0052 return m_accessibleObject; 0053 return parent(); 0054 } 0055 0056 bool hasSelection() const { return m_textCursor && m_textCursor->hasSelection(); } 0057 0058 inline int width() const { return qRound(m_layoutWidth); } 0059 inline int height() const { return qRound(m_layoutHeight); } 0060 inline int ascent() const { return m_ascent; } 0061 inline qreal naturalTextWidth() const { return m_layoutWidth; } 0062 0063 void setSelection(int start, int length); 0064 0065 inline QString selectedText() const { return m_textCursor ? m_textCursor->selectedText() : QString(); } 0066 inline QTextDocumentFragment selection() const { return m_textCursor ? m_textCursor->selection() : QTextDocumentFragment(); } 0067 0068 int selectionStart() const { return m_textCursor ? m_textCursor->selectionStart() : -1; } 0069 int selectionEnd() const { return m_textCursor ? m_textCursor->selectionEnd() : -1; } 0070 bool selectionContains(int cursorPos) { 0071 if(!m_textCursor) 0072 return false; 0073 return m_textCursor->selectionStart() <= cursorPos && cursorPos < m_textCursor->selectionEnd(); 0074 } 0075 bool selectionContainsX(int x) { return selectionContains(xToPos(x)); } 0076 0077 bool inSelection(int x) const 0078 { 0079 if(!m_textCursor || !m_textCursor->hasSelection()) 0080 return false; 0081 int pos = xToPos(x, QTextLine::CursorOnCharacter); 0082 return pos >= m_textCursor->selectionStart() && pos < m_textCursor->selectionEnd(); 0083 } 0084 0085 void eraseSelectedText() 0086 { 0087 removeSelectedText(); 0088 finishChange(true); 0089 } 0090 0091 int start() const { return 0; } 0092 int end() const { return m_document->length(); } 0093 0094 #ifndef QT_NO_CLIPBOARD 0095 void cut(QClipboard::Mode mode = QClipboard::Clipboard); 0096 void copy(QClipboard::Mode mode = QClipboard::Clipboard) const; 0097 void paste(QClipboard::Mode mode = QClipboard::Clipboard); 0098 #endif 0099 0100 int cursor() const{ return m_textCursor ? m_textCursor->position() : -1; } 0101 0102 int cursorWidth() const { return m_cursorWidth; } 0103 void setCursorWidth(int value) { m_cursorWidth = value; } 0104 0105 void setLineSeparatorSize(const QSizeF &size) { m_layoutSeparatorSize = size; } 0106 0107 Qt::CursorMoveStyle cursorMoveStyle() const { return m_document->defaultCursorMoveStyle(); } 0108 void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_document->setDefaultCursorMoveStyle(style); } 0109 0110 void cursorSetPosition(int pos, bool mark=false); 0111 void cursorMoveRelative(QTextCursor::MoveOperation oper, bool mark=false, int n=1); 0112 void cursorMovePosition(int steps, bool mark=false); 0113 0114 void cursorWordForward(bool mark) { cursorMoveRelative(QTextCursor::NextWord, mark); } 0115 void cursorWordBackward(bool mark) { cursorMoveRelative(QTextCursor::PreviousWord, mark); } 0116 0117 void home(bool mark); 0118 void end(bool mark); 0119 0120 int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const; 0121 QRect rectForPos(int pos) const; 0122 QRect cursorRect() const; 0123 QRect anchorRect() const; 0124 0125 qreal cursorToX(int cursor) const; 0126 qreal cursorToX() const { return cursorToX(m_textCursor ? m_textCursor->position() : 0); } 0127 0128 bool isReadOnly() const { return m_readOnly; } 0129 void setReadOnly(bool enable); 0130 0131 void setDocument(RichDocument *doc); 0132 0133 QString text() const; 0134 0135 void commitPreedit(); 0136 0137 void backspace(); 0138 void del(); 0139 void deselect() { internalDeselect(); finishChange(false); } 0140 void selectAll(); 0141 0142 enum TextType { Auto, Plain, HTML }; 0143 int insert(const QString &html, int pos=-1, TextType textType=Auto); 0144 void clear(); 0145 void selectWordAtPos(int); 0146 0147 QTextCharFormat styleAtPosition(int pos) const; 0148 0149 void toggleBold(); 0150 void toggleItalic(); 0151 void toggleUnderline(); 0152 void toggleStrikeOut(); 0153 const QColor textColor() const; 0154 void setTextColor(const QColor &color); 0155 0156 #if QT_CONFIG(completer) 0157 QCompleter *completer() const { return m_completer; } 0158 // Note that you must set the widget for the completer separately 0159 void setCompleter(const QCompleter *c) { m_completer = const_cast<QCompleter*>(c); } 0160 void complete(int key); 0161 #endif 0162 0163 int cursorPosition() const { return m_textCursor ? m_textCursor->position() : -1; } 0164 0165 // input methods 0166 #ifndef QT_NO_IM 0167 inline bool composeMode() const { return !preeditAreaText().isEmpty(); } 0168 void setPreeditArea(int cursor, const QString &text); 0169 #endif 0170 0171 int preeditAreaPosition() const; 0172 QString preeditAreaText() const; 0173 0174 Qt::LayoutDirection layoutDirection() const { 0175 if(m_layoutDirection == Qt::LayoutDirectionAuto && !m_document->isEmpty()) 0176 return m_document->toPlainText().isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; 0177 return m_layoutDirection; 0178 } 0179 void setLayoutDirection(Qt::LayoutDirection direction) 0180 { 0181 if(direction != m_layoutDirection) { 0182 m_layoutDirection = direction; 0183 updateDisplayText(); 0184 } 0185 } 0186 0187 void setFont(const QFont &font); 0188 0189 void processInputMethodEvent(QInputMethodEvent *event); 0190 void processKeyEvent(QKeyEvent* ev); 0191 0192 void setBlinkingCursorEnabled(bool enable); 0193 void updateCursorBlinking(); 0194 0195 const QPalette &palette() const { return m_palette; } 0196 void setPalette(const QPalette &p) { m_palette = p; } 0197 0198 enum DrawFlags { 0199 DrawText = 0x01, 0200 DrawSelections = 0x02, 0201 DrawCursor = 0x04, 0202 DrawAll = DrawText | DrawSelections | DrawCursor 0203 }; 0204 void draw(QPainter *, const QPoint &, const QRect &, int flags=DrawAll, int cursorPos=-1); 0205 0206 #ifndef QT_NO_SHORTCUT 0207 void processShortcutOverrideEvent(QKeyEvent *ke); 0208 #endif 0209 0210 void updateDisplayText(bool forceUpdate = false); 0211 0212 private: 0213 void init(); 0214 0215 void removeSelectedText(); 0216 0217 void internalDelete(bool wasBackspace = false); 0218 0219 inline void internalDeselect() 0220 { 0221 if(m_textCursor) { 0222 m_selDirty |= m_textCursor->hasSelection(); 0223 m_textCursor->clearSelection(); 0224 } 0225 } 0226 0227 void emitCursorPositionChanged(); 0228 0229 bool finishChange(bool edited, bool forceUpdate = false); 0230 0231 QPointer<QCompleter> m_completer; 0232 #if QT_CONFIG(completer) 0233 bool advanceToEnabledItem(int dir); 0234 #endif 0235 0236 // masking 0237 bool hasAcceptableInput(const QString &text) const; 0238 0239 virtual void timerEvent(QTimerEvent *event) override; 0240 0241 QTextLayout * cursorToLayout(int *cursorPosition) const; 0242 0243 signals: 0244 void cursorPositionChanged(int, int); 0245 void selectionChanged(); 0246 0247 void displayTextChanged(const QString &); 0248 void textChanged(const QString &); 0249 void textEdited(const QString &); 0250 0251 void resetInputContext(); 0252 void updateMicroFocus(); 0253 0254 void accepted(); 0255 void editingFinished(); 0256 void updateNeeded(const QRect &); 0257 void inputRejected(); 0258 0259 private: 0260 mutable QTextLayout *m_textLayouts = nullptr; 0261 int m_layoutCount = 0; 0262 qreal m_layoutWidth = 0.; 0263 qreal m_layoutHeight = 0.; 0264 QFont m_layoutFont; 0265 QSizeF m_layoutSeparatorSize; 0266 0267 RichDocument *m_document = nullptr; 0268 QTextCursor *m_textCursor = nullptr; 0269 0270 QPalette m_palette; 0271 Qt::LayoutDirection m_layoutDirection = Qt::LayoutDirectionAuto; 0272 int m_cursorWidth = 0; 0273 0274 uint m_readOnly : 1; 0275 uint m_textDirty : 1; 0276 uint m_selDirty : 1; 0277 uint m_blinkStatus : 1; 0278 uint m_blinkEnabled : 1; 0279 0280 int m_blinkTimer = 0; 0281 int m_ascent = 0; 0282 int m_lastCursorPos = -1; 0283 0284 int m_keyboardScheme = 0; 0285 QObject *m_accessibleObject = nullptr; 0286 0287 QVector<QTextLayout::FormatRange> m_preeditRanges; 0288 }; 0289 0290 } 0291 0292 #endif // RICHDOCUMENTEDITOR_H