Otclient  14/8/2020
uitextedit.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010-2020 OTClient <https://github.com/edubart/otclient>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 
23 #include "uitextedit.h"
27 #include <framework/core/clock.h>
30 #include <framework/input/mouse.h>
31 
33 {
34  m_cursorPos = 0;
36  m_textHidden = false;
37  m_shiftNavigation = false;
38  m_multiline = false;
39  m_cursorVisible = true;
40  m_cursorInRange = true;
41  m_maxLength = 0;
42  m_editable = true;
43  m_selectable = true;
44  m_autoScroll = true;
45  m_changeCursorImage = true;
46  m_selectionReference = 0;
47  m_selectionStart = 0;
48  m_selectionEnd = 0;
49  m_updatesEnabled = true;
50  m_selectionColor = Color::white;
51  m_selectionBackgroundColor = Color::black;
52  m_glyphsTextCoordsBuffer.enableHardwareCaching();
53  m_glyphsSelectCoordsBuffer.enableHardwareCaching();
54  m_glyphsMustRecache = true;
55  blinkCursor();
56 }
57 
59 {
60  if((drawPane & Fw::ForegroundPane) == 0)
61  return;
62 
67 
68  int textLength = m_text.length();
69  const TexturePtr& texture = m_font->getTexture();
70  if(!texture)
71  return;
72 
73  bool glyphsMustRecache = m_glyphsMustRecache;
74  if(glyphsMustRecache)
75  m_glyphsMustRecache = false;
76 
77  if(m_color != Color::alpha) {
78  if(glyphsMustRecache) {
79  m_glyphsTextCoordsBuffer.clear();
80  for(int i=0;i<textLength;++i)
81  m_glyphsTextCoordsBuffer.addRect(m_glyphsCoords[i], m_glyphsTexCoords[i]);
82  }
84  g_painter->drawTextureCoords(m_glyphsTextCoordsBuffer, texture);
85  }
86 
87  if(hasSelection()) {
88  if(glyphsMustRecache) {
89  m_glyphsSelectCoordsBuffer.clear();
90  for(int i=m_selectionStart;i<m_selectionEnd;++i)
91  m_glyphsSelectCoordsBuffer.addRect(m_glyphsCoords[i], m_glyphsTexCoords[i]);
92  }
93  g_painter->setColor(m_selectionBackgroundColor);
94  g_painter->drawFillCoords(m_glyphsSelectCoordsBuffer);
95  g_painter->setColor(m_selectionColor);
96  g_painter->drawTextureCoords(m_glyphsSelectCoordsBuffer, texture);
97  }
98 
99  // render cursor
100  if(isExplicitlyEnabled() && m_cursorVisible && m_cursorInRange && isActive() && m_cursorPos >= 0) {
101  assert(m_cursorPos <= textLength);
102  // draw every 333ms
103  const int delay = 333;
104  int elapsed = g_clock.millis() - m_cursorTicks;
105  if(elapsed <= delay) {
106  Rect cursorRect;
107  // when cursor is at 0
108  if(m_cursorPos == 0)
110  else
111  cursorRect = Rect(m_glyphsCoords[m_cursorPos-1].right(), m_glyphsCoords[m_cursorPos-1].top(), 1, m_font->getGlyphHeight());
112 
113  if(hasSelection() && m_cursorPos >= m_selectionStart && m_cursorPos <= m_selectionEnd)
114  g_painter->setColor(m_selectionColor);
115  else
117 
118  g_painter->drawFilledRect(cursorRect);
119  } else if(elapsed >= 2*delay) {
120  m_cursorTicks = g_clock.millis();
121  }
122  }
123 
125 }
126 
127 void UITextEdit::update(bool focusCursor)
128 {
129  if(!m_updatesEnabled)
130  return;
131 
132  std::string text = getDisplayedText();
133  m_drawText = text;
134  int textLength = text.length();
135 
136  // prevent glitches
137  if(m_rect.isEmpty())
138  return;
139 
140  // recache coords buffers
141  recacheGlyphs();
142 
143  // map glyphs positions
144  Size textBoxSize;
145  const std::vector<Point>& glyphsPositions = m_font->calculateGlyphsPositions(text, m_textAlign, &textBoxSize);
146  const Rect *glyphsTextureCoords = m_font->getGlyphsTextureCoords();
147  const Size *glyphsSize = m_font->getGlyphsSize();
148  int glyph;
149 
150  // update rect size
153  Size size = getSize();
154  if(size.width() <= 0 || (m_textHorizontalAutoResize && !m_textWrap))
155  size.setWidth(textBoxSize.width());
156  if(size.height() <= 0 || m_textVerticalAutoResize)
157  size.setHeight(textBoxSize.height());
158  setSize(size);
159  }
160 
161  // resize just on demand
162  if(textLength > (int)m_glyphsCoords.size()) {
163  m_glyphsCoords.resize(textLength);
164  m_glyphsTexCoords.resize(textLength);
165  }
166 
167  Point oldTextAreaOffset = m_textVirtualOffset;
168 
169  if(textBoxSize.width() <= getPaddingRect().width())
170  m_textVirtualOffset.x = 0;
171  if(textBoxSize.height() <= getPaddingRect().height())
172  m_textVirtualOffset.y = 0;
173 
174  // readjust start view area based on cursor position
175  m_cursorInRange = false;
176  if(focusCursor && m_autoScroll) {
177  if(m_cursorPos > 0 && textLength > 0) {
178  assert(m_cursorPos <= textLength);
179  Rect virtualRect(m_textVirtualOffset, m_rect.size() - Size(m_padding.left+m_padding.right, 0)); // previous rendered virtual rect
180  int pos = m_cursorPos - 1; // element before cursor
181  glyph = (uchar)text[pos]; // glyph of the element before cursor
182  Rect glyphRect(glyphsPositions[pos], glyphsSize[glyph]);
183 
184  // if the cursor is not on the previous rendered virtual rect we need to update it
185  if(!virtualRect.contains(glyphRect.topLeft()) || !virtualRect.contains(glyphRect.bottomRight())) {
186  // calculate where is the first glyph visible
187  Point startGlyphPos;
188  startGlyphPos.y = std::max<int>(glyphRect.bottom() - virtualRect.height(), 0);
189  startGlyphPos.x = std::max<int>(glyphRect.right() - virtualRect.width(), 0);
190 
191  // find that glyph
192  for(pos = 0; pos < textLength; ++pos) {
193  glyph = (uchar)text[pos];
194  glyphRect = Rect(glyphsPositions[pos], glyphsSize[glyph]);
195  glyphRect.setTop(std::max<int>(glyphRect.top() - m_font->getYOffset() - m_font->getGlyphSpacing().height(), 0));
196  glyphRect.setLeft(std::max<int>(glyphRect.left() - m_font->getGlyphSpacing().width(), 0));
197 
198  // first glyph entirely visible found
199  if(glyphRect.topLeft() >= startGlyphPos) {
200  m_textVirtualOffset.x = glyphsPositions[pos].x;
201  m_textVirtualOffset.y = glyphsPositions[pos].y - m_font->getYOffset();
202  break;
203  }
204  }
205  }
206  } else {
207  m_textVirtualOffset = Point(0,0);
208  }
209  m_cursorInRange = true;
210  } else {
211  if(m_cursorPos > 0 && textLength > 0) {
212  Rect virtualRect(m_textVirtualOffset, m_rect.size() - Size(2*m_padding.left+m_padding.right, 0) ); // previous rendered virtual rect
213  int pos = m_cursorPos - 1; // element before cursor
214  glyph = (uchar)text[pos]; // glyph of the element before cursor
215  Rect glyphRect(glyphsPositions[pos], glyphsSize[glyph]);
216  if(virtualRect.contains(glyphRect.topLeft()) && virtualRect.contains(glyphRect.bottomRight()))
217  m_cursorInRange = true;
218  } else {
219  m_cursorInRange = true;
220  }
221  }
222 
223  bool fireAreaUpdate = false;
224  if(oldTextAreaOffset != m_textVirtualOffset)
225  fireAreaUpdate = true;
226 
227  Rect textScreenCoords = m_rect;
228  textScreenCoords.expandLeft(-m_padding.left);
229  textScreenCoords.expandRight(-m_padding.right);
230  textScreenCoords.expandBottom(-m_padding.bottom);
231  textScreenCoords.expandTop(-m_padding.top);
232  m_drawArea = textScreenCoords;
233 
234  if(textScreenCoords.size() != m_textVirtualSize) {
235  m_textVirtualSize = textScreenCoords.size();
236  fireAreaUpdate = true;
237  }
238 
239  Size totalSize = textBoxSize;
240  if(totalSize.width() < m_textVirtualSize.width())
241  totalSize.setWidth(m_textVirtualSize.height());
242  if(totalSize.height() < m_textVirtualSize.height())
243  totalSize.setHeight(m_textVirtualSize.height());
244  if(m_textTotalSize != totalSize) {
245  m_textTotalSize = totalSize;
246  fireAreaUpdate = true;
247  }
248 
250  m_drawArea.translate(0, textScreenCoords.height() - textBoxSize.height());
251  } else if(m_textAlign & Fw::AlignVerticalCenter) {
252  m_drawArea.translate(0, (textScreenCoords.height() - textBoxSize.height()) / 2);
253  } else { // AlignTop
254  }
255 
257  m_drawArea.translate(textScreenCoords.width() - textBoxSize.width(), 0);
259  m_drawArea.translate((textScreenCoords.width() - textBoxSize.width()) / 2, 0);
260  } else { // AlignLeft
261 
262  }
263 
264  for(int i = 0; i < textLength; ++i) {
265  glyph = (uchar)text[i];
266  m_glyphsCoords[i].clear();
267 
268  // skip invalid glyphs
269  if(glyph < 32 && glyph != (uchar)'\n')
270  continue;
271 
272  // calculate initial glyph rect and texture coords
273  Rect glyphScreenCoords(glyphsPositions[i], glyphsSize[glyph]);
274  Rect glyphTextureCoords = glyphsTextureCoords[glyph];
275 
276  // first translate to align position
278  glyphScreenCoords.translate(0, textScreenCoords.height() - textBoxSize.height());
279  } else if(m_textAlign & Fw::AlignVerticalCenter) {
280  glyphScreenCoords.translate(0, (textScreenCoords.height() - textBoxSize.height()) / 2);
281  } else { // AlignTop
282  // nothing to do
283  }
284 
286  glyphScreenCoords.translate(textScreenCoords.width() - textBoxSize.width(), 0);
288  glyphScreenCoords.translate((textScreenCoords.width() - textBoxSize.width()) / 2, 0);
289  } else { // AlignLeft
290  // nothing to do
291  }
292 
293  // only render glyphs that are after startRenderPosition
294  if(glyphScreenCoords.bottom() < m_textVirtualOffset.y || glyphScreenCoords.right() < m_textVirtualOffset.x)
295  continue;
296 
297  // bound glyph topLeft to startRenderPosition
298  if(glyphScreenCoords.top() < m_textVirtualOffset.y) {
299  glyphTextureCoords.setTop(glyphTextureCoords.top() + (m_textVirtualOffset.y - glyphScreenCoords.top()));
300  glyphScreenCoords.setTop(m_textVirtualOffset.y);
301  }
302  if(glyphScreenCoords.left() < m_textVirtualOffset.x) {
303  glyphTextureCoords.setLeft(glyphTextureCoords.left() + (m_textVirtualOffset.x - glyphScreenCoords.left()));
304  glyphScreenCoords.setLeft(m_textVirtualOffset.x);
305  }
306 
307  // subtract startInternalPos
308  glyphScreenCoords.translate(-m_textVirtualOffset);
309 
310  // translate rect to screen coords
311  glyphScreenCoords.translate(textScreenCoords.topLeft());
312 
313  // only render if glyph rect is visible on screenCoords
314  if(!textScreenCoords.intersects(glyphScreenCoords))
315  continue;
316 
317  // bound glyph bottomRight to screenCoords bottomRight
318  if(glyphScreenCoords.bottom() > textScreenCoords.bottom()) {
319  glyphTextureCoords.setBottom(glyphTextureCoords.bottom() + (textScreenCoords.bottom() - glyphScreenCoords.bottom()));
320  glyphScreenCoords.setBottom(textScreenCoords.bottom());
321  }
322  if(glyphScreenCoords.right() > textScreenCoords.right()) {
323  glyphTextureCoords.setRight(glyphTextureCoords.right() + (textScreenCoords.right() - glyphScreenCoords.right()));
324  glyphScreenCoords.setRight(textScreenCoords.right());
325  }
326 
327  // render glyph
328  m_glyphsCoords[i] = glyphScreenCoords;
329  m_glyphsTexCoords[i] = glyphTextureCoords;
330  }
331 
332  if(fireAreaUpdate)
333  onTextAreaUpdate(m_textVirtualOffset, m_textVirtualSize, m_textTotalSize);
334 
335  g_app.repaint();
336 }
337 
339 {
340  if(pos < 0)
341  pos = m_text.length();
342 
343  if(pos != m_cursorPos) {
344  if(pos < 0)
345  m_cursorPos = 0;
346  else if((uint)pos >= m_text.length())
347  m_cursorPos = m_text.length();
348  else
349  m_cursorPos = pos;
350  update(true);
351  }
352 }
353 
354 void UITextEdit::setSelection(int start, int end)
355 {
356  if(start == m_selectionStart && end == m_selectionEnd)
357  return;
358 
359  if(start > end)
360  std::swap(start, end);
361 
362  if(end == -1)
363  end = m_text.length();
364 
365  m_selectionStart = stdext::clamp<int>(start, 0, (int)m_text.length());
366  m_selectionEnd = stdext::clamp<int>(end, 0, (int)m_text.length());
367  recacheGlyphs();
368 }
369 
370 void UITextEdit::setTextHidden(bool hidden)
371 {
372  m_textHidden = true;
373  update(true);
374 }
375 
377 {
378  m_textVirtualOffset = offset;
379  update();
380 }
381 
382 void UITextEdit::appendText(std::string text)
383 {
384  if(hasSelection())
385  del();
386 
387  if(m_cursorPos >= 0) {
388  // replace characters that are now allowed
389  if(!m_multiline)
390  stdext::replace_all(text, "\n", " ");
391  stdext::replace_all(text, "\r", "");
392  stdext::replace_all(text, "\t", " ");
393 
394  if(text.length() > 0) {
395  // only add text if textedit can add it
396  if(m_maxLength > 0 && m_text.length() + text.length() > m_maxLength)
397  return;
398 
399  // only ignore text append if it contains invalid characters
400  if(!m_validCharacters.empty()) {
401  for(char i: text) {
402  if(m_validCharacters.find(i) == std::string::npos)
403  return;
404  }
405  }
406 
407  std::string tmp = m_text;
408  tmp.insert(m_cursorPos, text);
409  m_cursorPos += text.length();
410  setText(tmp);
411  }
412  }
413 }
414 
416 {
417  if((c == '\n' && !m_multiline) || c == '\r')
418  return;
419 
420  if(hasSelection())
421  del();
422 
423  if(m_cursorPos >= 0) {
424  if(m_maxLength > 0 && m_text.length() + 1 > m_maxLength)
425  return;
426 
427  if(!m_validCharacters.empty() && m_validCharacters.find(c) == std::string::npos)
428  return;
429 
430  std::string tmp;
431  tmp = c;
432  std::string tmp2 = m_text;
433  tmp2.insert(m_cursorPos, tmp);
434  m_cursorPos++;
435  setText(tmp2);
436  }
437 }
438 
440 {
441  std::string tmp = m_text;
442  if(m_cursorPos >= 0 && tmp.length() > 0) {
443  if((uint)m_cursorPos >= tmp.length()) {
444  tmp.erase(tmp.begin() + (--m_cursorPos));
445  } else {
446  if(right)
447  tmp.erase(tmp.begin() + m_cursorPos);
448  else if(m_cursorPos > 0)
449  tmp.erase(tmp.begin() + --m_cursorPos);
450  }
451  setText(tmp);
452  }
453 }
454 
456 {
457  m_cursorTicks = g_clock.millis();
458  g_app.repaint();
459 }
460 
461 void UITextEdit::del(bool right)
462 {
463  if(hasSelection()) {
464  std::string tmp = m_text;
465  tmp.erase(m_selectionStart, m_selectionEnd - m_selectionStart);
466 
467  setCursorPos(m_selectionStart);
468  clearSelection();
469  setText(tmp);
470  } else
471  removeCharacter(right);
472 }
473 
474 void UITextEdit::paste(const std::string& text)
475 {
476  if(hasSelection())
477  del();
478  appendText(text);
479 }
480 
481 std::string UITextEdit::copy()
482 {
483  std::string text;
484  if(hasSelection()) {
485  text = getSelection();
487  }
488  return text;
489 }
490 
491 std::string UITextEdit::cut()
492 {
493  std::string text = copy();
494  del();
495  return text;
496 }
497 
499 {
501 }
502 
504 {
505  if(right) {
506  if((uint)m_cursorPos+1 <= m_text.length())
507  m_cursorPos++;
508  else
509  m_cursorPos = 0;
510  } else {
511  if(m_cursorPos-1 >= 0)
512  m_cursorPos--;
513  else
514  m_cursorPos = m_text.length();
515  }
516 
517  blinkCursor();
518  update(true);
519 }
520 
522 {
523  //TODO
524 }
525 
527 {
528  int textLength = m_text.length();
529 
530  // find any glyph that is actually on the
531  int candidatePos = -1;
532  Rect firstGlyphRect, lastGlyphRect;
533  for(int i=0;i<textLength;++i) {
534  Rect clickGlyphRect = m_glyphsCoords[i];
535  if(!clickGlyphRect.isValid())
536  continue;
537  if(!firstGlyphRect.isValid())
538  firstGlyphRect = clickGlyphRect;
539  lastGlyphRect = clickGlyphRect;
540  clickGlyphRect.expandTop(m_font->getYOffset() + m_font->getGlyphSpacing().height());
541  clickGlyphRect.expandLeft(m_font->getGlyphSpacing().width()+1);
542  if(clickGlyphRect.contains(pos)) {
543  candidatePos = i;
544  break;
545  }
546  else if(pos.y >= clickGlyphRect.top() && pos.y <= clickGlyphRect.bottom()) {
547  if(pos.x <= clickGlyphRect.left()) {
548  candidatePos = i;
549  break;
550  } else if(pos.x >= clickGlyphRect.right())
551  candidatePos = i+1;
552  }
553  }
554 
555  if(textLength > 0) {
556  if(pos.y < firstGlyphRect.top())
557  return 0;
558  else if(pos.y > lastGlyphRect.bottom())
559  return textLength;
560  }
561 
562  return candidatePos;
563 }
564 
566 {
567  std::string text;
568  if(m_textHidden)
569  text = std::string(m_text.length(), '*');
570  else
571  text = m_text;
572 
573  if(m_textWrap && m_rect.isValid())
574  text = m_font->wrapText(text, getPaddingRect().width() - m_textOffset.x);
575 
576  return text;
577 }
578 
580 {
581  if(!hasSelection())
582  return std::string();
583  return m_text.substr(m_selectionStart, m_selectionEnd - m_selectionStart);
584 }
585 
587 {
588  if(m_cursorPos > (int)m_text.length())
589  m_cursorPos = m_text.length();
590 
591  // any text changes reset the selection
592  if(m_selectable) {
593  m_selectionEnd = 0;
594  m_selectionStart = 0;
595  }
596 
597  blinkCursor();
598  update(true);
599 }
600 
601 void UITextEdit::onHoverChange(bool hovered)
602 {
603  if(m_changeCursorImage) {
604  if(hovered && !g_mouse.isCursorChanged())
605  g_mouse.pushCursor("text");
606  else
607  g_mouse.popCursor("text");
608  }
609 }
610 
611 void UITextEdit::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode)
612 {
613  UIWidget::onStyleApply(styleName, styleNode);
614 
615  for(const OTMLNodePtr& node : styleNode->children()) {
616  if(node->tag() == "text") {
617  setText(node->value());
618  setCursorPos(m_text.length());
619  } else if(node->tag() == "text-hidden")
620  setTextHidden(node->value<bool>());
621  else if(node->tag() == "shift-navigation")
622  setShiftNavigation(node->value<bool>());
623  else if(node->tag() == "multiline")
624  setMultiline(node->value<bool>());
625  else if(node->tag() == "max-length")
626  setMaxLength(node->value<int>());
627  else if(node->tag() == "editable")
628  setEditable(node->value<bool>());
629  else if(node->tag() == "selectable")
630  setSelectable(node->value<bool>());
631  else if(node->tag() == "selection-color")
632  setSelectionColor(node->value<Color>());
633  else if(node->tag() == "selection-background-color")
634  setSelectionBackgroundColor(node->value<Color>());
635  else if(node->tag() == "selection") {
636  Point selectionRange = node->value<Point>();
637  setSelection(selectionRange.x, selectionRange.y);
638  }
639  else if(node->tag() == "cursor-visible")
640  setCursorVisible(node->value<bool>());
641  else if(node->tag() == "change-cursor-image")
642  setChangeCursorImage(node->value<bool>());
643  else if(node->tag() == "auto-scroll")
644  setAutoScroll(node->value<bool>());
645  }
646 }
647 
648 void UITextEdit::onGeometryChange(const Rect& oldRect, const Rect& newRect)
649 {
650  update(true);
651  UIWidget::onGeometryChange(oldRect, newRect);
652 }
653 
654 void UITextEdit::onFocusChange(bool focused, Fw::FocusReason reason)
655 {
656  if(focused) {
657  if(reason == Fw::KeyboardFocusReason)
658  setCursorPos(m_text.length());
659  else
660  blinkCursor();
661  update(true);
662  } else if(m_selectable)
663  clearSelection();
664  UIWidget::onFocusChange(focused, reason);
665 }
666 
667 bool UITextEdit::onKeyPress(uchar keyCode, int keyboardModifiers, int autoRepeatTicks)
668 {
669  if(UIWidget::onKeyPress(keyCode, keyboardModifiers, autoRepeatTicks))
670  return true;
671 
672  if(keyboardModifiers == Fw::KeyboardNoModifier) {
673  if(keyCode == Fw::KeyDelete && m_editable) { // erase right character
674  if(hasSelection() || !m_text.empty()) {
675  del(true);
676  return true;
677  }
678  } else if(keyCode == Fw::KeyBackspace && m_editable) { // erase left character
679  if(hasSelection() || !m_text.empty()) {
680  del(false);
681  return true;
682  }
683  } else if(keyCode == Fw::KeyRight && !m_shiftNavigation) { // move cursor right
684  clearSelection();
686  return true;
687  } else if(keyCode == Fw::KeyLeft && !m_shiftNavigation) { // move cursor left
688  clearSelection();
689  moveCursorHorizontally(false);
690  return true;
691  } else if(keyCode == Fw::KeyHome) { // move cursor to first character
692  if(m_cursorPos != 0) {
693  clearSelection();
694  setCursorPos(0);
695  return true;
696  }
697  } else if(keyCode == Fw::KeyEnd) { // move cursor to last character
698  if(m_cursorPos != (int)m_text.length()) {
699  clearSelection();
700  setCursorPos(m_text.length());
701  return true;
702  }
703  } else if(keyCode == Fw::KeyTab && !m_shiftNavigation) {
704  clearSelection();
705  if(UIWidgetPtr parent = getParent())
706  parent->focusNextChild(Fw::KeyboardFocusReason, true);
707  return true;
708  } else if(keyCode == Fw::KeyEnter && m_multiline && m_editable) {
709  appendCharacter('\n');
710  return true;
711  } else if(keyCode == Fw::KeyUp && !m_shiftNavigation && m_multiline) {
712  moveCursorVertically(true);
713  return true;
714  } else if(keyCode == Fw::KeyDown && !m_shiftNavigation && m_multiline) {
715  moveCursorVertically(false);
716  return true;
717  }
718  } else if(keyboardModifiers == Fw::KeyboardCtrlModifier) {
719  if(keyCode == Fw::KeyV && m_editable) {
721  return true;
722  } else if(keyCode == Fw::KeyX && m_editable && m_selectable) {
723  if(hasSelection()) {
724  cut();
725  return true;
726  }
727  } else if(keyCode == Fw::KeyC && m_selectable) {
728  if(hasSelection()) {
729  copy();
730  return true;
731  }
732  } else if(keyCode == Fw::KeyA && m_selectable) {
733  if(m_text.length() > 0) {
734  selectAll();
735  return true;
736  }
737  }
738  } else if(keyboardModifiers == Fw::KeyboardShiftModifier) {
739  if(keyCode == Fw::KeyTab && !m_shiftNavigation) {
740  if(UIWidgetPtr parent = getParent())
741  parent->focusPreviousChild(Fw::KeyboardFocusReason, true);
742  return true;
743  } else if(keyCode == Fw::KeyRight || keyCode == Fw::KeyLeft) {
744 
745  int oldCursorPos = m_cursorPos;
746 
747  if(keyCode == Fw::KeyRight) // move cursor right
749  else if(keyCode == Fw::KeyLeft) // move cursor left
750  moveCursorHorizontally(false);
751 
752  if(m_shiftNavigation)
753  clearSelection();
754  else {
755  if(!hasSelection())
756  m_selectionReference = oldCursorPos;
757  setSelection(m_selectionReference, m_cursorPos);
758  }
759  return true;
760  } else if(keyCode == Fw::KeyHome) { // move cursor to first character
761  if(m_cursorPos != 0) {
762  setSelection(m_cursorPos, 0);
763  setCursorPos(0);
764  return true;
765  }
766  } else if(keyCode == Fw::KeyEnd) { // move cursor to last character
767  if(m_cursorPos != (int)m_text.length()) {
768  setSelection(m_cursorPos, m_text.length());
769  setCursorPos(m_text.length());
770  return true;
771  }
772  }
773  }
774 
775  return false;
776 }
777 
778 bool UITextEdit::onKeyText(const std::string& keyText)
779 {
780  if(m_editable) {
781  appendText(keyText);
782  return true;
783  }
784  return false;
785 }
786 
787 bool UITextEdit::onMousePress(const Point& mousePos, Fw::MouseButton button)
788 {
789  if(UIWidget::onMousePress(mousePos, button))
790  return true;
791 
792  if(button == Fw::MouseLeftButton) {
793  int pos = getTextPos(mousePos);
794  if(pos >= 0) {
795  setCursorPos(pos);
796 
797  if(m_selectable) {
798  m_selectionReference = pos;
799  setSelection(pos, pos);
800  }
801  }
802  return true;
803  }
804  return false;
805 }
806 
807 bool UITextEdit::onMouseRelease(const Point& mousePos, Fw::MouseButton button)
808 {
809  return UIWidget::onMouseRelease(mousePos, button);
810 }
811 
812 bool UITextEdit::onMouseMove(const Point& mousePos, const Point& mouseMoved)
813 {
814  if(UIWidget::onMouseMove(mousePos, mouseMoved))
815  return true;
816 
817  if(m_selectable && isPressed()) {
818  int pos = getTextPos(mousePos);
819  if(pos >= 0) {
820  setSelection(m_selectionReference, pos);
821  setCursorPos(pos);
822  }
823  return true;
824  }
825  return false;
826 }
827 
828 bool UITextEdit::onDoubleClick(const Point& mousePos)
829 {
830  if(UIWidget::onDoubleClick(mousePos))
831  return true;
832  if(m_selectable && m_text.length() > 0) {
833  selectAll();
834  return true;
835  }
836  return false;
837 }
838 
839 void UITextEdit::onTextAreaUpdate(const Point& offset, const Size& visibleSize, const Size& totalSize)
840 {
841  callLuaField("onTextAreaUpdate", offset, visibleSize, totalSize);
842 }
UITextEdit::setCursorPos
void setCursorPos(int pos)
Definition: uitextedit.cpp:338
TRect::contains
bool contains(const TPoint< T > &p, bool insideOnly=false) const
Definition: rect.h:141
Fw::KeyUp
@ KeyUp
Definition: const.h:72
UIWidget::onFocusChange
virtual void onFocusChange(bool focused, Fw::FocusReason reason)
Definition: uiwidget.cpp:1498
Painter::setColor
virtual void setColor(const Color &color)
Definition: painter.h:77
UITextEdit::getDisplayedText
std::string getDisplayedText()
Definition: uitextedit.cpp:565
Mouse::popCursor
void popCursor(const std::string &name)
Definition: mouse.cpp:76
EdgeGroup::right
T right
Definition: uiwidget.h:41
UIWidget::getPaddingRect
Rect getPaddingRect()
Definition: uiwidget.cpp:1054
Fw::KeyBackspace
@ KeyBackspace
Definition: const.h:61
UIWidget::isActive
bool isActive()
Definition: uiwidget.h:225
Fw::KeyEnter
@ KeyEnter
Definition: const.h:63
graphics.h
Mouse::pushCursor
bool pushCursor(const std::string &name)
Definition: mouse.cpp:64
UITextEdit::getSelection
std::string getSelection()
Definition: uitextedit.cpp:579
UIWidget::isPressed
bool isPressed()
Definition: uiwidget.h:230
std::swap
void swap(stdext::packed_vector< T, U > &lhs, stdext::packed_vector< T, U > &rhs)
Definition: packed_vector.h:158
Color
Definition: color.h:32
TRect::expandTop
void expandTop(T add)
Definition: rect.h:92
TPoint::y
T y
Definition: point.h:83
PlatformWindow::setClipboardText
virtual void setClipboardText(const std::string &text)=0
TSize::setWidth
void setWidth(T w)
Definition: size.h:47
platformwindow.h
UITextEdit::wrapText
void wrapText()
Definition: uitextedit.cpp:498
UIWidget::m_textOffset
Point m_textOffset
Definition: uiwidget.h:484
TRect< int >
UITextEdit::onKeyPress
virtual bool onKeyPress(uchar keyCode, int keyboardModifiers, int autoRepeatTicks)
Definition: uitextedit.cpp:667
UIWidget::m_textWrap
stdext::boolean< false > m_textWrap
Definition: uiwidget.h:485
UITextEdit::setShiftNavigation
void setShiftNavigation(bool enable)
Definition: uitextedit.h:46
UIWidget::onStyleApply
virtual void onStyleApply(const std::string &styleName, const OTMLNodePtr &styleNode)
Definition: uiwidget.cpp:1461
UITextEdit::onStyleApply
virtual void onStyleApply(const std::string &styleName, const OTMLNodePtr &styleNode)
Definition: uitextedit.cpp:611
UITextEdit::selectAll
void selectAll()
Definition: uitextedit.h:67
Fw::KeyboardNoModifier
@ KeyboardNoModifier
Definition: const.h:260
UITextEdit::setTextHidden
void setTextHidden(bool hidden)
Definition: uitextedit.cpp:370
UIWidget::m_textAlign
Fw::AlignmentFlag m_textAlign
Definition: uiwidget.h:483
Fw::AlignTopLeft
@ AlignTopLeft
Definition: const.h:200
EdgeGroup::top
T top
Definition: uiwidget.h:40
TRect::left
T left() const
Definition: rect.h:52
UITextEdit::onFocusChange
virtual void onFocusChange(bool focused, Fw::FocusReason reason)
Definition: uitextedit.cpp:654
TRect::isEmpty
bool isEmpty() const
Definition: rect.h:49
UITextEdit::copy
std::string copy()
Definition: uitextedit.cpp:481
Fw::KeyRight
@ KeyRight
Definition: const.h:75
UITextEdit::onMouseMove
virtual bool onMouseMove(const Point &mousePos, const Point &mouseMoved)
Definition: uitextedit.cpp:812
UITextEdit::drawSelf
void drawSelf(Fw::DrawPane drawPane)
Definition: uitextedit.cpp:58
UITextEdit::getTextPos
int getTextPos(Point pos)
Definition: uitextedit.cpp:526
Fw::KeyX
@ KeyX
Definition: const.h:141
TRect::setTop
void setTop(T pos)
Definition: rect.h:76
UITextEdit::paste
void paste(const std::string &text)
Definition: uitextedit.cpp:474
Fw::KeyboardFocusReason
@ KeyboardFocusReason
Definition: const.h:223
UITextEdit::onGeometryChange
virtual void onGeometryChange(const Rect &oldRect, const Rect &newRect)
Definition: uitextedit.cpp:648
Fw::KeyC
@ KeyC
Definition: const.h:120
TPoint::toSize
TSize< T > toSize() const
Definition: point.h:42
OTMLNode::children
OTMLNodeList children()
Definition: otmlnode.cpp:170
UITextEdit::appendText
void appendText(std::string text)
Definition: uitextedit.cpp:382
uitextedit.h
TRect::bottom
T bottom() const
Definition: rect.h:55
Point
TPoint< int > Point
Definition: point.h:86
Fw::KeyboardShiftModifier
@ KeyboardShiftModifier
Definition: const.h:263
PlatformWindow::getClipboardText
virtual std::string getClipboardText()=0
Fw::KeyLeft
@ KeyLeft
Definition: const.h:74
TRect::setLeft
void setLeft(T pos)
Definition: rect.h:75
UIWidget::m_textVerticalAutoResize
stdext::boolean< false > m_textVerticalAutoResize
Definition: uiwidget.h:486
UIWidget::m_text
std::string m_text
Definition: uiwidget.h:481
TSize::width
int width() const
Definition: size.h:43
UIWidget::m_rect
Rect m_rect
Definition: uiwidget.h:62
UIWidget::drawBorder
void drawBorder(const Rect &screenCoords)
Definition: uiwidgetbasestyle.cpp:348
Fw::FocusReason
FocusReason
Definition: const.h:221
Fw::MouseLeftButton
@ MouseLeftButton
Definition: const.h:248
CoordsBuffer::enableHardwareCaching
void enableHardwareCaching(HardwareBuffer::UsagePattern usagePattern=HardwareBuffer::DynamicDraw)
Definition: coordsbuffer.cpp:86
clock.h
TRect::expandLeft
void expandLeft(T add)
Definition: rect.h:91
UITextEdit::cut
std::string cut()
Definition: uitextedit.cpp:491
UITextEdit::setSelectable
void setSelectable(bool selectable)
Definition: uitextedit.h:51
UIWidget::onGeometryChange
virtual void onGeometryChange(const Rect &oldRect, const Rect &newRect)
Definition: uiwidget.cpp:1477
UIWidget::m_font
BitmapFontPtr m_font
Definition: uiwidget.h:489
TRect::setRight
void setRight(T pos)
Definition: rect.h:77
UITextEdit::setCursorVisible
void setCursorVisible(bool enable)
Definition: uitextedit.h:42
TRect::topLeft
TPoint< T > topLeft() const
Definition: rect.h:60
UITextEdit::onMousePress
virtual bool onMousePress(const Point &mousePos, Fw::MouseButton button)
Definition: uitextedit.cpp:787
UITextEdit::setSelection
void setSelection(int start, int end)
Definition: uitextedit.cpp:354
UITextEdit::clearSelection
void clearSelection()
Definition: uitextedit.h:68
g_window
PlatformWindow & g_window
Definition: platformwindow.cpp:37
Fw::KeyEnd
@ KeyEnd
Definition: const.h:69
UITextEdit::setMultiline
void setMultiline(bool enable)
Definition: uitextedit.h:47
g_mouse
Mouse g_mouse
Definition: mouse.cpp:28
BitmapFont::wrapText
std::string wrapText(const std::string &text, int maxWidth)
Definition: bitmapfont.cpp:293
TRect::expandRight
void expandRight(T add)
Definition: rect.h:93
uint
unsigned int uint
Definition: types.h:31
TSize::setHeight
void setHeight(T h)
Definition: size.h:48
UIWidget::m_padding
EdgeGroup< int > m_padding
Definition: uiwidget.h:292
LuaObject::callLuaField
R callLuaField(const std::string &field, const T &... args)
Definition: luaobject.h:172
UIWidget::onMouseRelease
virtual bool onMouseRelease(const Point &mousePos, Fw::MouseButton button)
Definition: uiwidget.cpp:1575
UITextEdit::moveCursorVertically
void moveCursorVertically(bool up)
Definition: uitextedit.cpp:521
UITextEdit::hasSelection
bool hasSelection()
Definition: uitextedit.h:83
Fw::AlignHorizontalCenter
@ AlignHorizontalCenter
Definition: const.h:198
UITextEdit::setSelectionColor
void setSelectionColor(const Color &color)
Definition: uitextedit.h:52
BitmapFont::getGlyphSpacing
Size getGlyphSpacing()
Definition: bitmapfont.h:64
mouse.h
Color::white
static const Color white
Definition: color.h:101
CoordsBuffer::clear
void clear()
Definition: coordsbuffer.h:38
UITextEdit::appendCharacter
void appendCharacter(char c)
Definition: uitextedit.cpp:415
UIWidget::setText
void setText(std::string text, bool dontFireLuaCall=false)
Definition: uiwidgettext.cpp:117
BitmapFont::getTexture
const TexturePtr & getTexture()
Definition: bitmapfont.h:62
Fw::KeyHome
@ KeyHome
Definition: const.h:68
UIWidget::getParent
UIWidgetPtr getParent()
Definition: uiwidget.h:255
Size
TSize< int > Size
Definition: size.h:107
Fw::KeyA
@ KeyA
Definition: const.h:118
UITextEdit::removeCharacter
void removeCharacter(bool right)
Definition: uitextedit.cpp:439
TPoint::x
T x
Definition: point.h:83
Mouse::isCursorChanged
bool isCursorChanged()
Definition: mouse.cpp:102
UIWidget::onMousePress
virtual bool onMousePress(const Point &mousePos, Fw::MouseButton button)
Definition: uiwidget.cpp:1560
UIWidget::onDoubleClick
virtual bool onDoubleClick(const Point &mousePos)
Definition: uiwidget.cpp:1595
UITextEdit::onDoubleClick
virtual bool onDoubleClick(const Point &mousePos)
Definition: uitextedit.cpp:828
g_app
ConsoleApplication g_app
Definition: consoleapplication.cpp:32
Color::black
static const Color black
Definition: color.h:102
UIWidget::getSize
Size getSize()
Definition: uiwidget.h:357
UIWidget::drawIcon
void drawIcon(const Rect &screenCoords)
Definition: uiwidgetbasestyle.cpp:380
Rect
TRect< int > Rect
Definition: rect.h:319
UIWidget::isExplicitlyEnabled
bool isExplicitlyEnabled()
Definition: uiwidget.h:240
UITextEdit::setMaxLength
void setMaxLength(uint maxLength)
Definition: uitextedit.h:48
Color::alpha
static const Color alpha
Definition: color.h:100
BitmapFont::getGlyphsTextureCoords
const Rect * getGlyphsTextureCoords()
Definition: bitmapfont.h:60
UIWidget::drawImage
void drawImage(const Rect &screenCoords)
Definition: uiwidgetimage.cpp:78
UITextEdit::setTextVirtualOffset
void setTextVirtualOffset(const Point &offset)
Definition: uitextedit.cpp:376
UIWidget::m_textHorizontalAutoResize
stdext::boolean< false > m_textHorizontalAutoResize
Definition: uiwidget.h:487
TSize::height
int height() const
Definition: size.h:44
Painter::resetColor
void resetColor()
Definition: painter.h:108
uchar
unsigned char uchar
Definition: types.h:29
Painter::drawFillCoords
virtual void drawFillCoords(CoordsBuffer &coordsBuffer)=0
TRect::right
T right() const
Definition: rect.h:54
BitmapFont::getYOffset
int getYOffset()
Definition: bitmapfont.h:63
TRect::isValid
bool isValid() const
Definition: rect.h:50
otmlnode.h
Fw::DrawPane
DrawPane
Definition: const.h:285
UITextEdit::setEditable
void setEditable(bool editable)
Definition: uitextedit.h:50
stdext::shared_object_ptr< Texture >
Fw::KeyV
@ KeyV
Definition: const.h:139
BitmapFont::calculateGlyphsPositions
const std::vector< Point > & calculateGlyphsPositions(const std::string &text, Fw::AlignmentFlag align=Fw::AlignTopLeft, Size *textBoxSize=nullptr)
Calculate glyphs positions to use on render, also calculates textBoxSize if wanted.
Definition: bitmapfont.cpp:172
bitmapfont.h
TRect::intersects
bool intersects(const TRect< T > &r) const
Definition: rect.h:181
UITextEdit::setChangeCursorImage
void setChangeCursorImage(bool enable)
Definition: uitextedit.h:43
UITextEdit::updateText
void updateText()
Definition: uitextedit.cpp:586
CoordsBuffer::addRect
void addRect(const Rect &dest)
Definition: coordsbuffer.h:48
Fw::ForegroundPane
@ ForegroundPane
Definition: const.h:286
TRect::expandBottom
void expandBottom(T add)
Definition: rect.h:94
stdext::replace_all
void replace_all(std::string &str, const std::string &search, const std::string &replacement)
Definition: string.cpp:268
g_painter
Painter * g_painter
Definition: painter.cpp:28
UITextEdit::del
void del(bool right=false)
Definition: uitextedit.cpp:461
UITextEdit::setAutoScroll
void setAutoScroll(bool autoScroll)
Definition: uitextedit.h:54
UIWidget::setSize
void setSize(const Size &size)
Definition: uiwidget.h:303
TRect::height
T height() const
Definition: rect.h:70
BitmapFont::getGlyphHeight
int getGlyphHeight()
Definition: bitmapfont.h:59
Fw::KeyDown
@ KeyDown
Definition: const.h:73
UITextEdit::setSelectionBackgroundColor
void setSelectionBackgroundColor(const Color &color)
Definition: uitextedit.h:53
TRect::top
T top() const
Definition: rect.h:53
UITextEdit::moveCursorHorizontally
void moveCursorHorizontally(bool right)
Definition: uitextedit.cpp:503
Clock::millis
ticks_t millis()
Definition: clock.h:37
UIWidget::onMouseMove
virtual bool onMouseMove(const Point &mousePos, const Point &mouseMoved)
Definition: uiwidget.cpp:1580
Fw::KeyboardCtrlModifier
@ KeyboardCtrlModifier
Definition: const.h:261
Fw::AlignBottom
@ AlignBottom
Definition: const.h:197
UITextEdit::onTextAreaUpdate
virtual void onTextAreaUpdate(const Point &vitualOffset, const Size &visibleSize, const Size &totalSize)
Definition: uitextedit.cpp:839
UITextEdit::onHoverChange
virtual void onHoverChange(bool hovered)
Definition: uitextedit.cpp:601
TPoint< int >
TRect::size
TSize< T > size() const
Definition: rect.h:71
EdgeGroup::bottom
T bottom
Definition: uiwidget.h:42
EdgeGroup::left
T left
Definition: uiwidget.h:43
Painter::drawFilledRect
virtual void drawFilledRect(const Rect &dest)=0
UIWidget::m_drawText
std::string m_drawText
Definition: uiwidget.h:482
TRect::width
T width() const
Definition: rect.h:69
UIWidget::onKeyPress
virtual bool onKeyPress(uchar keyCode, int keyboardModifiers, int autoRepeatTicks)
Definition: uiwidget.cpp:1550
Fw::AlignVerticalCenter
@ AlignVerticalCenter
Definition: const.h:199
UITextEdit::onMouseRelease
virtual bool onMouseRelease(const Point &mousePos, Fw::MouseButton button)
Definition: uitextedit.cpp:807
TSize< int >
g_clock
Clock g_clock
Definition: clock.cpp:25
Painter::drawTextureCoords
virtual void drawTextureCoords(CoordsBuffer &coordsBuffer, const TexturePtr &texture)=0
BitmapFont::getGlyphsSize
const Size * getGlyphsSize()
Definition: bitmapfont.h:61
Fw::MouseButton
MouseButton
Definition: const.h:246
TRect::translate
void translate(T x, T y)
Definition: rect.h:98
Fw::KeyDelete
@ KeyDelete
Definition: const.h:65
Fw::AlignRight
@ AlignRight
Definition: const.h:195
UITextEdit::blinkCursor
void blinkCursor()
Definition: uitextedit.cpp:455
Fw::KeyTab
@ KeyTab
Definition: const.h:60
UITextEdit::UITextEdit
UITextEdit()
Definition: uitextedit.cpp:32
TRect::setBottom
void setBottom(T pos)
Definition: rect.h:78
application.h
UITextEdit::onKeyText
virtual bool onKeyText(const std::string &keyText)
Definition: uitextedit.cpp:778
UIWidget::drawBackground
void drawBackground(const Rect &screenCoords)
Definition: uiwidgetbasestyle.cpp:336
UIWidget::m_color
Color m_color
Definition: uiwidget.h:281