GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/textfield.cpp Lines: 127 347 36.6 %
Date: 2018-11-12 Branches: 50 270 18.5 %

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-2018  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
91
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
91
                     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
182
    mCaretColor(&getThemeColor(ThemeColorId::CARET, 255U)),
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
546
    mTextChanged(true)
133
{
134
91
    mAllowLogic = false;
135
91
    setFocusable(true);
136
91
    addMouseListener(this);
137
91
    addKeyListener(this);
138
139
182
    setFrameSize(2);
140
182
    mForegroundColor = getThemeColor(ThemeColorId::TEXTFIELD, 255U);
141
182
    mForegroundColor2 = getThemeColor(ThemeColorId::TEXTFIELD_OUTLINE, 255U);
142
143
91
    addFocusListener(this);
144
145
91
    if (instances == 0)
146
    {
147
21
        if (theme != nullptr)
148
        {
149

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

364
        mFrameSize = mSkin->getOption("frameSize", 2);
163
    }
164
165
91
    adjustSize();
166
91
    if (!eventId.empty())
167
21
        setActionEventId(eventId);
168
169
91
    if (listener != nullptr)
170
21
        addActionListener(listener);
171
91
}
172
173
686
TextField::~TextField()
174
{
175
91
    if (mWindow != nullptr)
176
91
        mWindow->removeWidgetListener(this);
177
178
91
    if (gui != nullptr)
179
91
        gui->removeDragged(this);
180
181
91
    instances--;
182
91
    if (instances == 0)
183
    {
184
21
        if (theme != nullptr)
185
        {
186
21
            theme->unload(mSkin);
187
21
            Theme::unloadRect(skin, 0, 8);
188
        }
189
    }
190
91
    mTextChunk.deleteImage();
191
140
}
192
193
29
void TextField::updateAlpha()
194
{
195
    const float alpha = std::max(settings.guiAlpha,
196
87
        theme->getMinimumOpacity());
197
198
29
    if (alpha != mAlpha)
199
    {
200
        mAlpha = alpha;
201
        for (int a = 0; a < 9; a++)
202
        {
203
            if (skin.grid[a] != nullptr)
204
                skin.grid[a]->setAlpha(mAlpha);
205
        }
206
    }
207
29
}
208
209
29
void TextField::draw(Graphics *const graphics)
210
{
211
    BLOCK_START("TextField::draw")
212
29
    updateAlpha();
213
214
29
    Font *const font = getFont();
215
29
    if (isFocused())
216
    {
217
4
        drawCaret(graphics,
218

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

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

15
    if (sz < mCaretPosition)
806
        mCaretPosition = sz;
807
30
    mText = text;
808
15
    mTextChanged = true;
809
15
}
810
811
void TextField::mouseDragged(MouseEvent& event)
812
{
813
    event.consume();
814
}
815
816
1
void TextField::widgetHidden(const Event &event A_UNUSED)
817
{
818
1
    mTextChanged = true;
819
1
    mTextChunk.deleteImage();
820
1
}
821
822
89
void TextField::setParent(Widget *widget)
823
{
824
89
    if (mWindow != nullptr)
825
89
        mWindow->addWidgetListener(this);
826
178
    Widget::setParent(widget);
827
89
}
828
829
void TextField::setWindow(Widget *const widget)
830
{
831
    if ((widget == nullptr) && (mWindow != nullptr))
832
    {
833
        mWindow->removeWidgetListener(this);
834
        mWindow = nullptr;
835
    }
836
    else
837
    {
838
        Widget2::setWindow(widget);
839
    }
840
}
841
842
void TextField::signalEvent()
843
{
844
    distributeActionEvent();
845
2
}