1 /*
2 * The ManaPlus Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2010 The Mana Developers
5 * Copyright (C) 2011-2019 The ManaPlus Developers
6 * Copyright (C) 2019-2021 Andrei Karas
7 *
8 * This file is part of The ManaPlus Client.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /* _______ __ __ __ ______ __ __ _______ __ __
25 * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
26 * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
27 * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
28 * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
29 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
30 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
31 *
32 * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
33 *
34 *
35 * Per Larsson a.k.a finalman
36 * Olof Naessén a.k.a jansem/yakslem
37 *
38 * Visit: http://guichan.sourceforge.net
39 *
40 * License: (BSD)
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 * notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 * notice, this list of conditions and the following disclaimer in
48 * the documentation and/or other materials provided with the
49 * distribution.
50 * 3. Neither the name of Guichan nor the names of its contributors may
51 * be used to endorse or promote products derived from this software
52 * without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
55 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
56 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
57 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
58 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
60 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
61 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
62 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
63 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65 */
66
67 #include "gui/widgets/textfield.h"
68
69 #include "settings.h"
70
71 #ifdef USE_SDL2
72 #include "enums/input/keyvalue.h"
73 #endif // USE_SDL2
74
75 #include "gui/gui.h"
76 #include "gui/skin.h"
77 #if defined(ANDROID) || defined(__SWITCH__)
78 #include "gui/windowmanager.h"
79 #endif // ANDROID
80
81 #include "gui/fonts/font.h"
82
83 #include "gui/popups/popupmenu.h"
84
85 #include "input/inputmanager.h"
86
87 #include "utils/copynpaste.h"
88 #include "utils/stringutils.h"
89
90 #ifndef USE_SDL2
91 #include "utils/timer.h"
92 #endif // USE_SDL2
93
94 #include "render/graphics.h"
95
96 #include "resources/imagerect.h"
97
98 #include "resources/image/image.h"
99
100 #undef DELETE // Win32 compatibility hack
101
102 #include "debug.h"
103
104 Skin *TextField::mSkin;
105 int TextField::instances = 0;
106 float TextField::mAlpha = 1.0;
107 ImageRect TextField::skin;
108
TextField(const Widget2 * restrict const widget,const std::string & restrict text,const LoseFocusOnTab loseFocusOnTab,ActionListener * restrict const listener,const std::string & restrict eventId,const bool sendAlwaysEvents)109 TextField::TextField(const Widget2 *restrict const widget,
110 const std::string &restrict text,
111 const LoseFocusOnTab loseFocusOnTab,
112 ActionListener *restrict const listener,
113 const std::string &restrict eventId,
114 const bool sendAlwaysEvents) :
115 Widget(widget),
116 FocusListener(),
117 KeyListener(),
118 MouseListener(),
119 WidgetListener(),
120 mText(text),
121 mTextChunk(),
122 mCaretPosition(0),
123 mXScroll(0),
124 mCaretColor(&getThemeColor(ThemeColorId::CARET, 255U)),
125 mMinimum(0),
126 mMaximum(0),
127 mLastEventPaste(0),
128 mPadding(1),
129 mNumeric(false),
130 mLoseFocusOnTab(loseFocusOnTab),
131 mAllowSpecialActions(true),
132 mSendAlwaysEvents(sendAlwaysEvents),
133 mTextChanged(true)
134 {
135 mAllowLogic = false;
136 setFocusable(true);
137 addMouseListener(this);
138 addKeyListener(this);
139
140 setFrameSize(2);
141 mForegroundColor = getThemeColor(ThemeColorId::TEXTFIELD, 255U);
142 mForegroundColor2 = getThemeColor(ThemeColorId::TEXTFIELD_OUTLINE, 255U);
143
144 addFocusListener(this);
145
146 if (instances == 0)
147 {
148 if (theme != nullptr)
149 {
150 mSkin = theme->loadSkinRect(skin,
151 "textfield.xml",
152 "textfield_background.xml",
153 0,
154 8);
155 }
156 }
157
158 instances++;
159
160 if (mSkin != nullptr)
161 {
162 mPadding = mSkin->getPadding();
163 mFrameSize = mSkin->getOption("frameSize", 2);
164 }
165
166 adjustSize();
167 if (!eventId.empty())
168 setActionEventId(eventId);
169
170 if (listener != nullptr)
171 addActionListener(listener);
172 }
173
~TextField()174 TextField::~TextField()
175 {
176 if (mWindow != nullptr)
177 mWindow->removeWidgetListener(this);
178
179 if (gui != nullptr)
180 gui->removeDragged(this);
181
182 instances--;
183 if (instances == 0)
184 {
185 if (theme != nullptr)
186 {
187 theme->unload(mSkin);
188 Theme::unloadRect(skin, 0, 8);
189 }
190 }
191 mTextChunk.deleteImage();
192 }
193
updateAlpha()194 void TextField::updateAlpha()
195 {
196 const float alpha = std::max(settings.guiAlpha,
197 theme->getMinimumOpacity());
198
199 if (alpha != mAlpha)
200 {
201 mAlpha = alpha;
202 for (int a = 0; a < 9; a++)
203 {
204 if (skin.grid[a] != nullptr)
205 skin.grid[a]->setAlpha(mAlpha);
206 }
207 }
208 }
209
draw(Graphics * const graphics)210 void TextField::draw(Graphics *const graphics)
211 {
212 BLOCK_START("TextField::draw")
213 updateAlpha();
214
215 Font *const font = getFont();
216 if (isFocused())
217 {
218 drawCaret(graphics,
219 font->getWidth(mText.substr(0, mCaretPosition)) - mXScroll);
220 }
221
222 if (mTextChanged)
223 {
224 mTextChunk.textFont = font;
225 mTextChunk.deleteImage();
226 mTextChunk.text = mText;
227 mTextChunk.color = mForegroundColor;
228 mTextChunk.color2 = mForegroundColor2;
229 font->generate(mTextChunk);
230 mTextChanged = false;
231 }
232
233 const Image *const image = mTextChunk.img;
234 if (image != nullptr)
235 graphics->drawImage(image, mPadding - mXScroll, mPadding);
236
237 BLOCK_END("TextField::draw")
238 }
239
safeDraw(Graphics * const graphics)240 void TextField::safeDraw(Graphics *const graphics)
241 {
242 TextField::draw(graphics);
243 }
244
drawFrame(Graphics * const graphics)245 void TextField::drawFrame(Graphics *const graphics)
246 {
247 BLOCK_START("TextField::drawFrame")
248 const int bs = 2 * mFrameSize;
249 graphics->drawImageRect(0,
250 0,
251 mDimension.width + bs,
252 mDimension.height + bs,
253 skin);
254 BLOCK_END("TextField::drawFrame")
255 }
256
safeDrawFrame(Graphics * const graphics)257 void TextField::safeDrawFrame(Graphics *const graphics)
258 {
259 BLOCK_START("TextField::drawFrame")
260 const int bs = 2 * mFrameSize;
261 graphics->drawImageRect(0,
262 0,
263 mDimension.width + bs,
264 mDimension.height + bs,
265 skin);
266 BLOCK_END("TextField::drawFrame")
267 }
268
setNumeric(const bool numeric)269 void TextField::setNumeric(const bool numeric)
270 {
271 mNumeric = numeric;
272 if (!numeric)
273 return;
274
275 const char *const text = mText.c_str();
276 for (const char *textPtr = text; *textPtr != 0; ++textPtr)
277 {
278 if (*textPtr < '0' || *textPtr > '9')
279 {
280 setText(mText.substr(0, textPtr - text));
281 return;
282 }
283 }
284 }
285
getValue() const286 int TextField::getValue() const
287 {
288 if (!mNumeric)
289 return 0;
290
291 const int value = atoi(mText.c_str());
292 if (value < mMinimum)
293 return mMinimum;
294
295 if (value > mMaximum)
296 return mMaximum;
297
298 return value;
299 }
300
keyPressed(KeyEvent & event)301 void TextField::keyPressed(KeyEvent &event)
302 {
303 const int val = event.getKey().getValue();
304
305 #ifdef USE_SDL2
306 if (val == KeyValue::TEXTINPUT)
307 {
308 std::string str = event.getText();
309 mText.insert(mCaretPosition, str);
310 mTextChanged = true;
311 mCaretPosition += CAST_U32(str.size());
312 event.consume();
313 fixScroll();
314 if (mSendAlwaysEvents)
315 distributeActionEvent();
316 return;
317 }
318 bool consumed(false);
319 #else // USE_SDL2
320
321 if (val >= 32)
322 {
323 if (mNumeric)
324 {
325 if ((val >= '0' && val <= '9') ||
326 (val == '-' && mCaretPosition == 0U))
327 {
328 char buf[2];
329 buf[0] = CAST_8(val);
330 buf[1] = 0;
331 mText.insert(mCaretPosition, std::string(buf));
332 mTextChanged = true;
333 mCaretPosition += 1;
334 event.consume();
335 fixScroll();
336 if (mSendAlwaysEvents)
337 distributeActionEvent();
338 return;
339 }
340 }
341 else if ((mMaximum == 0) ||
342 CAST_S32(mText.size()) < mMaximum)
343 {
344 int len;
345 if (val < 128)
346 len = 1; // 0xxxxxxx
347 else if (val < 0x800)
348 len = 2; // 110xxxxx 10xxxxxx
349 else if (val < 0x10000)
350 len = 3; // 1110xxxx 10xxxxxx 10xxxxxx
351 else
352 len = 4; // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
353
354 char buf[4];
355 for (int i = 0; i < len; ++ i)
356 {
357 buf[i] = CAST_8(val >> (6 * (len - i - 1)));
358 if (i > 0)
359 buf[i] = CAST_8((buf[i] & 63) | 128);
360 }
361
362 if (len > 1)
363 buf[0] |= CAST_8(255U << (8 - len));
364
365 mText.insert(mCaretPosition, std::string(buf, buf + len));
366 mCaretPosition += len;
367 mTextChanged = true;
368 event.consume();
369 fixScroll();
370 if (mSendAlwaysEvents)
371 distributeActionEvent();
372 return;
373 }
374 }
375
376 /* In UTF-8, 10xxxxxx is only used for inner parts of characters. So skip
377 them when processing key presses. */
378
379 // unblock past key
380 if (val != 22)
381 mLastEventPaste = 0;
382
383 bool consumed(false);
384 #endif // USE_SDL2
385
386 const InputActionT action = event.getActionId();
387 if (!inputManager.isActionActive(InputAction::GUI_CTRL))
388 {
389 if (!handleNormalKeys(action, consumed))
390 {
391 if (consumed)
392 event.consume();
393 return;
394 }
395 }
396 else
397 {
398 handleCtrlKeys(action, consumed);
399 }
400
401 if (mSendAlwaysEvents)
402 distributeActionEvent();
403
404 if (consumed)
405 event.consume();
406 fixScroll();
407 }
408
handleNormalKeys(const InputActionT action,bool & consumed)409 bool TextField::handleNormalKeys(const InputActionT action, bool &consumed)
410 {
411 PRAGMA45(GCC diagnostic push)
412 PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
413 switch (action)
414 {
415 case InputAction::GUI_LEFT:
416 {
417 consumed = true;
418 while (mCaretPosition > 0)
419 {
420 --mCaretPosition;
421 if ((mText[mCaretPosition] & 192) != 128)
422 break;
423 }
424 break;
425 }
426
427 case InputAction::GUI_RIGHT:
428 {
429 consumed = true;
430 const unsigned sz = CAST_U32(mText.size());
431 while (mCaretPosition < sz)
432 {
433 ++mCaretPosition;
434 if (mCaretPosition == sz ||
435 (mText[mCaretPosition] & 192) != 128)
436 {
437 break;
438 }
439 }
440 break;
441 }
442
443 case InputAction::GUI_DELETE:
444 {
445 consumed = true;
446 unsigned sz = CAST_U32(mText.size());
447 while (mCaretPosition < sz)
448 {
449 --sz;
450 mText.erase(mCaretPosition, 1);
451 mTextChanged = true;
452 if (mCaretPosition == sz ||
453 (mText[mCaretPosition] & 192) != 128)
454 {
455 break;
456 }
457 }
458 break;
459 }
460
461 case InputAction::GUI_BACKSPACE:
462 consumed = true;
463 deleteCharLeft(mText, &mCaretPosition);
464 mTextChanged = true;
465 break;
466
467 case InputAction::GUI_SELECT2:
468 distributeActionEvent();
469 consumed = true;
470 fixScroll();
471 return false;
472
473 case InputAction::GUI_HOME:
474 mCaretPosition = 0;
475 consumed = true;
476 break;
477
478 case InputAction::GUI_END:
479 mCaretPosition = CAST_U32(mText.size());
480 consumed = true;
481 break;
482
483 case InputAction::GUI_TAB:
484 if (mLoseFocusOnTab == LoseFocusOnTab_true)
485 return false;
486 consumed = true;
487 break;
488
489 default:
490 break;
491 }
492 PRAGMA45(GCC diagnostic pop)
493 return true;
494 }
495
handleCtrlKeys(const InputActionT action,bool & consumed)496 void TextField::handleCtrlKeys(const InputActionT action, bool &consumed)
497 {
498 PRAGMA45(GCC diagnostic push)
499 PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
500 switch (action)
501 {
502 case InputAction::GUI_LEFT:
503 {
504 moveCaretWordBack();
505 consumed = true;
506 break;
507 }
508 case InputAction::GUI_RIGHT:
509 {
510 moveCaretWordForward();
511 consumed = true;
512 break;
513 }
514 case InputAction::GUI_B:
515 {
516 if (mAllowSpecialActions)
517 {
518 moveCaretBack();
519 consumed = true;
520 }
521 break;
522 }
523 case InputAction::GUI_F:
524 {
525 moveCaretForward();
526 consumed = true;
527 break;
528 }
529 case InputAction::GUI_D:
530 {
531 caretDelete();
532 consumed = true;
533 break;
534 }
535 case InputAction::GUI_E:
536 {
537 mCaretPosition = CAST_S32(mText.size());
538 consumed = true;
539 break;
540 }
541 case InputAction::GUI_H:
542 {
543 deleteCharLeft(mText, &mCaretPosition);
544 mTextChanged = true;
545 consumed = true;
546 break;
547 }
548 case InputAction::GUI_K:
549 {
550 mText = mText.substr(0, mCaretPosition);
551 mTextChanged = true;
552 consumed = true;
553 break;
554 }
555 case InputAction::GUI_U:
556 {
557 caretDeleteToStart();
558 consumed = true;
559 break;
560 }
561 case InputAction::GUI_C:
562 {
563 handleCopy();
564 consumed = true;
565 break;
566 }
567 case InputAction::GUI_V:
568 {
569 #ifdef USE_SDL2
570 handlePaste();
571 #else // USE_SDL2
572
573 // hack to prevent paste key sticking
574 if ((mLastEventPaste != 0) && mLastEventPaste > cur_time)
575 break;
576 handlePaste();
577 mLastEventPaste = cur_time + 2;
578 #endif // USE_SDL2
579
580 consumed = true;
581 break;
582 }
583 case InputAction::GUI_W:
584 {
585 caretDeleteWord();
586 consumed = true;
587 break;
588 }
589 default:
590 break;
591 }
592 PRAGMA45(GCC diagnostic pop)
593 }
594
moveCaretBack()595 void TextField::moveCaretBack()
596 {
597 while (mCaretPosition > 0)
598 {
599 --mCaretPosition;
600 if ((mText[mCaretPosition] & 192) != 128)
601 break;
602 }
603 }
604
moveCaretForward()605 void TextField::moveCaretForward()
606 {
607 const unsigned sz = CAST_U32(mText.size());
608 while (mCaretPosition < sz)
609 {
610 ++mCaretPosition;
611 if (mCaretPosition == sz || (mText[mCaretPosition] & 192) != 128)
612 break;
613 }
614 }
615
caretDelete()616 void TextField::caretDelete()
617 {
618 unsigned sz = CAST_U32(mText.size());
619 while (mCaretPosition < sz)
620 {
621 --sz;
622 mText.erase(mCaretPosition, 1);
623 if (mCaretPosition == sz || (mText[mCaretPosition] & 192) != 128)
624 break;
625 }
626 mTextChanged = true;
627 }
628
handlePaste()629 void TextField::handlePaste()
630 {
631 std::string text = getText();
632 size_t caretPos = CAST_SIZE(getCaretPosition());
633
634 if (retrieveBuffer(text, caretPos))
635 {
636 setText(text);
637 setCaretPosition(CAST_U32(caretPos));
638 }
639 }
640
caretDeleteToStart()641 void TextField::caretDeleteToStart()
642 {
643 if (mCaretPosition > 0)
644 {
645 mText = mText.substr(mCaretPosition);
646 mCaretPosition = 0;
647 }
648 mTextChanged = true;
649 }
650
moveCaretWordBack()651 void TextField::moveCaretWordBack()
652 {
653 const unsigned int oldCaret = mCaretPosition;
654 while (mCaretPosition > 0)
655 {
656 if (!isWordSeparator(mText[mCaretPosition - 1]))
657 break;
658 mCaretPosition --;
659 }
660 if (oldCaret != mCaretPosition)
661 return;
662 while (mCaretPosition > 0)
663 {
664 if (isWordSeparator(mText[mCaretPosition - 1]))
665 break;
666 mCaretPosition --;
667 }
668 }
669
moveCaretWordForward()670 void TextField::moveCaretWordForward()
671 {
672 const unsigned sz = CAST_U32(mText.size());
673 const unsigned int oldCaret = mCaretPosition;
674 while (mCaretPosition < sz)
675 {
676 if (!isWordSeparator(mText[mCaretPosition]))
677 break;
678 mCaretPosition ++;
679 }
680 if (oldCaret != mCaretPosition)
681 return;
682 while (mCaretPosition < sz)
683 {
684 if (isWordSeparator(mText[mCaretPosition]))
685 break;
686 mCaretPosition ++;
687 }
688 }
689
caretDeleteWord()690 void TextField::caretDeleteWord()
691 {
692 while (mCaretPosition > 0)
693 {
694 deleteCharLeft(mText, &mCaretPosition);
695 if (mCaretPosition > 0 && isWordSeparator(mText[mCaretPosition - 1]))
696 break;
697 }
698 mTextChanged = true;
699 }
700
handleCopy() const701 void TextField::handleCopy() const
702 {
703 std::string text = getText();
704 sendBuffer(text);
705 }
706
drawCaret(Graphics * graphics,int x)707 void TextField::drawCaret(Graphics* graphics, int x)
708 {
709 const ClipRect &clipArea = graphics->getTopClip();
710
711 graphics->setColor(*mCaretColor);
712 graphics->drawLine(x + mPadding, clipArea.height - mPadding,
713 x + mPadding, mPadding);
714 }
715
adjustSize()716 void TextField::adjustSize()
717 {
718 setWidth(getFont()->getWidth(mText) + 2 * mPadding + 1);
719 adjustHeight();
720
721 fixScroll();
722 }
723
adjustHeight()724 void TextField::adjustHeight()
725 {
726 setHeight(getFont()->getHeight() + 2 * mPadding);
727 }
728
fixScroll()729 void TextField::fixScroll()
730 {
731 if (isFocused())
732 {
733 const int caretX = getFont()->getWidth(
734 mText.substr(0, mCaretPosition));
735
736 const int width = mDimension.width;
737 const int pad = 2 * mPadding;
738 if (caretX - mXScroll >= width - pad)
739 {
740 mXScroll = caretX - width + pad;
741 }
742 else if (caretX - mXScroll <= 0)
743 {
744 mXScroll = caretX - width / 2;
745
746 if (mXScroll < 0)
747 mXScroll = 0;
748 }
749 }
750 }
751
setCaretPosition(unsigned int position)752 void TextField::setCaretPosition(unsigned int position)
753 {
754 const unsigned int sz = CAST_U32(mText.size());
755 if (position > sz)
756 mCaretPosition = CAST_S32(sz);
757 else
758 mCaretPosition = position;
759
760 fixScroll();
761 }
762
fontChanged()763 void TextField::fontChanged()
764 {
765 fixScroll();
766 }
767
mousePressed(MouseEvent & event)768 void TextField::mousePressed(MouseEvent &event)
769 {
770 #if defined(ANDROID) || defined(__SWITCH__)
771 if (!WindowManager::isKeyboardVisible())
772 inputManager.executeAction(InputAction::SHOW_KEYBOARD);
773 #endif // ANDROID
774
775 event.consume();
776 if (event.getButton() == MouseButton::RIGHT)
777 {
778 #ifndef DYECMD
779 if (popupMenu != nullptr)
780 popupMenu->showTextFieldPopup(this);
781 #endif // DYECMD
782 }
783 else if (event.getButton() == MouseButton::LEFT)
784 {
785 mCaretPosition = getFont()->getStringIndexAt(
786 mText, event.getX() + mXScroll);
787 fixScroll();
788 }
789 }
790
focusGained(const Event & event A_UNUSED)791 void TextField::focusGained(const Event &event A_UNUSED)
792 {
793 #if defined(ANDROID) || defined(__SWITCH__)
794 if (!WindowManager::isKeyboardVisible())
795 inputManager.executeAction(InputAction::SHOW_KEYBOARD);
796 #endif // ANDROID
797 }
798
focusLost(const Event & event A_UNUSED)799 void TextField::focusLost(const Event &event A_UNUSED)
800 {
801 }
802
setText(const std::string & text)803 void TextField::setText(const std::string& text)
804 {
805 const unsigned int sz = CAST_U32(text.size());
806 if (sz < mCaretPosition)
807 mCaretPosition = sz;
808 mText = text;
809 mTextChanged = true;
810 }
811
mouseDragged(MouseEvent & event)812 void TextField::mouseDragged(MouseEvent& event)
813 {
814 event.consume();
815 }
816
widgetHidden(const Event & event A_UNUSED)817 void TextField::widgetHidden(const Event &event A_UNUSED)
818 {
819 mTextChanged = true;
820 mTextChunk.deleteImage();
821 }
822
setParent(Widget * widget)823 void TextField::setParent(Widget *widget)
824 {
825 if (mWindow != nullptr)
826 mWindow->addWidgetListener(this);
827 Widget::setParent(widget);
828 }
829
setWindow(Widget * const widget)830 void TextField::setWindow(Widget *const widget)
831 {
832 if ((widget == nullptr) && (mWindow != nullptr))
833 {
834 mWindow->removeWidgetListener(this);
835 mWindow = nullptr;
836 }
837 else
838 {
839 Widget2::setWindow(widget);
840 }
841 }
842
signalEvent()843 void TextField::signalEvent()
844 {
845 distributeActionEvent();
846 }
847