HAL
code_editor.cpp
Go to the documentation of this file.
2 
6 
8 
9 #include "gui/gui_globals.h"
10 
11 #include <QPainter>
12 #include <QPropertyAnimation>
13 #include <QTextBlock>
14 #include <QDebug>
15 
16 #if QT_VERSION >= QT_VERSION_CHECK(5,13,0)
17  #include <QRegularExpression>
18 #else
19  #include <QRegExp>
20 #endif
21 
22 namespace hal
23 {
24 
26  mScrollbar(new CodeEditorScrollbar(this)),
27  mLineNumberArea(new LineNumberArea(this)),
28  mMinimap(new CodeEditorMinimap(this)),
29  mAnimation(new QPropertyAnimation(mScrollbar, "value", this)),
30  mLineNumbersEnabled(true),
31  mLineHighlightEnabled(true),
32  mMinimapEnabled(false),
33  mLineWrapEnabled(false)
34  {
35  connect(this, &CodeEditor::blockCountChanged, this, &CodeEditor::handleBlockCountChanged);
36  connect(this, &CodeEditor::updateRequest, this, &CodeEditor::updateLineNumberArea);
37  connect(this, &CodeEditor::updateRequest, this, &CodeEditor::updateMinimap);
38 
39  if (mLineHighlightEnabled)
40  {
41  connect(this, &CodeEditor::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine);
42  }
43 
44  setVerticalScrollBar(mScrollbar);
45  mScrollbar->setMinimapScrollbar(mMinimap->scrollbar());
46  mMinimap->scrollbar()->setScrollbar(mScrollbar);
47 
50 
51  mAnimation->setDuration(200);
52 
55 
57  updateLayout();
58 
59  installEventFilter(this);
60 
62  }
63 
64  bool CodeEditor::eventFilter(QObject* object, QEvent* event)
65  {
66 
67  Q_UNUSED(object)
68 
69  if (event->type() == QEvent::Wheel)
70  {
71  QWheelEvent* wheel = static_cast<QWheelEvent*>(event);
72  if (wheel->modifiers() == Qt::ControlModifier)
73  {
74  if (wheel->delta() > 0)
75  zoomIn(2);
76  else
77  zoomOut(2);
78 
79  return true;
80  }
81  }
82  return false;
83  }
84 
86  {
87  QPainter painter(mLineNumberArea);
88  painter.setFont(mLineNumberFont);
89  QPen pen;
90  pen.setColor(mLineNumberColor);
91  painter.setPen(pen);
92 
93  QTextBlock block = firstVisibleBlock();
94  int block_number = block.blockNumber();
95  qreal top = blockBoundingGeometry(block).translated(contentOffset()).top();
96  int bottom = event->rect().bottom();
97  int width = mLineNumberArea->width() - mLineNumberArea->rightOffset();
98 
99  while (block.isValid() && top <= bottom)
100  {
101  block_number++; // INCREMENT HERE SO FIRST BLOCK HAS INDEX 1 INSTEAD OF 0
102 
103  if (block.isVisible())
104  {
106  qreal height = blockBoundingGeometry(block).height();
107  painter.drawText(QRectF(0, top, width, height), Qt::AlignRight | Qt::AlignVCenter, QString::number(block_number));
108  }
109 
110  block = block.next();
111  }
112  }
113 
115  {
116  Q_UNUSED(event)
117 
118  // MIGHT BE NEEDED LATER
119  }
120 
122  {
123  // WARNING FUNCTION ONLY RETURNS CORRECT VALUES FOR MONOSPACE FONTS !
124  QFontMetrics fm(mLineNumberFont);
125  return mLineNumberArea->leftOffset() + fm.width(QString::number(blockCount())) + mLineNumberArea->rightOffset();
126  }
127 
129  {
130  return 160;
131  }
132 
134  {
135  updateLayout();
136 
137  qreal ratio = viewport()->height() / blockBoundingGeometry(document()->firstBlock()).height();
138  mMinimap->adjustSliderHeight(ratio);
139 
141  }
142 
143  void CodeEditor::clearLineHighlight()
144  {
145  QList<QTextEdit::ExtraSelection> no_selections;
146  setExtraSelections(no_selections);
147  }
148 
149  void CodeEditor::highlightCurrentLine()
150  {
151  /*
152  * Qt doesn't want to highlight blocks in line-wrap mode (even if the docs
153  * say so), so we hack our way around it by going to the end of a block and
154  * then go upwards selecting each line in that block separately.
155  */
156  QList<QTextEdit::ExtraSelection> extra_selections;
158  cursor.clearSelection();
159  cursor.movePosition(QTextCursor::EndOfBlock);
160  cursor.movePosition(QTextCursor::StartOfLine);
161  int block = cursor.blockNumber();
162  int oldPosition;
163 
164  do
165  {
166  QTextEdit::ExtraSelection selection;
167 
168  selection.format.setBackground(mCurrentLineBackground);
169  selection.format.setProperty(QTextFormat::FullWidthSelection, true);
170  selection.cursor = cursor;
171  extra_selections.append(selection);
172 
173  oldPosition = cursor.position();
174  cursor.movePosition(QTextCursor::Up);
175  // stop when we leave the block or hit the top of the document
176  } while(cursor.blockNumber() == block && cursor.position() != oldPosition);
177 
178  setExtraSelections(extra_selections);
179  }
180 
181  void CodeEditor::handleBlockCountChanged(int new_block_count)
182  {
183  Q_UNUSED(new_block_count);
184 
185  updateLayout();
186 
187  qreal ratio = viewport()->height() / blockBoundingGeometry(document()->firstBlock()).height();
188  mMinimap->adjustSliderHeight(ratio);
189  }
190 
191  void CodeEditor::updateLineNumberArea(const QRect& rect, int dy)
192  {
193  Q_UNUSED(rect)
194  Q_UNUSED(dy)
195 
196  mLineNumberArea->update();
197  }
198 
199  void CodeEditor::updateMinimap(const QRect& rect, int dy)
200  {
201  Q_UNUSED(rect)
202  Q_UNUSED(dy)
203 
204  mMinimap->update();
205  }
206 
207  void CodeEditor::search(const QString& string, SearchOptions searchOpts)
208  {
210 
212  QColor color = QColor(12, 15, 19);
213  QColor mBackgroundColor = QColor(255, 255, 0);
215 
216  options.setFlag(QTextDocument::FindCaseSensitively, searchOpts.isCaseSensitive());
217  options.setFlag(QTextDocument::FindWholeWords, searchOpts.isExactMatch());
218 
219  if(searchOpts.isRegularExpression())
220  {
221 #if QT_VERSION >= QT_VERSION_CHECK(5,13,0)
223 #else
224  QRegExp regExp(string, searchOpts.isCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive);
225 #endif
226  while (find(regExp, options))
227  {
229  extra.format.setForeground(QBrush(color));
230  extra.format.setBackground(mBackgroundColor);
231  extra.cursor = textCursor();
232  extraSelections.append(extra);
233  }
234  }
235  else
236  {
237 
238  while (find(string, options))
239  {
241  extra.format.setForeground(QBrush(color));
242  extra.format.setBackground(mBackgroundColor);
243  extra.cursor = textCursor();
244  extraSelections.append(extra);
245  }
246  }
247 
249  }
250 
252  {
253  mLineNumbersEnabled = !mLineNumbersEnabled;
254  updateLayout();
255  }
256 
258  {
259  mMinimapEnabled = !mMinimapEnabled;
260  updateLayout();
261  }
262 
264  {
265  return firstVisibleBlock().blockNumber();
266  }
267 
268  void CodeEditor::centerOnLine(const int number)
269  {
270  int total = document()->lineCount();
271 
272  if (number < 0 || number >= total)
273  return;
274 
275  if (mAnimation->state() == QPropertyAnimation::Running)
276  mAnimation->stop();
277 
278  mAnimation->setStartValue(verticalScrollBar()->value());
279  mAnimation->setEndValue(number);
280  mAnimation->start();
281  }
282 
284  {
286  font.setPointSize(pt);
288  }
289 
291  {
293  }
294 
296  {
297  return mMinimap;
298  }
299 
301  {
302  return mLineNumberFont;
303  }
304 
306  {
307  return mLineNumberColor;
308  }
309 
311  {
312  return mLineNumberBackground;
313  }
314 
316  {
317  return mLineNumberHighlightColor;
318  }
319 
321  {
322  return mLineNumberHighlightBackground;
323  }
324 
326  {
327  return mCurrentLineBackground;
328  }
329 
331  {
332  mLineNumberFont = font;
333  }
334 
336  {
337  mLineNumberColor = color;
338  }
339 
341  {
342  mLineNumberBackground = color;
343  }
344 
346  {
347  mLineNumberHighlightColor = color;
348  }
349 
351  {
352  mLineNumberHighlightBackground = color;
353  }
354 
356  {
357  mCurrentLineBackground = color;
358  }
359 
360  void CodeEditor::updateLayout()
361  {
362  int left_margin = 0;
363  int right_margin = 0;
364 
365  QRect cr = contentsRect();
366 
367  if (mLineNumbersEnabled)
368  {
369  left_margin = lineNumberAreaWidth();
370  mLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), viewport()->height()));
371  mLineNumberArea->show();
372  }
373  else
374  mLineNumberArea->hide();
375 
376  if (mMinimapEnabled)
377  {
378  right_margin = minimapWidth();
379  mMinimap->setGeometry(QRect(cr.right() - minimapWidth(), cr.top(), minimapWidth(), viewport()->height()));
380  mMinimap->show();
381  }
382  else
383  mMinimap->hide();
384 
385  setViewportMargins(left_margin, 0, right_margin, 0);
386  }
387 
389  {
390  mLineNumbersEnabled = enabled;
391  updateLayout();
392  }
393 
395  {
396  mLineHighlightEnabled = enabled;
397 
398  if(enabled)
399  {
400  connect(this, &CodeEditor::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine);
401  highlightCurrentLine();
402  }
403  else
404  {
405  disconnect(this, &CodeEditor::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine);
406  clearLineHighlight();
407  }
408 
409  updateLayout();
410  }
411 
413  {
414  mLineWrapEnabled = enabled;
416  updateLayout();
417  }
418 
419  void CodeEditor::setMinimapEnabled(bool enabled)
420  {
421  mMinimapEnabled = enabled;
422  updateLayout();
423  }
424 }
virtual void resizeEvent(QResizeEvent *event) override
void setHighlightCurrentLineEnabled(bool enabled)
int lineNumberAreaWidth()
void setLineNumberColor(QColor &color)
void setFontSize(int pt)
QColor lineNumberHighlightColor
Definition: code_editor.h:57
void setLineWrapEnabled(bool enabled)
QColor lineNumberColor
Definition: code_editor.h:55
CodeEditor(QWidget *parent=nullptr)
Definition: code_editor.cpp:25
virtual bool eventFilter(QObject *object, QEvent *event) override
Definition: code_editor.cpp:64
void centerOnLine(const int number)
int first_visible_block()
void setLineNumberBackground(QColor &color)
QColor lineNumberHighlightBackground
Definition: code_editor.h:58
void setMinimapEnabled(bool enabled)
void lineNumberAreaPaintEvent(QPaintEvent *event)
Definition: code_editor.cpp:85
void handleWheelEvent(QWheelEvent *event)
void setLineNumberHighlightColor(QColor &color)
QColor lineNumberBackground
Definition: code_editor.h:56
void setLineNumberFont(const QFont &font)
CodeEditorMinimap * minimap()
void setLineNumberHighlightBackground(QColor &color)
void toggleLineNumbers()
QFont lineNumberFont
Definition: code_editor.h:54
void search(const QString &string, SearchOptions searchOpts=8)
void minimapPaintEvent(QPaintEvent *event)
QColor currentLineBackground
Definition: code_editor.h:59
void setLineNumberEnabled(bool enabled)
void setCurrentLineBackground(QColor &color)
A minimap that supports an easier navigation in larger files.
void adjustSliderHeight(int viewport_height)
MinimapScrollbar * scrollbar()
Represents the scrollbar of the CodeEditor.
void setMinimapScrollbar(MinimapScrollbar *scrollbar)
Shows line numbers next to a CodeEditor.
void setScrollbar(QScrollBar *scrollbar)
bool isExactMatch() const
bool isCaseSensitive() const
bool isRegularExpression() const
void start(QAbstractAnimation::DeletionPolicy policy)
virtual bool event(QEvent *event) override
QScrollBar * horizontalScrollBar() const const
void setVerticalScrollBar(QScrollBar *scrollBar)
void setViewportMargins(int left, int top, int right, int bottom)
QScrollBar * verticalScrollBar() const const
QWidget * viewport() const const
int width(const QString &text, int len) const const
void setFrameStyle(int style)
Qt::KeyboardModifiers modifiers() const const
void append(const T &value)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
void installEventFilter(QObject *filterObj)
void drawText(const QPointF &position, const QString &text)
void setFont(const QFont &font)
void setPen(const QColor &color)
void setColor(const QColor &color)
QRectF blockBoundingGeometry(const QTextBlock &block) const const
void blockCountChanged(int newBlockCount)
QPointF contentOffset() const const
void cursorPositionChanged()
QTextDocument * document() const const
QList< QTextEdit::ExtraSelection > extraSelections() const const
bool find(const QString &exp, QTextDocument::FindFlags options)
QTextBlock firstVisibleBlock() const const
void setLineWrapMode(QPlainTextEdit::LineWrapMode mode)
void moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode)
virtual void resizeEvent(QResizeEvent *e) override
void setExtraSelections(const QList< QTextEdit::ExtraSelection > &selections)
QTextCursor textCursor() const const
void updateRequest(const QRect &rect, int dy)
virtual void wheelEvent(QWheelEvent *e) override
void zoomIn(int range)
void zoomOut(int range)
int left() const const
int right() const const
int top() const const
qreal height() const const
qreal top() const const
QRectF translated(qreal dx, qreal dy) const const
QString number(int n, int base)
AlignRight
CaseSensitive
NoContextMenu
ControlModifier
int blockNumber() const const
bool isValid() const const
bool isVisible() const const
QTextBlock next() const const
typedef FindFlags
void setDocumentMargin(qreal margin)
int lineCount() const const
void setDuration(int msecs)
void setEndValue(const QVariant &value)
void setStartValue(const QVariant &value)
int delta() const const
QRect contentsRect() const const
void setContextMenuPolicy(Qt::ContextMenuPolicy policy)
void ensurePolished() const const
void hide()
void setGeometry(int x, int y, int w, int h)
void show()
void update()