Vuo 2.4.4
Loading...
Searching...
No Matches
VuoCodeEditor.cc
Go to the documentation of this file.
1
10#include "VuoCodeEditor.hh"
11
12#include "VuoCodeGutter.hh"
14#include "VuoCodeIssueList.hh"
15#include "VuoCodeWindow.hh"
16#include "VuoEditor.hh"
17#include "VuoInputEditorIcon.hh"
18#include "VuoRendererColors.hh"
19
23VuoCodeEditor::VuoCodeEditor(QString initialSourceCode):
24 QTextEdit(),
25 highlighter(nullptr)
26{
27 setAcceptRichText(false);
28
29 setPlainText(initialSourceCode);
30
31 VuoEditor *editor = static_cast<VuoEditor *>(qApp);
32 isDark = editor->isInterfaceDark();
33 updateColor(isDark);
34
35 gutter = new VuoCodeGutter(this);
36
37 // VuoCodeGutter doesn't yet support wrapping.
38 setLineWrapMode(QTextEdit::NoWrap);
39
40 fontSizes << 8 << 9 << 10 << 11 << 12 << 13 << 14 << 16 << 20 << 28 << 36 << 48;
41 zoom11();
42
43 connect(this, &QTextEdit::cursorPositionChanged, this, &VuoCodeEditor::cursorPositionChanged);
44 cursorPositionChanged();
45
46 highlighter = new VuoCodeHighlighterGLSL(document(), this);
47}
48
49void VuoCodeEditor::setFontSize(int fontSize)
50{
51 currentFontSize = fontSize;
52
53 // Menlo is included with Mac OS 10.7+, and includes italic and bold variants (unlike Monaco).
54 QFont font("Menlo", fontSize);
55
56 int spacesPerTab = 4;
57 QFontMetricsF fontMetrics(font);
58 setTabStopDistance(spacesPerTab * fontMetrics.horizontalAdvance(' '));
59
60 setFont(font);
61
62 // A little thicker / more visible than the default 1px.
63 setCursorWidth(fontSize/5.);
64
66}
67
72{
73 setFontSize(11);
74}
75
80{
81 foreach (int fontSize, fontSizes)
82 if (fontSize > currentFontSize)
83 {
84 setFontSize(fontSize);
85 break;
86 }
87}
88
93{
94 QList<int>::reverse_iterator i;
95 for (i = fontSizes.rbegin(); i != fontSizes.rend(); ++i)
96 if (*i < currentFontSize)
97 {
98 setFontSize(*i);
99 break;
100 }
101}
102
107{
108 return currentFontSize == 11;
109}
110
114void VuoCodeEditor::selectLine(int lineNumber)
115{
116 QTextCursor cursor = textCursor();
117 cursor.movePosition(QTextCursor::Start);
118 cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, lineNumber - 1);
119
120 // Select the line, placing the cursor at the beginning of the line.
121 cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
122 cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
123
124 setTextCursor(cursor);
125 setFocus();
126}
127
128void VuoCodeEditor::keyPressEvent(QKeyEvent *event)
129{
130 int key = event->key();
131 Qt::KeyboardModifiers modifiers = event->modifiers();
132
133 if (key == Qt::Key_Tab)
134 handleTab(true);
135 else if (key == Qt::Key_BracketRight
136 && modifiers & Qt::ControlModifier
137 && !(modifiers & Qt::ShiftModifier))
138 handleTab(true);
139 else if (key == Qt::Key_BracketLeft
140 && modifiers & Qt::ControlModifier
141 && !(modifiers & Qt::ShiftModifier))
142 handleTab(false);
143 else if (key == Qt::Key_Backtab)
144 handleTab(false);
145 else if (key == Qt::Key_Return
146 && !((modifiers & Qt::AltModifier) || (modifiers & Qt::ControlModifier))) // bubble up to VuoCodeWindow
147 handleLinebreak(false);
148 else if (key == Qt::Key_BraceRight)
149 handleBlockEnd();
150 else if (key == Qt::Key_Slash && modifiers & Qt::ControlModifier)
151 handleComment();
152 else
153 QTextEdit::keyPressEvent(event);
154}
155
160void VuoCodeEditor::handleTab(bool forward)
161{
162 QTextCursor cursor = textCursor();
163
164 if (!cursor.hasSelection())
165 {
166 if (forward)
167 cursor.insertText("\t");
168 return;
169 }
170
171 cursor.beginEditBlock();
172
173 int selectionEnd = cursor.selectionEnd();
174 cursor.setPosition(cursor.selectionStart());
175 cursor.movePosition(QTextCursor::StartOfBlock);
176 do {
177 if (forward)
178 {
179 cursor.insertText("\t");
180 ++selectionEnd;
181 }
182 else
183 if (toPlainText().at(cursor.position()) == '\t')
184 {
185 cursor.deleteChar();
186 --selectionEnd;
187 cursor.movePosition(QTextCursor::NextCharacter);
188 }
189
190 if (!cursor.movePosition(QTextCursor::NextBlock))
191 break;
192 } while (cursor.position() < selectionEnd);
193
194 cursor.endEditBlock();
195}
196
201void VuoCodeEditor::handleLinebreak(bool dontBreakAtCursor)
202{
203 QTextCursor cursor = textCursor();
204 cursor.beginEditBlock();
205
206 if (dontBreakAtCursor)
207 cursor.movePosition(QTextCursor::EndOfLine);
208 cursor.insertText("\n");
209 QTextCursor insertionCursor = cursor;
210
211 // Get previous line's indent level.
212 int indentLevel = 0;
213 {
214 cursor.movePosition(QTextCursor::PreviousBlock);
215 while (toPlainText().at(cursor.position()) == '\t')
216 {
217 ++indentLevel;
218 if (!cursor.movePosition(QTextCursor::NextCharacter))
219 break;
220 }
221
222 // Adjust indentation level if the line ends with a brace (excluding trailing whitespace).
223 cursor.movePosition(QTextCursor::StartOfBlock);
224 int startPos = cursor.position();
225 cursor.movePosition(QTextCursor::EndOfBlock);
226 while (toPlainText().at(cursor.position()).isSpace() && cursor.position() >= startPos)
227 if (!cursor.movePosition(QTextCursor::PreviousCharacter))
228 break;
229 if (toPlainText().at(cursor.position()) == '{')
230 ++indentLevel;
231 }
232
233 for (int i = 0; i < indentLevel; ++i)
234 insertionCursor.insertText("\t");
235
236 cursor.endEditBlock();
237
238 // Actually move the visible cursor.
239 // If we do this before endEditBlock(), the viewport resets its scroll position.
240 setTextCursor(insertionCursor);
241}
242
246void VuoCodeEditor::handleBlockEnd()
247{
248 QTextCursor cursor = textCursor();
249 cursor.beginEditBlock();
250
251 if (toPlainText().length()
252 && toPlainText().at(fmax(0, cursor.position() - 1)) == '\t')
253 cursor.deletePreviousChar();
254
255 cursor.insertText("}");
256
257 cursor.endEditBlock();
258}
259
264int VuoCodeEditor::toggleLineComment(QTextCursor &cursor)
265{
266 cursor.movePosition(QTextCursor::StartOfBlock);
267
268 while (cursor.position() < toPlainText().length()
269 && toPlainText().at(cursor.position()).isSpace())
270 cursor.movePosition(QTextCursor::NextCharacter);
271
272 if (toPlainText().mid(cursor.position(), 2) == "//")
273 {
274 cursor.deleteChar();
275 cursor.deleteChar();
276 return -2;
277 }
278 else
279 {
280 cursor.movePosition(QTextCursor::StartOfBlock);
281 cursor.insertText("//");
282 return 2;
283 }
284}
285
289void VuoCodeEditor::handleComment()
290{
291 QTextCursor cursor = textCursor();
292 cursor.beginEditBlock();
293
294 if (cursor.hasSelection())
295 {
296 int selectionEnd = cursor.selectionEnd();
297 cursor.setPosition(cursor.selectionStart());
298 do {
299 int delta = toggleLineComment(cursor);
300 selectionEnd += delta;
301 if (!cursor.movePosition(QTextCursor::NextBlock))
302 break;
303 } while (cursor.position() < selectionEnd);
304 }
305 else
306 toggleLineComment(cursor);
307
308 cursor.endEditBlock();
309}
310
311void VuoCodeEditor::resizeEvent(QResizeEvent *event)
312{
313 QTextEdit::resizeEvent(event);
314 gutter->resize(event->size());
315}
316
317int VuoCodeEditor::getCurrentLineNumber()
318{
319 QTextCursor cursor = textCursor();
320 cursor.movePosition(QTextCursor::StartOfLine);
321
322 int lines = 1;
323 while (cursor.movePosition(QTextCursor::PreviousBlock))
324 ++lines;
325
326 return lines;
327}
328
329void VuoCodeEditor::cursorPositionChanged()
330{
331 // Highlight the current line.
332 QTextEdit::ExtraSelection selection;
333 selection.format.setBackground(currentLineColor);
334 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
335 selection.cursor = textCursor();
336 selection.cursor.clearSelection();
337 QList<QTextEdit::ExtraSelection> extraSelections;
338 extraSelections.append(selection);
339 setExtraSelections(extraSelections);
340
341
342 if (parent())
343 {
344 VuoCodeWindow *codeWindow = static_cast<VuoCodeWindow *>(parent()->parent()->parent());
345 if (codeWindow->issueList)
346 codeWindow->issueList->selectIssueForLine(getCurrentLineNumber());
347 }
348}
349
354{
356 c.setDark(isDark);
357
358 QPalette p;
359
360 QColor background = c.canvasFill();
361 p.setColor(QPalette::Base, background);
362
363 p.setColor(QPalette::Active, QPalette::Highlight, isDark ? "#12418c" : "#74acec");
364 p.setColor(QPalette::Inactive, QPalette::Highlight, isDark ? "#606060" : "#e0e0e0");
365 p.setColor(QPalette::Active, QPalette::HighlightedText, isDark ? "#c0c0c0" : "#404040");
366 p.setColor(QPalette::Inactive, QPalette::HighlightedText, isDark ? "#c0c0c0" : "#404040");
367
368 setPalette(p);
369
370 currentLineColor = c.canvasFill().lighter(isDark ? 110 : 97);
371 gutterColor = c.canvasFill().lighter(isDark ? 150 : 90);
372 gutterTextColor = c.canvasFill().lighter(isDark ? 250 : 70);
373 operatorColor = isDark ? "#c0c0c0" : "#000000";
374 commentColor = isDark ? "#606060" : "#c0c0c0";
375
376 {
377 VuoRendererColors c(VuoNode::TintMagenta);
378 c.setDark(isDark);
380 }
381
382 {
383 VuoRendererColors c(VuoNode::TintCyan);
384 c.setDark(isDark);
386 }
387
388 {
389 VuoRendererColors c(VuoNode::TintOrange);
390 c.setDark(isDark);
392 }
393
394 {
395 VuoRendererColors c(VuoNode::TintBlue);
396 c.setDark(isDark);
398 }
399
400 {
401 VuoRendererColors c(VuoNode::TintYellow);
402 c.setDark(isDark);
404 }
405
407
408 setStyleSheet(VUO_QSTRINGIFY(
409 QTextEdit {
410 background: %1;
411 color: %2;
412 }
413 ).arg(background.name())
414 // Fade out normal text a little, so keywords and operators stand out more.
415 .arg(isDark ? "#a0a0a0" : "#606060"));
416
417 if (highlighter && isDark != this->isDark)
418 {
419 this->isDark = isDark;
420
421 highlighter->rehighlight();
422 cursorPositionChanged();
423 }
424}