GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/textfield.cpp Lines: 127 365 34.8 %
Date: 2017-11-29 Branches: 49 254 19.3 %

Line Branch Exec Source
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-2017  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/*      _______   __   __   __   ______   __   __   _______   __   __
24
 *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
25
 *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
26
 *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
27
 *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
28
 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
29
 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
30
 *
31
 * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
32
 *
33
 *
34
 * Per Larsson a.k.a finalman
35
 * Olof Naessén a.k.a jansem/yakslem
36
 *
37
 * Visit: http://guichan.sourceforge.net
38
 *
39
 * License: (BSD)
40
 * Redistribution and use in source and binary forms, with or without
41
 * modification, are permitted provided that the following conditions
42
 * are met:
43
 * 1. Redistributions of source code must retain the above copyright
44
 *    notice, this list of conditions and the following disclaimer.
45
 * 2. Redistributions in binary form must reproduce the above copyright
46
 *    notice, this list of conditions and the following disclaimer in
47
 *    the documentation and/or other materials provided with the
48
 *    distribution.
49
 * 3. Neither the name of Guichan nor the names of its contributors may
50
 *    be used to endorse or promote products derived from this software
51
 *    without specific prior written permission.
52
 *
53
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
54
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
55
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
56
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
57
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
58
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
59
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
60
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
61
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
62
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
63
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64
 */
65
66
#include "gui/widgets/textfield.h"
67
68
#include "settings.h"
69
70
#ifdef USE_SDL2
71
#include "enums/input/keyvalue.h"
72
#endif  // USE_SDL2
73
74
#include "gui/gui.h"
75
#include "gui/skin.h"
76
#ifdef ANDROID
77
#include "gui/windowmanager.h"
78
#endif  // ANDROID
79
80
#include "gui/fonts/font.h"
81
82
#include "gui/popups/popupmenu.h"
83
84
#include "input/inputmanager.h"
85
86
#include "utils/copynpaste.h"
87
#include "utils/stringutils.h"
88
89
#ifndef USE_SDL2
90
#include "utils/timer.h"
91
#endif  // USE_SDL2
92
93
#include "render/graphics.h"
94
95
#include "resources/imagerect.h"
96
97
#include "resources/image/image.h"
98
99
#undef DELETE  // Win32 compatibility hack
100
101
#include "debug.h"
102
103
Skin *TextField::mSkin;
104
int TextField::instances = 0;
105
float TextField::mAlpha = 1.0;
106
ImageRect TextField::skin;
107
108
182
TextField::TextField(const Widget2 *restrict const widget,
109
                     const std::string &restrict text,
110
                     const LoseFocusOnTab loseFocusOnTab,
111
                     ActionListener *restrict const listener,
112
                     const std::string &restrict eventId,
113
182
                     const bool sendAlwaysEvents) :
114
    Widget(widget),
115
    FocusListener(),
116
    KeyListener(),
117
    MouseListener(),
118
    WidgetListener(),
119
    mText(text),
120
    mTextChunk(),
121
    mCaretPosition(0),
122
    mXScroll(0),
123
364
    mCaretColor(&getThemeColor(ThemeColorId::CARET)),
124
    mMinimum(0),
125
    mMaximum(0),
126
    mLastEventPaste(0),
127
    mPadding(1),
128
    mNumeric(false),
129
    mLoseFocusOnTab(loseFocusOnTab),
130
    mAllowSpecialActions(true),
131
    mSendAlwaysEvents(sendAlwaysEvents),
132
1092
    mTextChanged(true)
133
{
134
182
    mAllowLogic = false;
135
182
    setFocusable(true);
136
182
    addMouseListener(this);
137
182
    addKeyListener(this);
138
139
364
    setFrameSize(2);
140
364
    mForegroundColor = getThemeColor(ThemeColorId::TEXTFIELD);
141
364
    mForegroundColor2 = getThemeColor(ThemeColorId::TEXTFIELD_OUTLINE);
142
143
182
    addFocusListener(this);
144
145
182
    if (instances == 0)
146
    {
147
42
        if (theme != nullptr)
148
        {
149

294
            mSkin = theme->loadSkinRect(skin, "textfield.xml",
150
                "textfield_background.xml");
151
        }
152
    }
153
154
182
    instances++;
155
156
182
    if (mSkin != nullptr)
157
    {
158
364
        mPadding = mSkin->getPadding();
159

728
        mFrameSize = mSkin->getOption("frameSize", 2);
160
    }
161
162
182
    adjustSize();
163
182
    if (!eventId.empty())
164
42
        setActionEventId(eventId);
165
166
182
    if (listener != nullptr)
167
42
        addActionListener(listener);
168
182
}
169
170
1372
TextField::~TextField()
171
{
172
182
    if (mWindow != nullptr)
173
182
        mWindow->removeWidgetListener(this);
174
175
182
    if (gui != nullptr)
176
182
        gui->removeDragged(this);
177
178
182
    instances--;
179
182
    if (instances == 0)
180
    {
181
42
        if (theme != nullptr)
182
        {
183
42
            theme->unload(mSkin);
184
42
            Theme::unloadRect(skin);
185
        }
186
    }
187
182
    mTextChunk.deleteImage();
188
280
}
189
190
58
void TextField::updateAlpha()
191
{
192
    const float alpha = std::max(settings.guiAlpha,
193
174
        theme->getMinimumOpacity());
194
195
58
    if (alpha != mAlpha)
196
    {
197
        mAlpha = alpha;
198
        for (int a = 0; a < 9; a++)
199
        {
200
            if (skin.grid[a] != nullptr)
201
                skin.grid[a]->setAlpha(mAlpha);
202
        }
203
    }
204
58
}
205
206
58
void TextField::draw(Graphics *const graphics)
207
{
208
    BLOCK_START("TextField::draw")
209
58
    updateAlpha();
210
211
58
    Font *const font = getFont();
212
58
    if (isFocused())
213
    {
214
8
        drawCaret(graphics,
215
16
            font->getWidth(mText.substr(0, mCaretPosition)) - mXScroll);
216
    }
217
218
58
    if (mTextChanged)
219
    {
220
58
        mTextChunk.textFont = font;
221
58
        mTextChunk.deleteImage();
222
116
        mTextChunk.text = mText;
223
58
        mTextChunk.color = mForegroundColor;
224
58
        mTextChunk.color2 = mForegroundColor2;
225
58
        font->generate(mTextChunk);
226
58
        mTextChanged = false;
227
    }
228
229
58
    const Image *const image = mTextChunk.img;
230
58
    if (image != nullptr)
231
16
        graphics->drawImage(image, mPadding - mXScroll, mPadding);
232
233
    BLOCK_END("TextField::draw")
234
58
}
235
236
void TextField::safeDraw(Graphics *const graphics)
237
{
238
    TextField::draw(graphics);
239
}
240
241
58
void TextField::drawFrame(Graphics *const graphics)
242
{
243
    BLOCK_START("TextField::drawFrame")
244
58
    const int bs = 2 * mFrameSize;
245
116
    graphics->drawImageRect(0,
246
        0,
247
58
        mDimension.width + bs,
248
58
        mDimension.height + bs,
249
58
        skin);
250
    BLOCK_END("TextField::drawFrame")
251
58
}
252
253
void TextField::safeDrawFrame(Graphics *const graphics)
254
{
255
    BLOCK_START("TextField::drawFrame")
256
    const int bs = 2 * mFrameSize;
257
    graphics->drawImageRect(0,
258
        0,
259
        mDimension.width + bs,
260
        mDimension.height + bs,
261
        skin);
262
    BLOCK_END("TextField::drawFrame")
263
}
264
265
18
void TextField::setNumeric(const bool numeric)
266
{
267
18
    mNumeric = numeric;
268
18
    if (!numeric)
269
        return;
270
271
36
    const char *const text = mText.c_str();
272
18
    for (const char *textPtr = text; *textPtr != 0; ++textPtr)
273
    {
274
        if (*textPtr < '0' || *textPtr > '9')
275
        {
276
            setText(mText.substr(0, textPtr - text));
277
            return;
278
        }
279
    }
280
}
281
282
16
int TextField::getValue() const
283
{
284
16
    if (!mNumeric)
285
        return 0;
286
287
48
    const int value = atoi(mText.c_str());
288
16
    if (value < mMinimum)
289
        return mMinimum;
290
291
12
    if (value > mMaximum)
292
        return mMaximum;
293
294
    return value;
295
}
296
297
void TextField::keyPressed(KeyEvent &event)
298
{
299
    const int val = event.getKey().getValue();
300
301
#ifdef USE_SDL2
302
    if (val == KeyValue::TEXTINPUT)
303
    {
304
        std::string str = event.getText();
305
        mText.insert(mCaretPosition, str);
306
        mTextChanged = true;
307
        mCaretPosition += CAST_U32(str.size());
308
        event.consume();
309
        fixScroll();
310
        if (mSendAlwaysEvents)
311
            distributeActionEvent();
312
        return;
313
    }
314
    bool consumed(false);
315
#else  // USE_SDL2
316
317
    if (val >= 32)
318
    {
319
        if (mNumeric)
320
        {
321
            if ((val >= '0' && val <= '9') ||
322
                (val == '-' && mCaretPosition == 0u))
323
            {
324
                char buf[2];
325
                buf[0] = CAST_8(val);
326
                buf[1] = 0;
327
                mText.insert(mCaretPosition, std::string(buf));
328
                mTextChanged = true;
329
                mCaretPosition += 1;
330
                event.consume();
331
                fixScroll();
332
                if (mSendAlwaysEvents)
333
                    distributeActionEvent();
334
                return;
335
            }
336
        }
337
        else if ((mMaximum == 0) ||
338
                 CAST_S32(mText.size()) < mMaximum)
339
        {
340
            int len;
341
            if (val < 128)
342
                len = 1;               // 0xxxxxxx
343
            else if (val < 0x800)
344
                len = 2;               // 110xxxxx 10xxxxxx
345
            else if (val < 0x10000)
346
                len = 3;               // 1110xxxx 10xxxxxx 10xxxxxx
347
            else
348
                len = 4;               // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
349
350
            char buf[4];
351
            for (int i = 0; i < len; ++ i)
352
            {
353
                buf[i] = CAST_8(val >> (6 * (len - i - 1)));
354
                if (i > 0)
355
                    buf[i] = CAST_8((buf[i] & 63) | 128);
356
            }
357
358
            if (len > 1)
359
                buf[0] |= CAST_8(255U << (8 - len));
360
361
            mText.insert(mCaretPosition, std::string(buf, buf + len));
362
            mCaretPosition += len;
363
            mTextChanged = true;
364
            event.consume();
365
            fixScroll();
366
            if (mSendAlwaysEvents)
367
                distributeActionEvent();
368
            return;
369
        }
370
    }
371
372
    /* In UTF-8, 10xxxxxx is only used for inner parts of characters. So skip
373
       them when processing key presses. */
374
375
    // unblock past key
376
    if (val != 22)
377
        mLastEventPaste = 0;
378
379
    bool consumed(false);
380
#endif  // USE_SDL2
381
382
    const InputActionT action = event.getActionId();
383
    if (!inputManager.isActionActive(InputAction::GUI_CTRL))
384
    {
385
        if (!handleNormalKeys(action, consumed))
386
        {
387
            if (consumed)
388
                event.consume();
389
            return;
390
        }
391
    }
392
    else
393
    {
394
        handleCtrlKeys(action, consumed);
395
    }
396
397
    if (mSendAlwaysEvents)
398
        distributeActionEvent();
399
400
    if (consumed)
401
        event.consume();
402
    fixScroll();
403
}
404
405
bool TextField::handleNormalKeys(const InputActionT action, bool &consumed)
406
{
407
    PRAGMA45(GCC diagnostic push)
408
    PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
409
    switch (action)
410
    {
411
        case InputAction::GUI_LEFT:
412
        {
413
            consumed = true;
414
            while (mCaretPosition > 0)
415
            {
416
                --mCaretPosition;
417
                if ((mText[mCaretPosition] & 192) != 128)
418
                    break;
419
            }
420
            break;
421
        }
422
423
        case InputAction::GUI_RIGHT:
424
        {
425
            consumed = true;
426
            const unsigned sz = CAST_U32(mText.size());
427
            while (mCaretPosition < sz)
428
            {
429
                ++mCaretPosition;
430
                if (mCaretPosition == sz ||
431
                    (mText[mCaretPosition] & 192) != 128)
432
                {
433
                    break;
434
                }
435
            }
436
            break;
437
        }
438
439
        case InputAction::GUI_DELETE:
440
        {
441
            consumed = true;
442
            unsigned sz = CAST_U32(mText.size());
443
            while (mCaretPosition < sz)
444
            {
445
                --sz;
446
                mText.erase(mCaretPosition, 1);
447
                mTextChanged = true;
448
                if (mCaretPosition == sz ||
449
                    (mText[mCaretPosition] & 192) != 128)
450
                {
451
                    break;
452
                }
453
            }
454
            break;
455
        }
456
457
        case InputAction::GUI_BACKSPACE:
458
            consumed = true;
459
            deleteCharLeft(mText, &mCaretPosition);
460
            mTextChanged = true;
461
            break;
462
463
        case InputAction::GUI_SELECT2:
464
            distributeActionEvent();
465
            consumed = true;
466
            fixScroll();
467
            return false;
468
469
        case InputAction::GUI_HOME:
470
            mCaretPosition = 0;
471
            consumed = true;
472
            break;
473
474
        case InputAction::GUI_END:
475
            mCaretPosition = CAST_U32(mText.size());
476
            consumed = true;
477
            break;
478
479
        case InputAction::GUI_TAB:
480
            if (mLoseFocusOnTab == LoseFocusOnTab_true)
481
                return false;
482
            consumed = true;
483
            break;
484
485
        default:
486
            break;
487
    }
488
    PRAGMA45(GCC diagnostic pop)
489
    return true;
490
}
491
492
void TextField::handleCtrlKeys(const InputActionT action, bool &consumed)
493
{
494
    PRAGMA45(GCC diagnostic push)
495
    PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
496
    switch (action)
497
    {
498
        case InputAction::GUI_LEFT:
499
        {
500
            moveCaretWordBack();
501
            consumed = true;
502
            break;
503
        }
504
        case InputAction::GUI_RIGHT:
505
        {
506
            moveCaretWordForward();
507
            consumed = true;
508
            break;
509
        }
510
        case InputAction::GUI_B:
511
        {
512
            if (mAllowSpecialActions)
513
            {
514
                moveCaretBack();
515
                consumed = true;
516
            }
517
            break;
518
        }
519
        case InputAction::GUI_F:
520
        {
521
            moveCaretForward();
522
            consumed = true;
523
            break;
524
        }
525
        case InputAction::GUI_D:
526
        {
527
            caretDelete();
528
            consumed = true;
529
            break;
530
        }
531
        case InputAction::GUI_E:
532
        {
533
            mCaretPosition = CAST_S32(mText.size());
534
            consumed = true;
535
            break;
536
        }
537
        case InputAction::GUI_H:
538
        {
539
            deleteCharLeft(mText, &mCaretPosition);
540
            mTextChanged = true;
541
            consumed = true;
542
            break;
543
        }
544
        case InputAction::GUI_K:
545
        {
546
            mText = mText.substr(0, mCaretPosition);
547
            mTextChanged = true;
548
            consumed = true;
549
            break;
550
        }
551
        case InputAction::GUI_U:
552
        {
553
            caretDeleteToStart();
554
            consumed = true;
555
            break;
556
        }
557
        case InputAction::GUI_C:
558
        {
559
            handleCopy();
560
            consumed = true;
561
            break;
562
        }
563
        case InputAction::GUI_V:
564
        {
565
#ifdef USE_SDL2
566
            handlePaste();
567
#else  // USE_SDL2
568
569
            // hack to prevent paste key sticking
570
            if ((mLastEventPaste != 0) && mLastEventPaste > cur_time)
571
                break;
572
            handlePaste();
573
            mLastEventPaste = cur_time + 2;
574
#endif  // USE_SDL2
575
576
            consumed = true;
577
            break;
578
        }
579
        case InputAction::GUI_W:
580
        {
581
            caretDeleteWord();
582
            consumed = true;
583
            break;
584
        }
585
        default:
586
            break;
587
    }
588
    PRAGMA45(GCC diagnostic pop)
589
}
590
591
void TextField::moveCaretBack()
592
{
593
    while (mCaretPosition > 0)
594
    {
595
        --mCaretPosition;
596
        if ((mText[mCaretPosition] & 192) != 128)
597
        break;
598
    }
599
}
600
601
void TextField::moveCaretForward()
602
{
603
    const unsigned sz = CAST_U32(mText.size());
604
    while (mCaretPosition < sz)
605
    {
606
        ++mCaretPosition;
607
        if (mCaretPosition == sz || (mText[mCaretPosition] & 192) != 128)
608
            break;
609
    }
610
}
611
612
void TextField::caretDelete()
613
{
614
    unsigned sz = CAST_U32(mText.size());
615
    while (mCaretPosition < sz)
616
    {
617
        --sz;
618
        mText.erase(mCaretPosition, 1);
619
        if (mCaretPosition == sz || (mText[mCaretPosition] & 192) != 128)
620
            break;
621
    }
622
    mTextChanged = true;
623
}
624
625
void TextField::handlePaste()
626
{
627
    std::string text = getText();
628
    size_t caretPos = CAST_SIZE(getCaretPosition());
629
630
    if (retrieveBuffer(text, caretPos))
631
    {
632
        setText(text);
633
        setCaretPosition(CAST_U32(caretPos));
634
    }
635
}
636
637
void TextField::caretDeleteToStart()
638
{
639
    if (mCaretPosition > 0)
640
    {
641
        mText = mText.substr(mCaretPosition);
642
        mCaretPosition = 0;
643
    }
644
    mTextChanged = true;
645
}
646
647
void TextField::moveCaretWordBack()
648
{
649
    const unsigned int oldCaret = mCaretPosition;
650
    while (mCaretPosition > 0)
651
    {
652
        if (!isWordSeparator(mText[mCaretPosition - 1]))
653
            break;
654
        mCaretPosition --;
655
    }
656
    if (oldCaret != mCaretPosition)
657
        return;
658
    while (mCaretPosition > 0)
659
    {
660
        if (isWordSeparator(mText[mCaretPosition - 1]))
661
            break;
662
        mCaretPosition --;
663
    }
664
}
665
666
void TextField::moveCaretWordForward()
667
{
668
    const unsigned sz = CAST_U32(mText.size());
669
    const unsigned int oldCaret = mCaretPosition;
670
    while (mCaretPosition < sz)
671
    {
672
        if (!isWordSeparator(mText[mCaretPosition]))
673
            break;
674
        mCaretPosition ++;
675
    }
676
    if (oldCaret != mCaretPosition)
677
        return;
678
    while (mCaretPosition < sz)
679
    {
680
        if (isWordSeparator(mText[mCaretPosition]))
681
            break;
682
        mCaretPosition ++;
683
    }
684
}
685
686
void TextField::caretDeleteWord()
687
{
688
    while (mCaretPosition > 0)
689
    {
690
        deleteCharLeft(mText, &mCaretPosition);
691
        if (mCaretPosition > 0 && isWordSeparator(mText[mCaretPosition - 1]))
692
            break;
693
    }
694
    mTextChanged = true;
695
}
696
697
void TextField::handleCopy() const
698
{
699
    std::string text = getText();
700
    sendBuffer(text);
701
}
702
703
8
void TextField::drawCaret(Graphics* graphics, int x)
704
{
705
8
    const ClipRect &clipArea = graphics->getTopClip();
706
707
8
    graphics->setColor(*mCaretColor);
708
8
    graphics->drawLine(x + mPadding, clipArea.height - mPadding,
709
8
        x + mPadding, mPadding);
710
8
}
711
712
184
void TextField::adjustSize()
713
{
714
184
    setWidth(getFont()->getWidth(mText) + 2 * mPadding + 1);
715
184
    adjustHeight();
716
717
184
    fixScroll();
718
184
}
719
720
184
void TextField::adjustHeight()
721
{
722
184
    setHeight(getFont()->getHeight() + 2 * mPadding);
723
184
}
724
725
196
void TextField::fixScroll()
726
{
727
196
    if (isFocused())
728
    {
729
2
        const int caretX = getFont()->getWidth(
730
4
            mText.substr(0, mCaretPosition));
731
732
2
        const int width = mDimension.width;
733
2
        const int pad = 2 * mPadding;
734
2
        if (caretX - mXScroll >= width - pad)
735
        {
736
            mXScroll = caretX - width + pad;
737
        }
738
2
        else if (caretX - mXScroll <= 0)
739
        {
740
2
            mXScroll = caretX - width / 2;
741
742
2
            if (mXScroll < 0)
743
2
                mXScroll = 0;
744
        }
745
    }
746
196
}
747
748
12
void TextField::setCaretPosition(unsigned int position)
749
{
750
12
    const unsigned int sz = CAST_U32(mText.size());
751

12
    if (position > sz)
752
10
        mCaretPosition = CAST_S32(sz);
753
    else
754
2
        mCaretPosition = position;
755
756
12
    fixScroll();
757
12
}
758
759
void TextField::fontChanged()
760
{
761
    fixScroll();
762
}
763
764
void TextField::mousePressed(MouseEvent &event)
765
{
766
#ifdef ANDROID
767
    if (!WindowManager::isKeyboardVisible())
768
        inputManager.executeAction(InputAction::SHOW_KEYBOARD);
769
#endif  // ANDROID
770
771
    event.consume();
772
    if (event.getButton() == MouseButton::RIGHT)
773
    {
774
#ifndef DYECMD
775
        if (popupMenu != nullptr)
776
            popupMenu->showTextFieldPopup(this);
777
#endif  // DYECMD
778
    }
779
    else if (event.getButton() == MouseButton::LEFT)
780
    {
781
        mCaretPosition = getFont()->getStringIndexAt(
782
            mText, event.getX() + mXScroll);
783
        fixScroll();
784
    }
785
}
786
787
8
void TextField::focusGained(const Event &event A_UNUSED)
788
{
789
#ifdef ANDROID
790
    if (!WindowManager::isKeyboardVisible())
791
        inputManager.executeAction(InputAction::SHOW_KEYBOARD);
792
#endif  // ANDROID
793
8
}
794
795
void TextField::focusLost(const Event &event A_UNUSED)
796
{
797
}
798
799
24
void TextField::setText(const std::string& text)
800
{
801
24
    const unsigned int sz = CAST_U32(text.size());
802

24
    if (sz < mCaretPosition)
803
        mCaretPosition = sz;
804
48
    mText = text;
805
24
    mTextChanged = true;
806
24
}
807
808
void TextField::mouseDragged(MouseEvent& event)
809
{
810
    event.consume();
811
}
812
813
1
void TextField::widgetHidden(const Event &event A_UNUSED)
814
{
815
1
    mTextChanged = true;
816
1
    mTextChunk.deleteImage();
817
1
}
818
819
178
void TextField::setParent(Widget *widget)
820
{
821
178
    if (mWindow != nullptr)
822
178
        mWindow->addWidgetListener(this);
823
356
    Widget::setParent(widget);
824
178
}
825
826
void TextField::setWindow(Widget *const widget)
827
{
828
    if ((widget == nullptr) && (mWindow != nullptr))
829
    {
830
        mWindow->removeWidgetListener(this);
831
        mWindow = nullptr;
832
    }
833
    else
834
    {
835
        Widget2::setWindow(widget);
836
    }
837
4
}