| 1 | /* |
| 2 | * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) |
| 3 | * |
| 4 | * This program is free software: you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License as published by |
| 6 | * the Free Software Foundation, either version 3 of the License, or |
| 7 | * (at your option) any later version. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License |
| 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
| 16 | */ |
| 17 | #ifndef QCONSOLE_H |
| 18 | #define QCONSOLE_H |
| 19 | |
| 20 | #include <QAbstractScrollArea> |
| 21 | #include <QVector> |
| 22 | #include <memory> |
| 23 | |
| 24 | struct ConsoleLine { |
| 25 | QString text; |
| 26 | QStringList fragments; |
| 27 | int maxColumns; |
| 28 | }; |
| 29 | |
| 30 | enum class ConsoleCaretType { |
| 31 | ctVerticalLine,ctHorizontalLine,ctBlock,ctHalfBlock |
| 32 | }; |
| 33 | |
| 34 | using PConsoleLine = std::shared_ptr<ConsoleLine>; |
| 35 | |
| 36 | using ConsoleLineList = QVector<PConsoleLine>; |
| 37 | |
| 38 | /** |
| 39 | * @brief The RowColumn struct |
| 40 | * column and row are 0-based |
| 41 | */ |
| 42 | struct RowColumn { |
| 43 | int column; |
| 44 | int row; |
| 45 | }; |
| 46 | |
| 47 | /** |
| 48 | * @brief The LineChar struct |
| 49 | * line and ch are 0-based |
| 50 | */ |
| 51 | struct LineChar { |
| 52 | int ch; |
| 53 | int line; |
| 54 | }; |
| 55 | |
| 56 | class QConsole; |
| 57 | class ConsoleLines : public QObject{ |
| 58 | Q_OBJECT |
| 59 | public: |
| 60 | explicit ConsoleLines(QConsole* console); |
| 61 | void addLine(const QString& line); |
| 62 | void RemoveLastLine(); |
| 63 | void changeLastLine(const QString& newLine); |
| 64 | QString getLastLine(); |
| 65 | QString getLine(int line); |
| 66 | QChar getChar(int line,int ch); |
| 67 | QChar getChar(const LineChar& lineChar); |
| 68 | /** |
| 69 | * @brief getRows |
| 70 | * @param startRow 1-based |
| 71 | * @param endRow 1-based |
| 72 | * @return |
| 73 | */ |
| 74 | QStringList getRows(int startRow, int endRow); |
| 75 | |
| 76 | LineChar rowColumnToLineChar(const RowColumn& rowColumn); |
| 77 | LineChar rowColumnToLineChar(int row ,int column); |
| 78 | RowColumn lineCharToRowColumn(const LineChar& lineChar); |
| 79 | RowColumn lineCharToRowColumn(int line, int ch); |
| 80 | int rows() const; |
| 81 | int lines() const; |
| 82 | bool layouting() const; |
| 83 | int maxLines() const; |
| 84 | void setMaxLines(int maxLines); |
| 85 | void clear(); |
| 86 | |
| 87 | int getMaxLines() const; |
| 88 | public slots: |
| 89 | void layout(); |
| 90 | signals: |
| 91 | void layoutStarted(); |
| 92 | void layoutFinished(); |
| 93 | void needRelayout(); |
| 94 | void rowsAdded(int rowCount); |
| 95 | void lastRowsRemoved(int rowCount); |
| 96 | void lastRowsChanged(int rowCount); |
| 97 | private: |
| 98 | int breakLine(const QString& line, QStringList& fragments); |
| 99 | private: |
| 100 | ConsoleLineList mLines; |
| 101 | int mRows; |
| 102 | bool mLayouting; |
| 103 | bool mNeedRelayout; |
| 104 | int mOldTabSize; |
| 105 | QConsole* mConsole; |
| 106 | int mMaxLines; |
| 107 | }; |
| 108 | |
| 109 | |
| 110 | class QConsole : public QAbstractScrollArea |
| 111 | { |
| 112 | Q_OBJECT |
| 113 | public: |
| 114 | explicit QConsole(QWidget* parent = nullptr); |
| 115 | int historySize() const; |
| 116 | void setHistorySize(int historySize); |
| 117 | |
| 118 | int tabSize() const; |
| 119 | |
| 120 | int columnsPerRow() const; |
| 121 | |
| 122 | int rowsInWindow() const; |
| 123 | int charColumns(QChar ch, int columnsBefore) const; |
| 124 | |
| 125 | void invalidate(); |
| 126 | void invalidateRows(int startRow,int endRow); |
| 127 | void invalidateRect(const QRect& rect); |
| 128 | |
| 129 | void addLine(const QString& line); |
| 130 | void addText(const QString& text); |
| 131 | void removeLastLine(); |
| 132 | void changeLastLine(const QString& line); |
| 133 | QString getLastLine(); |
| 134 | void clear(); |
| 135 | void copy(); |
| 136 | void paste(); |
| 137 | void selectAll(); |
| 138 | QString selText(); |
| 139 | |
| 140 | signals: |
| 141 | void commandInput(const QString& command); |
| 142 | private: |
| 143 | ConsoleLines mContents; |
| 144 | QStringList mCommandHistory; |
| 145 | int mHistorySize; |
| 146 | int mHistoryIndex; |
| 147 | QString mCommand; |
| 148 | QString mCurrentEditableLine; |
| 149 | // bool mIndex; |
| 150 | int mRowHeight; |
| 151 | int mTopRow; // 1-based |
| 152 | int mRowsInWindow; |
| 153 | int mColumnsPerRow; |
| 154 | int mColumnWidth; |
| 155 | bool mReadonly; |
| 156 | LineChar mSelectionBegin; |
| 157 | LineChar mSelectionEnd; |
| 158 | int mCaretChar; |
| 159 | QColor mBackground; |
| 160 | QColor mForeground; |
| 161 | QColor mSelectionBackground; |
| 162 | QColor mSelectionForeground; |
| 163 | QColor mInactiveSelectionBackground; |
| 164 | QColor mInactiveSelectionForeground; |
| 165 | int mTabSize; |
| 166 | std::shared_ptr<QImage> mContentImage; |
| 167 | int mBlinkTimerId; |
| 168 | int mBlinkStatus; |
| 169 | QTimer* mScrollTimer; |
| 170 | int mScrollDeltaY; |
| 171 | private: |
| 172 | void fontChanged(); |
| 173 | void recalcCharExtent(); |
| 174 | void sizeOrFontChanged(bool bFont); |
| 175 | int clientWidth(); |
| 176 | int clientHeight(); |
| 177 | /** |
| 178 | * @brief setTopRow |
| 179 | * @param value 1-based |
| 180 | */ |
| 181 | void setTopRow(int value); |
| 182 | int maxScrollHeight(); |
| 183 | void updateScrollbars(); |
| 184 | void paintRows(QPainter& painter, int row1,int row2); |
| 185 | void ensureCaretVisible(); |
| 186 | void showCaret(); |
| 187 | void hideCaret(); |
| 188 | void updateCaret(); |
| 189 | LineChar caretPos(); |
| 190 | RowColumn caretRowColumn(); |
| 191 | QPoint rowColumnToPixels(const RowColumn& rowColumn); |
| 192 | QRect getCaretRect(); |
| 193 | void paintCaret(QPainter &painter, const QRect rcClip); |
| 194 | void textInputed(const QString& text); |
| 195 | void loadCommandFromHistory(); |
| 196 | LineChar selectionBegin(); |
| 197 | LineChar selectionEnd(); |
| 198 | void setCaretChar(int newCaretChar, bool resetSelection); |
| 199 | bool caretInSelection(); |
| 200 | QString removeSelection(); |
| 201 | bool hasSelection(); |
| 202 | int computeScrollY(int Y); |
| 203 | RowColumn pixelsToNearestRowColumn(int x,int y); |
| 204 | QString lineBreak(); |
| 205 | |
| 206 | |
| 207 | private slots: |
| 208 | void doScrolled(); |
| 209 | void contentsLayouted(); |
| 210 | void contentsRowsAdded(int rowCount); |
| 211 | void contentsLastRowsRemoved(int rowCount); |
| 212 | void contentsLastRowsChanged(int rowCount); |
| 213 | void scrollTimerHandler(); |
| 214 | |
| 215 | // QWidget interface |
| 216 | protected: |
| 217 | void mousePressEvent(QMouseEvent *event) override; |
| 218 | void mouseReleaseEvent(QMouseEvent *event) override; |
| 219 | void mouseMoveEvent(QMouseEvent *event) override; |
| 220 | void keyPressEvent(QKeyEvent *event) override; |
| 221 | void focusInEvent(QFocusEvent *event) override; |
| 222 | void focusOutEvent(QFocusEvent *event) override; |
| 223 | void paintEvent(QPaintEvent *event) override; |
| 224 | bool event(QEvent *event) override; |
| 225 | |
| 226 | |
| 227 | // QWidget interface |
| 228 | protected: |
| 229 | void resizeEvent(QResizeEvent *event) override; |
| 230 | |
| 231 | // QObject interface |
| 232 | protected: |
| 233 | void timerEvent(QTimerEvent *event) override; |
| 234 | |
| 235 | // QWidget interface |
| 236 | protected: |
| 237 | void inputMethodEvent(QInputMethodEvent *event) override; |
| 238 | void wheelEvent(QWheelEvent *event) override; |
| 239 | }; |
| 240 | |
| 241 | |
| 242 | #endif // QCONSOLE_H |
| 243 | |