GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/scrollarea.cpp Lines: 299 685 43.6 %
Date: 2018-11-12 Branches: 183 683 26.8 %

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/scrollarea.h"
67
68
#include "settings.h"
69
70
#include "gui/gui.h"
71
#include "gui/skin.h"
72
73
#include "utils/delete2.h"
74
#include "utils/stringutils.h"
75
76
#include "render/graphics.h"
77
78
#include "render/vertexes/imagecollection.h"
79
80
#include "resources/imagerect.h"
81
82
#include "resources/image/image.h"
83
84
#include "debug.h"
85
86
int ScrollArea::instances = 0;
87
float ScrollArea::mAlpha = 1.0;
88
bool ScrollArea::mShowButtons = true;
89
int ScrollArea::mMarkerSize = 0;
90
int ScrollArea::mScrollbarSize = 12;
91
1
ImageRect ScrollArea::background;
92
1
ImageRect ScrollArea::vMarker;
93
1
ImageRect ScrollArea::vMarkerHi;
94
1
ImageRect ScrollArea::vBackground;
95
1
ImageRect ScrollArea::hBackground;
96
Image *ScrollArea::buttons[4][2];
97
98
3
static std::string const buttonFiles[2] =
99
{
100
    "scrollbuttons.xml",
101
    "scrollbuttons_pressed.xml"
102

5
};
103
104
164
ScrollArea::ScrollArea(Widget2 *const widget2,
105
                       Widget *const widget,
106
                       const Opaque opaque,
107
164
                       const std::string &skin) :
108
    BasicContainer(widget2),
109
    MouseListener(),
110
    WidgetListener(),
111

164
    mVertexes(new ImageCollection),
112

164
    mVertexes2(new ImageCollection),
113
    mHPolicy(SHOW_AUTO),
114
    mVPolicy(SHOW_AUTO),
115
    mVScroll(0),
116
    mHScroll(0),
117
    mScrollbarWidth(12),
118
    mUpButtonScrollAmount(10),
119
    mDownButtonScrollAmount(10),
120
    mLeftButtonScrollAmount(10),
121
    mRightButtonScrollAmount(10),
122
    mHorizontalMarkerDragOffset(0),
123
    mVerticalMarkerDragOffset(0),
124
    mX(0),
125
    mY(0),
126
    mClickX(0),
127
    mClickY(0),
128
    mXOffset(0),
129
    mYOffset(0),
130
    mDrawWidth(0),
131
    mDrawHeight(0),
132
    mVBarVisible(false),
133
    mHBarVisible(false),
134
    mUpButtonPressed(false),
135
    mDownButtonPressed(false),
136
    mLeftButtonPressed(false),
137
    mRightButtonPressed(false),
138
    mIsVerticalMarkerDragged(false),
139
    mIsHorizontalMarkerDragged(false),
140
    mOpaque(Opaque_true),
141
984
    mHasMouse(false)
142
{
143
164
    setContent(widget);
144
164
    addMouseListener(this);
145
164
    mOpaque = opaque;
146
328
    init(skin);
147
164
}
148
149
820
ScrollArea::~ScrollArea()
150
{
151
164
    if (gui != nullptr)
152
164
        gui->removeDragged(this);
153
154
    // Garbage collection
155
164
    delete getContent();
156
157
164
    instances--;
158
164
    if (instances == 0)
159
    {
160
35
        Theme::unloadRect(background, 0, 8);
161
35
        Theme::unloadRect(vMarker, 0, 8);
162
35
        Theme::unloadRect(vMarkerHi, 0, 8);
163
35
        Theme::unloadRect(vBackground, 0, 8);
164
35
        Theme::unloadRect(hBackground, 0, 8);
165
105
        for (int i = 0; i < 2; i ++)
166
        {
167
630
            for (int f = UP; f < BUTTONS_DIR; f ++)
168
            {
169
280
                if (buttons[f][i] != nullptr)
170
280
                    buttons[f][i]->decRef();
171
            }
172
        }
173
    }
174
175
164
    delete2(mVertexes);
176
164
    delete2(mVertexes2);
177
178
164
    setContent(nullptr);
179
328
}
180
181
164
void ScrollArea::init(std::string skinName)
182
{
183
328
    setOpaque(mOpaque);
184
185
328
    setUpButtonScrollAmount(2);
186
328
    setDownButtonScrollAmount(2);
187
328
    setLeftButtonScrollAmount(2);
188
328
    setRightButtonScrollAmount(2);
189
190
164
    if (instances == 0)
191
    {
192
665
        for (int f = 0; f < 9; f ++)
193
        {
194
315
            background.grid[f] = nullptr;
195
315
            vMarker.grid[f] = nullptr;
196
315
            vMarkerHi.grid[f] = nullptr;
197
315
            vBackground.grid[f] = nullptr;
198
315
            hBackground.grid[f] = nullptr;
199
        }
200
201
        // +++ here probably need move background from static
202
35
        if (skinName.empty())
203
            skinName = "scroll_background.xml";
204
35
        if (theme != nullptr)
205
        {
206
140
            theme->loadRect(background,
207
                skinName,
208
                "scroll_background.xml",
209
                0,
210
35
                8);
211
245
            theme->loadRect(vMarker,
212
                "scroll.xml",
213
                "",
214
                0,
215
35
                8);
216
245
            theme->loadRect(vMarkerHi,
217
                "scroll_highlighted.xml",
218
                "scroll.xml",
219
                0,
220
35
                8);
221
245
            theme->loadRect(vBackground,
222
                "scroll_vbackground.xml",
223
                "",
224
                0,
225
35
                8);
226
245
            theme->loadRect(hBackground,
227
                "scroll_hbackground.xml",
228
                "",
229
                0,
230
35
                8);
231
        }
232
233
175
        for (int i = 0; i < 2; i ++)
234
        {
235
70
            Skin *skin = nullptr;
236
70
            if (theme != nullptr)
237
            {
238
280
                skin = theme->load(buttonFiles[i],
239
                    "scrollbuttons.xml",
240
                    true,
241
140
                    Theme::getThemePath());
242
            }
243
70
            if (skin != nullptr)
244
            {
245
70
                const ImageRect &rect = skin->getBorder();
246
350
                for (int f = UP; f < BUTTONS_DIR; f ++)
247
                {
248
280
                    if (rect.grid[f] != nullptr)
249
280
                        rect.grid[f]->incRef();
250
280
                    buttons[f][i] = rect.grid[f];
251
                }
252
70
                if (i == 0)
253
                {
254
140
                    mShowButtons = (skin->getOption("showbuttons", 1) == 1);
255
140
                    mMarkerSize = skin->getOption("markersize", 0);
256
140
                    mScrollbarSize = skin->getOption("scrollbarsize", 12);
257
                }
258
            }
259
            else
260
            {
261
                for (int f = UP; f < BUTTONS_DIR; f ++)
262
                    buttons[f][i] = nullptr;
263
            }
264
70
            if (theme != nullptr)
265
70
                theme->unload(skin);
266
        }
267
    }
268
164
    mScrollbarWidth = mScrollbarSize;
269
164
    instances++;
270
164
}
271
272
1
void ScrollArea::logic()
273
{
274
    BLOCK_START("ScrollArea::logic")
275
2
    if (!isVisible())
276
    {
277
        BLOCK_END("ScrollArea::logic")
278
        return;
279
    }
280
281
1
    checkPolicies();
282
283
2
    setVerticalScrollAmount(mVScroll);
284
2
    setHorizontalScrollAmount(mHScroll);
285
286
1
    Widget *const content = getContent();
287
1
    if (content != nullptr)
288
    {
289
1
        unsigned int frameSize = content->getFrameSize();
290
1
        content->setPosition(-mHScroll + frameSize, -mVScroll + frameSize);
291
1
        content->logic();
292
293
        // When no scrollbar in a certain direction,
294
        // adapt content size to match the content dimension exactly.
295
1
        frameSize = 2 * content->getFrameSize();
296
1
        if (mHPolicy == ScrollArea::SHOW_NEVER)
297
        {
298
3
            content->setWidth((mVBarVisible ? (mDimension.width
299
3
                - mScrollbarWidth) : mDimension.width) - frameSize);
300
        }
301
1
        if (mVPolicy == ScrollArea::SHOW_NEVER)
302
        {
303
            content->setHeight((mHBarVisible ? (mDimension.height
304
                - mScrollbarWidth) : mDimension.height) - frameSize);
305
        }
306
    }
307
308
1
    if (mUpButtonPressed)
309
        setVerticalScrollAmount(mVScroll - mUpButtonScrollAmount);
310
1
    else if (mDownButtonPressed)
311
        setVerticalScrollAmount(mVScroll + mDownButtonScrollAmount);
312
1
    else if (mLeftButtonPressed)
313
        setHorizontalScrollAmount(mHScroll - mLeftButtonScrollAmount);
314
1
    else if (mRightButtonPressed)
315
        setHorizontalScrollAmount(mHScroll + mRightButtonScrollAmount);
316
    BLOCK_END("ScrollArea::logic")
317
}
318
319
17
void ScrollArea::updateAlpha()
320
{
321
    const float alpha = std::max(settings.guiAlpha,
322
51
        theme->getMinimumOpacity());
323
324
17
    if (alpha != mAlpha)
325
    {
326
        mAlpha = alpha;
327
        for (int a = 0; a < 9; a++)
328
        {
329
            if (background.grid[a] != nullptr)
330
                background.grid[a]->setAlpha(mAlpha);
331
            if (hBackground.grid[a] != nullptr)
332
                hBackground.grid[a]->setAlpha(mAlpha);
333
            if (vBackground.grid[a] != nullptr)
334
                vBackground.grid[a]->setAlpha(mAlpha);
335
            if (vMarker.grid[a] != nullptr)
336
                vMarker.grid[a]->setAlpha(mAlpha);
337
            if (vMarkerHi.grid[a] != nullptr)
338
                vMarkerHi.grid[a]->setAlpha(mAlpha);
339
        }
340
    }
341
17
}
342
343
17
void ScrollArea::draw(Graphics *const graphics)
344
{
345
    BLOCK_START("ScrollArea::draw")
346

17
    if (mVBarVisible || mHBarVisible)
347
    {
348
11
        if (mOpaque == Opaque_false)
349
            updateCalcFlag(graphics);
350
        // need add caching or remove calc calls.
351
//        if (mRedraw)
352
        {
353
11
            mVertexes->clear();
354
11
            if (mVBarVisible)
355
            {
356
11
                if (mShowButtons)
357
                {
358
                    calcButton(graphics, UP);
359
                    calcButton(graphics, DOWN);
360
                }
361
11
                calcVBar(graphics);
362
11
                calcVMarker(graphics);
363
            }
364
365
11
            if (mHBarVisible)
366
            {
367
                if (mShowButtons)
368
                {
369
                    calcButton(graphics, LEFT);
370
                    calcButton(graphics, RIGHT);
371
                }
372
                calcHBar(graphics);
373
                calcHMarker(graphics);
374
            }
375
11
            graphics->finalize(mVertexes);
376
        }
377
11
        graphics->drawTileCollection(mVertexes);
378
    }
379
380
17
    updateAlpha();
381
382
17
    if (mRedraw)
383
    {
384
17
        const bool redraw = graphics->getRedraw();
385
17
        graphics->setRedraw(true);
386
17
        drawChildren(graphics);
387
17
        graphics->setRedraw(redraw);
388
    }
389
    else
390
    {
391
        drawChildren(graphics);
392
    }
393
17
    mRedraw = false;
394
    BLOCK_END("ScrollArea::draw")
395
17
}
396
397
void ScrollArea::safeDraw(Graphics *const graphics)
398
{
399
    BLOCK_START("ScrollArea::draw")
400
    if (mVBarVisible)
401
    {
402
        if (mShowButtons)
403
        {
404
            drawButton(graphics, UP);
405
            drawButton(graphics, DOWN);
406
        }
407
        drawVBar(graphics);
408
        drawVMarker(graphics);
409
    }
410
411
    if (mHBarVisible)
412
    {
413
        if (mShowButtons)
414
        {
415
            drawButton(graphics, LEFT);
416
            drawButton(graphics, RIGHT);
417
        }
418
        drawHBar(graphics);
419
        drawHMarker(graphics);
420
    }
421
422
    updateAlpha();
423
424
    safeDrawChildren(graphics);
425
    mRedraw = false;
426
    BLOCK_END("ScrollArea::draw")
427
}
428
429
16
void ScrollArea::updateCalcFlag(const Graphics *const graphics)
430
{
431
16
    if (!mRedraw)
432
    {
433
        // because we don't know where parent windows was moved,
434
        // need recalc vertexes
435
        const ClipRect &rect = graphics->getTopClip();
436
        if (rect.xOffset != mXOffset || rect.yOffset != mYOffset)
437
        {
438
            mRedraw = true;
439
            mXOffset = rect.xOffset;
440
            mYOffset = rect.yOffset;
441
        }
442
        else if (rect.width != mDrawWidth || rect.height != mDrawHeight)
443
        {
444
            mRedraw = true;
445
            mDrawWidth = rect.width;
446
            mDrawHeight = rect.height;
447
        }
448
        else if (graphics->getRedraw())
449
        {
450
            mRedraw = true;
451
        }
452
    }
453
16
}
454
455
16
void ScrollArea::drawFrame(Graphics *const graphics)
456
{
457
    BLOCK_START("ScrollArea::drawFrame")
458
16
    if (mOpaque == Opaque_true)
459
    {
460
16
        const int bs = mFrameSize * 2;
461
16
        const int w = mDimension.width + bs;
462
16
        const int h = mDimension.height + bs;
463
464
16
        updateCalcFlag(graphics);
465
466
16
        if (mRedraw)
467
        {
468
16
            mVertexes2->clear();
469
16
            graphics->calcWindow(mVertexes2,
470
                0, 0,
471
                w, h,
472
32
                background);
473
16
            graphics->finalize(mVertexes2);
474
        }
475
16
        graphics->drawTileCollection(mVertexes2);
476
    }
477
    BLOCK_END("ScrollArea::drawFrame")
478
16
}
479
480
void ScrollArea::safeDrawFrame(Graphics *const graphics)
481
{
482
    BLOCK_START("ScrollArea::drawFrame")
483
    if (mOpaque == Opaque_true)
484
    {
485
        const int bs = mFrameSize * 2;
486
        const int w = mDimension.width + bs;
487
        const int h = mDimension.height + bs;
488
489
        updateCalcFlag(graphics);
490
        graphics->drawImageRect(0, 0,
491
            w, h,
492
            background);
493
    }
494
    BLOCK_END("ScrollArea::drawFrame")
495
}
496
497
void ScrollArea::setOpaque(Opaque opaque)
498
{
499
    mOpaque = opaque;
500

328
    setFrameSize(mOpaque == Opaque_true ? 2 : 0);
501
}
502
503
Image *ScrollArea::getImageByState(Rect &dim, const BUTTON_DIR dir)
504
{
505
    int state = 0;
506
507
    switch (dir)
508
    {
509
        case UP:
510
            state = mUpButtonPressed ? 1 : 0;
511
            dim = getUpButtonDimension();
512
            break;
513
        case DOWN:
514
            state = mDownButtonPressed ? 1 : 0;
515
            dim = getDownButtonDimension();
516
            break;
517
        case LEFT:
518
            state = mLeftButtonPressed ? 1 : 0;
519
            dim = getLeftButtonDimension();
520
            break;
521
        case RIGHT:
522
            state = mRightButtonPressed ? 1 : 0;
523
            dim = getRightButtonDimension();
524
            break;
525
        case BUTTONS_DIR:
526
        default:
527
            logger->log("ScrollArea::drawButton unknown dir: "
528
                        + toString(CAST_U32(dir)));
529
            return nullptr;
530
    }
531
    return buttons[CAST_SIZE(dir)][state];
532
}
533
534
void ScrollArea::drawButton(Graphics *const graphics,
535
                            const BUTTON_DIR dir)
536
{
537
    Rect dim;
538
    const Image *const image = getImageByState(dim, dir);
539
540
    if (image != nullptr)
541
        graphics->drawImage(image, dim.x, dim.y);
542
}
543
544
void ScrollArea::calcButton(Graphics *const graphics,
545
                            const BUTTON_DIR dir)
546
{
547
    Rect dim;
548
    const Image *const image = getImageByState(dim, dir);
549
550
    if (image != nullptr)
551
    {
552
        static_cast<Graphics*>(graphics)->calcTileCollection(
553
            mVertexes, image, dim.x, dim.y);
554
    }
555
}
556
557
void ScrollArea::drawVBar(Graphics *const graphics) const
558
{
559
    const Rect &dim = getVerticalBarDimension();
560
561
    if (vBackground.grid[4] != nullptr)
562
    {
563
        graphics->drawPattern(vBackground.grid[4],
564
            dim.x, dim.y, dim.width, dim.height);
565
    }
566
    if (vBackground.grid[1] != nullptr)
567
    {
568
        graphics->drawPattern(vBackground.grid[1],
569
            dim.x, dim.y,
570
            dim.width, vBackground.grid[1]->getHeight());
571
    }
572
    if (vBackground.grid[7] != nullptr)
573
    {
574
        graphics->drawPattern(vBackground.grid[7],
575
            dim.x, dim.height - vBackground.grid[7]->getHeight() + dim.y,
576
            dim.width, vBackground.grid[7]->getHeight());
577
    }
578
}
579
580
11
void ScrollArea::calcVBar(const Graphics *const graphics)
581
{
582
22
    const Rect &dim = getVerticalBarDimension();
583
584
11
    if (vBackground.grid[4] != nullptr)
585
    {
586
11
        graphics->calcPattern(mVertexes,
587
            vBackground.grid[4],
588
11
            dim.x, dim.y,
589
22
            dim.width, dim.height);
590
    }
591
11
    if (vBackground.grid[1] != nullptr)
592
    {
593
22
        graphics->calcPattern(mVertexes,
594
            vBackground.grid[1],
595
11
            dim.x, dim.y,
596
33
            dim.width, vBackground.grid[1]->getHeight());
597
    }
598
11
    if (vBackground.grid[7] != nullptr)
599
    {
600
33
        graphics->calcPattern(mVertexes,
601
            vBackground.grid[7],
602
22
            dim.x, dim.height - vBackground.grid[7]->getHeight() + dim.y,
603
33
            dim.width, vBackground.grid[7]->getHeight());
604
    }
605
11
}
606
607
void ScrollArea::drawHBar(Graphics *const graphics) const
608
{
609
    const Rect &dim = getHorizontalBarDimension();
610
611
    if (hBackground.grid[4] != nullptr)
612
    {
613
        graphics->drawPattern(hBackground.grid[4],
614
            dim.x, dim.y,
615
            dim.width, dim.height);
616
    }
617
618
    if (hBackground.grid[3] != nullptr)
619
    {
620
        graphics->drawPattern(hBackground.grid[3],
621
            dim.x, dim.y,
622
            hBackground.grid[3]->getWidth(), dim.height);
623
    }
624
625
    if (hBackground.grid[5] != nullptr)
626
    {
627
        graphics->drawPattern(hBackground.grid[5],
628
            dim.x + dim.width - hBackground.grid[5]->getWidth(),
629
            dim.y,
630
            hBackground.grid[5]->getWidth(),
631
            dim.height);
632
    }
633
}
634
635
void ScrollArea::calcHBar(const Graphics *const graphics)
636
{
637
    const Rect &dim = getHorizontalBarDimension();
638
639
    if (hBackground.grid[4] != nullptr)
640
    {
641
        graphics->calcPattern(mVertexes,
642
            hBackground.grid[4],
643
            dim.x, dim.y,
644
            dim.width, dim.height);
645
    }
646
647
    if (hBackground.grid[3] != nullptr)
648
    {
649
        graphics->calcPattern(mVertexes,
650
            hBackground.grid[3],
651
            dim.x, dim.y,
652
            hBackground.grid[3]->getWidth(), dim.height);
653
    }
654
655
    if (hBackground.grid[5] != nullptr)
656
    {
657
        graphics->calcPattern(mVertexes,
658
            hBackground.grid[5],
659
            dim.x + dim.width - hBackground.grid[5]->getWidth(),
660
            dim.y,
661
            hBackground.grid[5]->getWidth(),
662
            dim.height);
663
    }
664
}
665
666
void ScrollArea::drawVMarker(Graphics *const graphics)
667
{
668
    const Rect &dim = getVerticalMarkerDimension();
669
670
    if ((mHasMouse) && (mX > (mDimension.width - mScrollbarWidth)))
671
    {
672
        graphics->drawImageRect(dim.x, dim.y,
673
            dim.width, dim.height,
674
            vMarkerHi);
675
    }
676
    else
677
    {
678
        graphics->drawImageRect(dim.x, dim.y,
679
            dim.width, dim.height,
680
            vMarker);
681
    }
682
}
683
684
11
void ScrollArea::calcVMarker(Graphics *const graphics)
685
{
686
22
    const Rect &dim = getVerticalMarkerDimension();
687
688

11
    if ((mHasMouse) && (mX > (mDimension.width - mScrollbarWidth)))
689
    {
690
        graphics->calcWindow(mVertexes,
691
            dim.x, dim.y,
692
            dim.width, dim.height,
693
            vMarkerHi);
694
    }
695
    else
696
    {
697
11
        graphics->calcWindow(mVertexes,
698
11
            dim.x, dim.y,
699
11
            dim.width, dim.height,
700
22
            vMarker);
701
    }
702
11
}
703
704
void ScrollArea::drawHMarker(Graphics *const graphics)
705
{
706
    const Rect dim = getHorizontalMarkerDimension();
707
708
    if ((mHasMouse) && (mY > (mDimension.height - mScrollbarWidth)))
709
    {
710
        graphics->drawImageRect(dim.x, dim.y,
711
            dim.width, dim.height,
712
            vMarkerHi);
713
    }
714
    else
715
    {
716
        graphics->drawImageRect(
717
            dim.x, dim.y,
718
            dim.width, dim.height,
719
            vMarker);
720
    }
721
}
722
723
void ScrollArea::calcHMarker(Graphics *const graphics)
724
{
725
    const Rect dim = getHorizontalMarkerDimension();
726
727
    if ((mHasMouse) && (mY > (mDimension.height - mScrollbarWidth)))
728
    {
729
        graphics->calcWindow(mVertexes,
730
            dim.x, dim.y,
731
            dim.width, dim.height,
732
            vMarkerHi);
733
    }
734
    else
735
    {
736
        graphics->calcWindow(mVertexes,
737
            dim.x, dim.y,
738
            dim.width, dim.height,
739
            vMarker);
740
    }
741
}
742
743
void ScrollArea::mouseMoved(MouseEvent& event)
744
{
745
    mX = event.getX();
746
    mY = event.getY();
747
}
748
749
void ScrollArea::mouseEntered(MouseEvent& event A_UNUSED)
750
{
751
    mHasMouse = true;
752
}
753
754
void ScrollArea::mouseExited(MouseEvent& event A_UNUSED)
755
{
756
    mHasMouse = false;
757
}
758
759
void ScrollArea::widgetResized(const Event &event A_UNUSED)
760
{
761
    mRedraw = true;
762
    const unsigned int frameSize = 2 * mFrameSize;
763
    Widget *const content = getContent();
764
    if (content != nullptr)
765
    {
766
        content->setSize(mDimension.width - frameSize,
767
            mDimension.height - frameSize);
768
    }
769
}
770
771
void ScrollArea::widgetMoved(const Event& event A_UNUSED)
772
{
773
    mRedraw = true;
774
}
775
776
void ScrollArea::mousePressed(MouseEvent& event)
777
{
778
    const int x = event.getX();
779
    const int y = event.getY();
780
781
    if (getUpButtonDimension().isPointInRect(x, y))
782
    {
783
        setVerticalScrollAmount(mVScroll
784
                                - mUpButtonScrollAmount);
785
        mUpButtonPressed = true;
786
        event.consume();
787
    }
788
    else if (getDownButtonDimension().isPointInRect(x, y))
789
    {
790
        setVerticalScrollAmount(mVScroll
791
                                + mDownButtonScrollAmount);
792
        mDownButtonPressed = true;
793
        event.consume();
794
    }
795
    else if (getLeftButtonDimension().isPointInRect(x, y))
796
    {
797
        setHorizontalScrollAmount(mHScroll
798
                                  - mLeftButtonScrollAmount);
799
        mLeftButtonPressed = true;
800
        event.consume();
801
    }
802
    else if (getRightButtonDimension().isPointInRect(x, y))
803
    {
804
        setHorizontalScrollAmount(mHScroll
805
                                  + mRightButtonScrollAmount);
806
        mRightButtonPressed = true;
807
        event.consume();
808
    }
809
    else if (getVerticalMarkerDimension().isPointInRect(x, y))
810
    {
811
        mIsHorizontalMarkerDragged = false;
812
        mIsVerticalMarkerDragged = true;
813
814
        mVerticalMarkerDragOffset = y - getVerticalMarkerDimension().y;
815
        event.consume();
816
    }
817
    else if (getVerticalBarDimension().isPointInRect(x, y))
818
    {
819
        if (y < getVerticalMarkerDimension().y)
820
        {
821
            setVerticalScrollAmount(mVScroll
822
                - CAST_S32(getChildrenArea().height * 0.1));
823
        }
824
        else
825
        {
826
            setVerticalScrollAmount(mVScroll
827
                + CAST_S32(getChildrenArea().height * 0.1));
828
        }
829
        event.consume();
830
    }
831
    else if (getHorizontalMarkerDimension().isPointInRect(x, y))
832
    {
833
        mIsHorizontalMarkerDragged = true;
834
        mIsVerticalMarkerDragged = false;
835
        mHorizontalMarkerDragOffset = x - getHorizontalMarkerDimension().x;
836
        event.consume();
837
    }
838
    else if (getHorizontalBarDimension().isPointInRect(x, y))
839
    {
840
        if (x < getHorizontalMarkerDimension().x)
841
        {
842
            setHorizontalScrollAmount(mHScroll
843
                - CAST_S32(getChildrenArea().width * 0.1));
844
        }
845
        else
846
        {
847
            setHorizontalScrollAmount(mHScroll
848
                + CAST_S32(getChildrenArea().width * 0.1));
849
        }
850
        event.consume();
851
    }
852
853
    if (event.getButton() == MouseButton::LEFT &&
854
        !event.isConsumed())
855
    {
856
        mClickX = event.getX();
857
        mClickY = event.getY();
858
    }
859
}
860
861
void ScrollArea::mouseReleased(MouseEvent& event)
862
{
863
    if (event.getButton() == MouseButton::LEFT &&
864
        mClickX != 0 &&
865
        mClickY != 0)
866
    {
867
        if (!event.isConsumed())
868
        {
869
#ifdef ANDROID
870
            int dx = mClickX - event.getX();
871
            int dy = mClickY - event.getY();
872
#else  // ANDROID
873
874
            int dx = event.getX() - mClickX;
875
            int dy = event.getY() - mClickY;
876
#endif  // ANDROID
877
878
            if ((dx < 20 && dx > 0) || (dx > -20 && dx < 0))
879
                dx = 0;
880
881
            if ((dy < 20 && dy > 0) || (dy > -20 && dy < 0))
882
                dy = 0;
883
884
            if (abs(dx) > abs(dy))
885
            {
886
                int s = mHScroll + dx;
887
                if (s < 0)
888
                {
889
                    s = 0;
890
                }
891
                else
892
                {
893
                    const int maxH = getHorizontalMaxScroll();
894
                    if (s > maxH)
895
                        s = maxH;
896
                }
897
898
                setHorizontalScrollAmount(s);
899
            }
900
            else if (dy != 0)
901
            {
902
                int s = mVScroll + dy;
903
                if (s < 0)
904
                {
905
                    s = 0;
906
                }
907
                else
908
                {
909
                    const int maxV = getVerticalMaxScroll();
910
                    if (s > maxV)
911
                        s = maxV;
912
                }
913
914
                setVerticalScrollAmount(s);
915
            }
916
            mClickX = 0;
917
            mClickY = 0;
918
            if (mMouseConsume && ((dx != 0) || (dy != 0)))
919
                event.consume();
920
        }
921
    }
922
    mUpButtonPressed = false;
923
    mDownButtonPressed = false;
924
    mLeftButtonPressed = false;
925
    mRightButtonPressed = false;
926
    mIsHorizontalMarkerDragged = false;
927
    mIsVerticalMarkerDragged = false;
928
    if (mMouseConsume)
929
        event.consume();
930
    mRedraw = true;
931
}
932
933
void ScrollArea::mouseDragged(MouseEvent &event)
934
{
935
    if (mIsVerticalMarkerDragged)
936
    {
937
        const Rect barDim = getVerticalBarDimension();
938
939
        const int pos = event.getY() - barDim.y
940
              - mVerticalMarkerDragOffset;
941
        const int length = getVerticalMarkerDimension().height;
942
943
        if ((barDim.height - length) > 0)
944
        {
945
            setVerticalScrollAmount((getVerticalMaxScroll() * pos)
946
                                      / (barDim.height - length));
947
        }
948
        else
949
        {
950
            setVerticalScrollAmount(0);
951
        }
952
    }
953
954
    if (mIsHorizontalMarkerDragged)
955
    {
956
        const Rect barDim = getHorizontalBarDimension();
957
958
        const int pos = event.getX() - barDim.x
959
            - mHorizontalMarkerDragOffset;
960
        const int length = getHorizontalMarkerDimension().width;
961
962
        if ((barDim.width - length) > 0)
963
        {
964
            setHorizontalScrollAmount((getHorizontalMaxScroll() * pos)
965
                                      / (barDim.width - length));
966
        }
967
        else
968
        {
969
            setHorizontalScrollAmount(0);
970
        }
971
    }
972
973
    event.consume();
974
    mRedraw = true;
975
}
976
977
11
Rect ScrollArea::getVerticalBarDimension() const
978
{
979
11
    if (!mVBarVisible)
980
        return Rect(0, 0, 0, 0);
981
982
11
    const int height = mShowButtons ? mScrollbarWidth : 0;
983
11
    if (mHBarVisible)
984
    {
985
        return Rect(mDimension.width - mScrollbarWidth,
986
            height,
987
            mScrollbarWidth,
988
            mDimension.height - 2 * height - mScrollbarWidth);
989
    }
990
991
11
    return Rect(mDimension.width - mScrollbarWidth,
992
        height,
993
11
        mScrollbarWidth,
994
22
        mDimension.height - 2 * height);
995
}
996
997
Rect ScrollArea::getHorizontalBarDimension() const
998
{
999
    if (!mHBarVisible)
1000
        return Rect(0, 0, 0, 0);
1001
1002
    const int width = mShowButtons ? mScrollbarWidth : 0;
1003
    if (mVBarVisible)
1004
    {
1005
        return Rect(width,
1006
            mDimension.height - mScrollbarWidth,
1007
            mDimension.width - 2 * width - mScrollbarWidth,
1008
            mScrollbarWidth);
1009
    }
1010
1011
    return Rect(width,
1012
                      mDimension.height - mScrollbarWidth,
1013
                      mDimension.width - 2 * width,
1014
                      mScrollbarWidth);
1015
}
1016
1017
11
Rect ScrollArea::getVerticalMarkerDimension()
1018
{
1019
11
    if (!mVBarVisible)
1020
        return Rect(0, 0, 0, 0);
1021
1022
    int length;
1023
    int pos;
1024
    int height;
1025
    const int h2 = mShowButtons
1026
11
        ? mScrollbarWidth : mMarkerSize / 2;
1027
    const Widget *content;
1028
22
    if (!mWidgets.empty())
1029
22
        content = *mWidgets.begin();
1030
    else
1031
        content = nullptr;
1032
1033
11
    if (mHBarVisible)
1034
        height = mDimension.height - 2 * h2 - mScrollbarWidth;
1035
    else
1036
11
        height = mDimension.height - 2 * h2;
1037
1038
11
    const int maxV = getVerticalMaxScroll();
1039

11
    if ((mMarkerSize != 0) && (maxV != 0))
1040
    {
1041
        pos = (mVScroll * height / maxV - mMarkerSize / 2);
1042
        length = mMarkerSize;
1043
    }
1044
    else
1045
    {
1046
11
        if (content != nullptr)
1047
        {
1048
11
            const int h3 = content->getHeight();
1049
11
            if (h3 != 0)
1050
10
                length = (height * getChildrenArea().height) / h3;
1051
            else
1052
                length = height;
1053
        }
1054
        else
1055
        {
1056
            length = height;
1057
        }
1058
1059
11
        if (length < mScrollbarWidth)
1060
            length = mScrollbarWidth;
1061
1062
11
        if (length > height)
1063
10
            length = height;
1064
1065
11
        if (maxV != 0)
1066
            pos = ((height - length) * mVScroll) / maxV;
1067
        else
1068
            pos = 0;
1069
    }
1070
1071
11
    return Rect(mDimension.width - mScrollbarWidth, h2 + pos,
1072
22
        mScrollbarWidth, length);
1073
}
1074
1075
Rect ScrollArea::getHorizontalMarkerDimension()
1076
{
1077
    if (!mHBarVisible)
1078
        return Rect(0, 0, 0, 0);
1079
1080
    int length;
1081
    int pos;
1082
    int width;
1083
    const int w2 = mShowButtons
1084
        ? mScrollbarWidth : mMarkerSize / 2;
1085
    const Widget *content;
1086
    if (!mWidgets.empty())
1087
        content = *mWidgets.begin();
1088
    else
1089
        content = nullptr;
1090
1091
    if (mVBarVisible)
1092
        width = mDimension.width - 2 * w2 - mScrollbarWidth;
1093
    else
1094
        width = mDimension.width - w2 - mScrollbarWidth;
1095
1096
    const int maxH = getHorizontalMaxScroll();
1097
    if (mMarkerSize != 0 && maxH != 0)
1098
    {
1099
        pos = (mHScroll * width / maxH - mMarkerSize / 2);
1100
        length = mMarkerSize;
1101
    }
1102
    else
1103
    {
1104
        if (content != nullptr)
1105
        {
1106
            const int w3 = content->getWidth();
1107
            if (w3 != 0)
1108
                length = (width * getChildrenArea().width) / w3;
1109
            else
1110
                length = width;
1111
        }
1112
        else
1113
        {
1114
            length = width;
1115
        }
1116
1117
        if (length < mScrollbarWidth)
1118
            length = mScrollbarWidth;
1119
1120
        if (length > width)
1121
            length = width;
1122
1123
        if (maxH != 0)
1124
        {
1125
            pos = ((width - length) * mHScroll) / maxH;
1126
        }
1127
        else
1128
        {
1129
            pos = 0;
1130
        }
1131
    }
1132
1133
    return Rect(w2 + pos, mDimension.height - mScrollbarWidth,
1134
        length, mScrollbarWidth);
1135
}
1136
1137
Rect ScrollArea::getUpButtonDimension() const
1138
{
1139
    if (!mVBarVisible || !mShowButtons)
1140
        return Rect(0, 0, 0, 0);
1141
1142
    return Rect(mDimension.width - mScrollbarWidth, 0,
1143
        mScrollbarWidth, mScrollbarWidth);
1144
}
1145
1146
Rect ScrollArea::getDownButtonDimension() const
1147
{
1148
    if (!mVBarVisible || !mShowButtons)
1149
        return Rect(0, 0, 0, 0);
1150
1151
    if (mHBarVisible)
1152
    {
1153
        return Rect(mDimension.width - mScrollbarWidth,
1154
            mDimension.height - mScrollbarWidth * 2,
1155
            mScrollbarWidth,
1156
            mScrollbarWidth);
1157
    }
1158
1159
    return Rect(mDimension.width - mScrollbarWidth,
1160
        mDimension.height - mScrollbarWidth,
1161
        mScrollbarWidth,
1162
        mScrollbarWidth);
1163
}
1164
1165
Rect ScrollArea::getLeftButtonDimension() const
1166
{
1167
    if (!mHBarVisible || !mShowButtons)
1168
        return Rect(0, 0, 0, 0);
1169
1170
    return Rect(0, mDimension.height - mScrollbarWidth,
1171
        mScrollbarWidth, mScrollbarWidth);
1172
}
1173
1174
Rect ScrollArea::getRightButtonDimension() const
1175
{
1176
    if (!mHBarVisible || !mShowButtons)
1177
        return Rect(0, 0, 0, 0);
1178
1179
    if (mVBarVisible)
1180
    {
1181
        return Rect(mDimension.width - mScrollbarWidth*2,
1182
            mDimension.height - mScrollbarWidth,
1183
            mScrollbarWidth,
1184
            mScrollbarWidth);
1185
    }
1186
1187
    return Rect(mDimension.width - mScrollbarWidth,
1188
        mDimension.height - mScrollbarWidth,
1189
        mScrollbarWidth,
1190
        mScrollbarWidth);
1191
}
1192
1193
165
void ScrollArea::setContent(Widget* widget)
1194
{
1195
165
    if (widget != nullptr)
1196
    {
1197
165
        clear();
1198
165
        add(widget);
1199
165
        widget->setPosition(0, 0);
1200
    }
1201
    else
1202
    {
1203
164
        clear();
1204
    }
1205
1206
329
    checkPolicies();
1207
165
}
1208
1209
2
Widget* ScrollArea::getContent()
1210
{
1211




4528
    if (!mWidgets.empty())
1212
4136
        return *mWidgets.begin();
1213
1214
    return nullptr;
1215
}
1216
1217
56
void ScrollArea::setHorizontalScrollPolicy(const ScrollPolicy hPolicy)
1218
{
1219
56
    mHPolicy = hPolicy;
1220
56
    checkPolicies();
1221
56
}
1222
1223
26
void ScrollArea::setVerticalScrollPolicy(const ScrollPolicy vPolicy)
1224
{
1225
26
    mVPolicy = vPolicy;
1226
26
    checkPolicies();
1227
26
}
1228
1229
4
void ScrollArea::setScrollPolicy(const ScrollPolicy hPolicy,
1230
                                 const ScrollPolicy vPolicy)
1231
{
1232
4
    mHPolicy = hPolicy;
1233
4
    mVPolicy = vPolicy;
1234
4
    checkPolicies();
1235
4
}
1236
1237
3
void ScrollArea::setVerticalScrollAmount(const int vScroll)
1238
{
1239
207
    const int max = getVerticalMaxScroll();
1240
1241
207
    mVScroll = vScroll;
1242
1243







207
    if (vScroll > max)
1244
5
        mVScroll = max;
1245
1246







207
    if (vScroll < 0)
1247
        mVScroll = 0;
1248
3
}
1249
1250
void ScrollArea::setHorizontalScrollAmount(int hScroll)
1251
{
1252
204
    const int max = getHorizontalMaxScroll();
1253
1254
204
    mHScroll = hScroll;
1255
1256






204
    if (hScroll > max)
1257
        mHScroll = max;
1258






204
    else if (hScroll < 0)
1259
        mHScroll = 0;
1260
}
1261
1262
1
void ScrollArea::setScrollAmount(const int hScroll, const int vScroll)
1263
{
1264
1
    setHorizontalScrollAmount(hScroll);
1265
1
    setVerticalScrollAmount(vScroll);
1266
1
}
1267
1268
204
int ScrollArea::getHorizontalMaxScroll()
1269
{
1270
204
    checkPolicies();
1271
1272
204
    const Widget *const content = getContent();
1273
204
    if (content == nullptr)
1274
        return 0;
1275
1276
816
    const int value = content->getWidth() - getChildrenArea().width +
1277
408
        2 * content->getFrameSize();
1278
1279
204
    if (value < 0)
1280
        return 0;
1281
1282
1
    return value;
1283
}
1284
1285
219
int ScrollArea::getVerticalMaxScroll()
1286
{
1287
219
    checkPolicies();
1288
1289
219
    const Widget *const content = getContent();
1290
219
    if (content == nullptr)
1291
        return 0;
1292
1293
    int value;
1294
1295
657
    value = content->getHeight() - getChildrenArea().height +
1296
219
        2 * content->getFrameSize();
1297
1298
219
    if (value < 0)
1299
        return 0;
1300
1301
201
    return value;
1302
}
1303
1304
void ScrollArea::setScrollbarWidth(const int width)
1305
{
1306
    if (width > 0)
1307
        mScrollbarWidth = width;
1308
}
1309
1310
202
void ScrollArea::showWidgetPart(Widget *const widget, const Rect &area)
1311
{
1312
202
    const Widget *const content = getContent();
1313
202
    if (widget != content || (content == nullptr))
1314
        return;
1315
1316
202
    BasicContainer::showWidgetPart(widget, area);
1317
1318
404
    setHorizontalScrollAmount(content->getFrameSize()
1319
404
        - content->getX());
1320
404
    setVerticalScrollAmount(content->getFrameSize()
1321
202
        - content->getY());
1322
}
1323
1324
219
Rect ScrollArea::getChildrenArea()
1325
{
1326
    const Rect area = Rect(0, 0,
1327
1793
        mVBarVisible ? (getWidth() - mScrollbarWidth) : getWidth(),
1328












3912
        mHBarVisible ? (getHeight() - mScrollbarWidth) : getHeight());
1329
1330












652
    if (area.width < 0 || area.height < 0)
1331
        return Rect();
1332
1333
    return area;
1334
}
1335
1336
Widget *ScrollArea::getWidgetAt(int x, int y)
1337
{
1338
    if (getChildrenArea().isPointInRect(x, y))
1339
        return getContent();
1340
1341
    return nullptr;
1342
}
1343
1344
315
void ScrollArea::setWidth(int width)
1345
{
1346
315
    Widget::setWidth(width);
1347
315
    checkPolicies();
1348
315
}
1349
1350
317
void ScrollArea::setHeight(int height)
1351
{
1352
317
    Widget::setHeight(height);
1353
317
    checkPolicies();
1354
317
}
1355
1356
1
void ScrollArea::setDimension(const Rect& dimension)
1357
{
1358
1
    Widget::setDimension(dimension);
1359
1
    checkPolicies();
1360
1
}
1361
1362
void ScrollArea::mouseWheelMovedUp(MouseEvent& event)
1363
{
1364
    if (event.isConsumed())
1365
        return;
1366
1367
    setVerticalScrollAmount(mVScroll
1368
        - getChildrenArea().height / 8);
1369
1370
    event.consume();
1371
}
1372
1373
void ScrollArea::mouseWheelMovedDown(MouseEvent& event)
1374
{
1375
    if (event.isConsumed())
1376
        return;
1377
1378
    setVerticalScrollAmount(mVScroll
1379
        + getChildrenArea().height / 8);
1380
1381
    event.consume();
1382
}
1383
1384
1472
void ScrollArea::checkPolicies()
1385
{
1386
2944
    const int w = getWidth();
1387
2944
    const int h = getHeight();
1388
1389
1472
    mHBarVisible = false;
1390
1472
    mVBarVisible = false;
1391
1392
1472
    const Widget *const content = getContent();
1393
1472
    if (content == nullptr)
1394
    {
1395
164
        mHBarVisible = (mHPolicy == SHOW_ALWAYS);
1396
164
        mVBarVisible = (mVPolicy == SHOW_ALWAYS);
1397
164
        return;
1398
    }
1399
1400
1308
    if (mHPolicy == SHOW_AUTO &&
1401
        mVPolicy == SHOW_AUTO)
1402
    {
1403
1140
        if (content->getWidth() <= w
1404
            && content->getHeight() <= h)
1405
        {
1406
            mHBarVisible = false;
1407
            mVBarVisible = false;
1408
        }
1409
1410
1140
        if (content->getWidth() > w)
1411
        {
1412
322
            mHBarVisible = true;
1413
        }
1414
1415
2280
        if ((content->getHeight() > h)
1416

1142
            || (mHBarVisible && content->getHeight()
1417
2
            > h - mScrollbarWidth))
1418
        {
1419
964
            mVBarVisible = true;
1420
        }
1421
1422

1140
        if (mVBarVisible && content->getWidth() > w - mScrollbarWidth)
1423
962
            mHBarVisible = true;
1424
1425
        return;
1426
    }
1427
1428
168
    switch (mHPolicy)
1429
    {
1430
        case SHOW_NEVER:
1431
            mHBarVisible = false;
1432
            break;
1433
1434
        case SHOW_ALWAYS:
1435
            mHBarVisible = true;
1436
            break;
1437
1438
        case SHOW_AUTO:
1439
8
            if (mVPolicy == SHOW_NEVER)
1440
            {
1441
2
                mHBarVisible = (content->getWidth() > w);
1442
            }
1443
            else  // (mVPolicy == SHOW_ALWAYS)
1444
            {
1445
12
                mHBarVisible = (content->getWidth()
1446
6
                    > w - mScrollbarWidth);
1447
            }
1448
            break;
1449
1450
        default:
1451
            break;
1452
    }
1453
1454
168
    switch (mVPolicy)
1455
    {
1456
        case SHOW_NEVER:
1457
            mVBarVisible = false;
1458
            break;
1459
1460
        case SHOW_ALWAYS:
1461
17
            mVBarVisible = true;
1462
17
            break;
1463
1464
        case SHOW_AUTO:
1465
144
            if (mHPolicy == SHOW_NEVER)
1466
            {
1467
144
                mVBarVisible = (content->getHeight() > h);
1468
            }
1469
            else  // (mHPolicy == SHOW_ALWAYS)
1470
            {
1471
                mVBarVisible = (content->getHeight()
1472
                    > h - mScrollbarWidth);
1473
            }
1474
            break;
1475
        default:
1476
            break;
1477
    }
1478
}
1479
1480
bool ScrollArea::isSelectable() const noexcept2
1481
{
1482
    if (mVBarVisible || mHBarVisible)
1483
        return true;
1484
    return Widget::isSelectable();
1485

3
}