Vuo  2.0.1
VuoTextEditor.cc
Go to the documentation of this file.
1 
10 #include "VuoTextEditor.hh"
11 
12 extern "C"
13 {
14  #include "VuoText.h"
15 }
16 
22 {
23  textEdit = NULL;
24  isCodeEditor = false;
25 }
26 
31 void VuoTextEditor::setUpDialog(QDialog &dialog, json_object *originalValue, json_object *details)
32 {
33  isCodeEditor = getCodeEditor(details);
34  textEdit = new QPlainTextEdit(&dialog);
35 
36  textEdit->setPlainText( convertToTextEditFormat(originalValue) );
37  textEdit->setFocus();
38 
39  if (isCodeEditor)
40  {
41  textEdit->setStyleSheet(textEdit->styleSheet() + "QPlainTextEdit { font-family: Monaco; font-size: 10pt; } ");
42  }
43  else
44  {
45  textEdit->setTabChangesFocus(true);
48  }
49 
50  resizeToFitText(); // Although the height may be adjusted later, the width needs to be set now so the dialog's position can be calculated.
51  connect(textEdit, &QPlainTextEdit::textChanged, this, &VuoTextEditor::resizeToFitText);
52 
53  textEdit->setAcceptDrops(true);
54  textEdit->installEventFilter(this);
55  textEdit->viewport()->installEventFilter(this);
56 }
57 
62 {
63  return convertFromTextEditFormat(textEdit->toPlainText());
64 }
65 
70 {
71  return VuoText_makeFromJson(value);
72 }
73 
78 {
79  return VuoText_getJson( valueAsString.toUtf8().constData() );
80 }
81 
86 {
88 }
89 
96 void VuoTextEditor::resizeToFitTextWithBaseline(int baselineWidth, int baselineHeight)
97 {
98  int scrollbarWidth = 15; // textEdit->verticalScrollBar()->width() returns the wrong value the first couple times it's called…
99  int textEditWidth = ((baselineWidth > 0)? baselineWidth : (isCodeEditor ? 600 : 270)) - scrollbarWidth;
100 
101  QTextDocument *document = textEdit->document();
102  qreal margin = document->documentMargin() + textEdit->frameWidth();
103 
104  // Enforce positive text widths.
105  if (textEditWidth-2*margin-scrollbarWidth <= 0)
106  textEditWidth -= (textEditWidth-2*margin-scrollbarWidth);
107 
108  int textWidth = textEditWidth - 2*margin;
109  document->setTextWidth(textWidth);
110  QFontMetrics fm(textEdit->font());
111  int textHeight = document->lineCount() * (fm.lineSpacing() + 1);
112  int textEditHeight = textHeight + 2*margin;
113 
114  QRect screenRect = QApplication::desktop()->availableGeometry(textEdit);
115  QRect textEditRect(textEdit->mapToGlobal(textEdit->pos()), QSize(textEditWidth, textEditHeight));
116  QRect textEditInScreenRect = textEditRect.intersected(screenRect);
117  int minTextEditHeight = fm.lineSpacing() + 2*margin;
118  minTextEditHeight = max(minTextEditHeight, baselineHeight);
119  textEditHeight = qMax(textEditInScreenRect.height(), minTextEditHeight);
120 
121  textEdit->viewport()->setFixedSize(textEditWidth, textEditHeight - 1);
122  textEdit->setFixedSize(textEditWidth + scrollbarWidth, textEditHeight + 3);
123  getDialog()->setFixedSize(getDialog()->sizeHint()); // adjustSize() limits to 2/3 of the screen's size (https://doc.qt.io/qt-5/qwidget.html#adjustSize)
124 
125  textEdit->ensureCursorVisible();
126 }
127 
131 bool VuoTextEditor::eventFilter(QObject *object, QEvent *event)
132 {
133  // Handle the first part of drag-and-drop events.
134  // If the intercepted event is a drag-and-drop-related event destined for
135  // the text edit, re-route it to the input editor itself so that it may be
136  // intercepted by the window filtering input editor events.
137  // The window has access to more of the information (e.g., the
138  // composition storage directory) necessary to usefully handle the event.
139  if ((object == textEdit &&
140  (event->type() == QEvent::DragEnter ||
141  event->type() == QEvent::DragMove ||
142  event->type() == QEvent::DragLeave)) ||
143  (object == textEdit->viewport() &&
144  (event->type() == QEvent::Drop))) // drop events go to the viewport (https://bugreports.qt.io/browse/QTBUG-3748)
145  {
146  QApplication::sendEvent(this, event);
147  return true;
148  }
149 
150  // Handle Return and Option-Return key presses.
151  // For regular text editors, this makes Return dismiss the input editor and Option-Return add a linebreak.
152  // For code editors, this makes Return (or Option-Return) add a linebreak and Command-Return dismiss the input editor.
153  if (object == textEdit && event->type() == QEvent::KeyPress)
154  {
155  QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
156  if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter)
157  {
158  bool optionKeyPressed = (keyEvent->modifiers() & Qt::AltModifier);
159  bool commandKeyPressed = (keyEvent->modifiers() & Qt::ControlModifier);
160  bool shouldInsertLinebreak = (isCodeEditor ?
161  ! commandKeyPressed :
162  optionKeyPressed);
163  if (shouldInsertLinebreak)
164  textEdit->textCursor().insertText("\n");
165  else
166  QApplication::sendEvent(textEdit->parent(), event);
167  return true;
168  }
169  }
170 
171  // When the input editor is shown, perform some final setup.
172  if (object == textEdit && event->type() == QEvent::Show)
173  {
174  // Set the tab width, which couldn't be done in setUpDialog() since textEdit->font() wasn't yet ready.
175  QFontMetrics fm(textEdit->font());
176  textEdit->setTabStopWidth(4 * fm.width("W"));
177 
178  // Resize the input editor if needed so that its height fits within the screen.
179  resizeToFitText();
180 
181  if (textEdit->document()->lineCount() > 1)
182  {
183  // For multi-line text editors, set the cursor at the beginning and scroll to top.
184  textEdit->moveCursor(QTextCursor::Start);
185  textEdit->ensureCursorVisible();
186  }
187  else
188  {
189  // For one-line text editors, set the cursor at the end and select all.
190  textEdit->selectAll();
191  }
192 
193  return false;
194  }
195 
197 }
198 
202 bool VuoTextEditor::event(QEvent *event)
203 {
204  if (event->type() == QEvent::Drop)
205  {
206  // Handle the second part of drag-and-drop events.
207 
208  // This input editor will receive drop events originally intended for
209  // the text edit, re-routed to allow the dropped data to be modified by
210  // the window filtering events on the input editor, and then passed back
211  // to the input editor. Here, we forward the modified drop event
212  // to the text edit for which it was originally intended.
213 
214  // But first: If the text edit's full text was selected at the time
215  // of the drop, delete the old text before inserting the new text.
216  QTextCursor cursor = textEdit->textCursor();
217  if (cursor.selectionStart() == 0 && cursor.selectionEnd() == textEdit->toPlainText().length())
218  textEdit->setPlainText("");
219 
220  textEdit->viewport()->removeEventFilter(this);
221  QApplication::sendEvent(textEdit, event);
222  textEdit->viewport()->installEventFilter(this);
223 
224  return true;
225  }
226 
227  return VuoInputEditorWithDialog::event(event);
228 }
229 
235 {
236  if (details)
237  {
238  json_object *o = NULL;
239  if (json_object_object_get_ex(details, "isCodeEditor", &o))
240  return json_object_get_boolean(o);
241  }
242  return false;
243 }
244 
249 {
250  return true;
251 }