GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/scrollarea.cpp Lines: 299 685 43.6 %
Date: 2021-03-17 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-2019  The ManaPlus Developers
6
 *  Copyright (C) 2019-2021  Andrei Karas
7
 *
8
 *  This file is part of The ManaPlus Client.
9
 *
10
 *  This program is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU General Public License as published by
12
 *  the Free Software Foundation; either version 2 of the License, or
13
 *  any later version.
14
 *
15
 *  This program is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU General Public License
21
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
/*      _______   __   __   __   ______   __   __   _______   __   __
25
 *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
26
 *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
27
 *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
28
 *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
29
 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
30
 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
31
 *
32
 * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
33
 *
34
 *
35
 * Per Larsson a.k.a finalman
36
 * Olof Naessén a.k.a jansem/yakslem
37
 *
38
 * Visit: http://guichan.sourceforge.net
39
 *
40
 * License: (BSD)
41
 * Redistribution and use in source and binary forms, with or without
42
 * modification, are permitted provided that the following conditions
43
 * are met:
44
 * 1. Redistributions of source code must retain the above copyright
45
 *    notice, this list of conditions and the following disclaimer.
46
 * 2. Redistributions in binary form must reproduce the above copyright
47
 *    notice, this list of conditions and the following disclaimer in
48
 *    the documentation and/or other materials provided with the
49
 *    distribution.
50
 * 3. Neither the name of Guichan nor the names of its contributors may
51
 *    be used to endorse or promote products derived from this software
52
 *    without specific prior written permission.
53
 *
54
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
55
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
56
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
57
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
58
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
60
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
61
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
62
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
63
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65
 */
66
67
#include "gui/widgets/scrollarea.h"
68
69
#include "settings.h"
70
71
#include "gui/gui.h"
72
#include "gui/skin.h"
73
74
#include "utils/delete2.h"
75
#include "utils/stringutils.h"
76
77
#include "render/graphics.h"
78
79
#include "render/vertexes/imagecollection.h"
80
81
#include "resources/imagerect.h"
82
83
#include "resources/image/image.h"
84
85
#include "debug.h"
86
87
int ScrollArea::instances = 0;
88
float ScrollArea::mAlpha = 1.0;
89
bool ScrollArea::mShowButtons = true;
90
int ScrollArea::mMarkerSize = 0;
91
int ScrollArea::mScrollbarSize = 12;
92
1
ImageRect ScrollArea::background;
93
1
ImageRect ScrollArea::vMarker;
94
1
ImageRect ScrollArea::vMarkerHi;
95
1
ImageRect ScrollArea::vBackground;
96
1
ImageRect ScrollArea::hBackground;
97
Image *ScrollArea::buttons[4][2];
98
99
3
static std::string const buttonFiles[2] =
100
{
101
    "scrollbuttons.xml",
102
    "scrollbuttons_pressed.xml"
103

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

164
    mVertexes(new ImageCollection),
113

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

17
    if (mVBarVisible || mHBarVisible)
348
    {
349
11
        if (mOpaque == Opaque_false)
350
            updateCalcFlag(graphics);
351
        // need add caching or remove calc calls.
352
//        if (mRedraw)
353
        {
354
11
            mVertexes->clear();
355
11
            if (mVBarVisible)
356
            {
357
11
                if (mShowButtons)
358
                {
359
                    calcButton(graphics, UP);
360
                    calcButton(graphics, DOWN);
361
                }
362
11
                calcVBar(graphics);
363
11
                calcVMarker(graphics);
364
            }
365
366
11
            if (mHBarVisible)
367
            {
368
                if (mShowButtons)
369
                {
370
                    calcButton(graphics, LEFT);
371
                    calcButton(graphics, RIGHT);
372
                }
373
                calcHBar(graphics);
374
                calcHMarker(graphics);
375
            }
376
11
            graphics->finalize(mVertexes);
377
        }
378
11
        graphics->drawTileCollection(mVertexes);
379
    }
380
381
17
    updateAlpha();
382
383
17
    if (mRedraw)
384
    {
385
17
        const bool redraw = graphics->getRedraw();
386
17
        graphics->setRedraw(true);
387
17
        drawChildren(graphics);
388
17
        graphics->setRedraw(redraw);
389
    }
390
    else
391
    {
392
        drawChildren(graphics);
393
    }
394
17
    mRedraw = false;
395
    BLOCK_END("ScrollArea::draw")
396
17
}
397
398
void ScrollArea::safeDraw(Graphics *const graphics)
399
{
400
    BLOCK_START("ScrollArea::draw")
401
    if (mVBarVisible)
402
    {
403
        if (mShowButtons)
404
        {
405
            drawButton(graphics, UP);
406
            drawButton(graphics, DOWN);
407
        }
408
        drawVBar(graphics);
409
        drawVMarker(graphics);
410
    }
411
412
    if (mHBarVisible)
413
    {
414
        if (mShowButtons)
415
        {
416
            drawButton(graphics, LEFT);
417
            drawButton(graphics, RIGHT);
418
        }
419
        drawHBar(graphics);
420
        drawHMarker(graphics);
421
    }
422
423
    updateAlpha();
424
425
    safeDrawChildren(graphics);
426
    mRedraw = false;
427
    BLOCK_END("ScrollArea::draw")
428
}
429
430
16
void ScrollArea::updateCalcFlag(const Graphics *const graphics)
431
{
432
16
    if (!mRedraw)
433
    {
434
        // because we don't know where parent windows was moved,
435
        // need recalc vertexes
436
        const ClipRect &rect = graphics->getTopClip();
437
        if (rect.xOffset != mXOffset || rect.yOffset != mYOffset)
438
        {
439
            mRedraw = true;
440
            mXOffset = rect.xOffset;
441
            mYOffset = rect.yOffset;
442
        }
443
        else if (rect.width != mDrawWidth || rect.height != mDrawHeight)
444
        {
445
            mRedraw = true;
446
            mDrawWidth = rect.width;
447
            mDrawHeight = rect.height;
448
        }
449
        else if (graphics->getRedraw())
450
        {
451
            mRedraw = true;
452
        }
453
    }
454
16
}
455
456
16
void ScrollArea::drawFrame(Graphics *const graphics)
457
{
458
    BLOCK_START("ScrollArea::drawFrame")
459
16
    if (mOpaque == Opaque_true)
460
    {
461
16
        updateCalcFlag(graphics);
462
463
16
        if (mRedraw)
464
        {
465
16
            const int bs = mFrameSize * 2;
466
16
            const int w = mDimension.width + bs;
467
16
            const int h = mDimension.height + bs;
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
    Widget *const content = getContent();
763
    if (content != nullptr)
764
    {
765
        const unsigned int frameSize = 2 * mFrameSize;
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
        const int length = getVerticalMarkerDimension().height;
939
940
        if ((barDim.height - length) > 0)
941
        {
942
            const int pos = event.getY() - barDim.y
943
                - mVerticalMarkerDragOffset;
944
            setVerticalScrollAmount((getVerticalMaxScroll() * pos)
945
                                      / (barDim.height - length));
946
        }
947
        else
948
        {
949
            setVerticalScrollAmount(0);
950
        }
951
    }
952
953
    if (mIsHorizontalMarkerDragged)
954
    {
955
        const Rect barDim = getHorizontalBarDimension();
956
        const int length = getHorizontalMarkerDimension().width;
957
958
        if ((barDim.width - length) > 0)
959
        {
960
            const int pos = event.getX() - barDim.x
961
                - mHorizontalMarkerDragOffset;
962
            setHorizontalScrollAmount((getHorizontalMaxScroll() * pos)
963
                                      / (barDim.width - length));
964
        }
965
        else
966
        {
967
            setHorizontalScrollAmount(0);
968
        }
969
    }
970
971
    event.consume();
972
    mRedraw = true;
973
}
974
975
11
Rect ScrollArea::getVerticalBarDimension() const
976
{
977
11
    if (!mVBarVisible)
978
        return Rect(0, 0, 0, 0);
979
980
11
    const int height = mShowButtons ? mScrollbarWidth : 0;
981
11
    if (mHBarVisible)
982
    {
983
        return Rect(mDimension.width - mScrollbarWidth,
984
            height,
985
            mScrollbarWidth,
986
            mDimension.height - 2 * height - mScrollbarWidth);
987
    }
988
989
11
    return Rect(mDimension.width - mScrollbarWidth,
990
        height,
991
11
        mScrollbarWidth,
992
22
        mDimension.height - 2 * height);
993
}
994
995
Rect ScrollArea::getHorizontalBarDimension() const
996
{
997
    if (!mHBarVisible)
998
        return Rect(0, 0, 0, 0);
999
1000
    const int width = mShowButtons ? mScrollbarWidth : 0;
1001
    if (mVBarVisible)
1002
    {
1003
        return Rect(width,
1004
            mDimension.height - mScrollbarWidth,
1005
            mDimension.width - 2 * width - mScrollbarWidth,
1006
            mScrollbarWidth);
1007
    }
1008
1009
    return Rect(width,
1010
                      mDimension.height - mScrollbarWidth,
1011
                      mDimension.width - 2 * width,
1012
                      mScrollbarWidth);
1013
}
1014
1015
11
Rect ScrollArea::getVerticalMarkerDimension()
1016
{
1017
11
    if (!mVBarVisible)
1018
        return Rect(0, 0, 0, 0);
1019
1020
    int length;
1021
    int pos;
1022
    int height;
1023
    const int h2 = mShowButtons
1024
11
        ? mScrollbarWidth : mMarkerSize / 2;
1025
    const Widget *content;
1026
22
    if (!mWidgets.empty())
1027
22
        content = *mWidgets.begin();
1028
    else
1029
        content = nullptr;
1030
1031
11
    if (mHBarVisible)
1032
        height = mDimension.height - 2 * h2 - mScrollbarWidth;
1033
    else
1034
11
        height = mDimension.height - 2 * h2;
1035
1036
11
    const int maxV = getVerticalMaxScroll();
1037

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




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







207
    if (vScroll > max)
1242
5
        mVScroll = max;
1243
1244







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






204
    if (hScroll > max)
1255
        mHScroll = max;
1256






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












3912
        mHBarVisible ? (getHeight() - mScrollbarWidth) : getHeight());
1327
1328












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

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

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

3
}