summaryrefslogtreecommitdiff
path: root/src/ui/scrollwidget.c
blob: 65b20a280be9702145ff9dd4165b383aa22ad393 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include "scrollwidget.h"
#include "paint.h"
#include "util.h"

iDefineObjectConstruction(ScrollWidget)

struct Impl_ScrollWidget {
    iWidget widget;
    iRangei range;
    int thumb;
    int thumbSize;
    iClick click;
    int startThumb;
};

void init_ScrollWidget(iScrollWidget *d) {
    iWidget *w = as_Widget(d);
    init_Widget(w);
    setId_Widget(w, "scroll");
    setFlags_Widget(w,
                    fixedWidth_WidgetFlag | resizeToParentHeight_WidgetFlag |
                        moveToParentRightEdge_WidgetFlag,
                    iTrue);
    w->rect.size.x = gap_UI * 3;
    init_Click(&d->click, d, SDL_BUTTON_LEFT);
}

void deinit_ScrollWidget(iScrollWidget *d) {
    iUnused(d);
}

static int thumbSize_ScrollWidget_(const iScrollWidget *d) {
    return iMax(gap_UI * 6, d->thumbSize);
}

static iRect thumbRect_ScrollWidget_(const iScrollWidget *d) {
    const iRect bounds = bounds_Widget(constAs_Widget(d));
    iRect rect = init_Rect(bounds.pos.x, bounds.pos.y, bounds.size.x, 0);
    const int total = size_Range(&d->range);
    if (total > 0) {
        const int tsize = thumbSize_ScrollWidget_(d);
        const int tpos =
            iClamp((float) d->thumb / (float) total, 0, 1) * (height_Rect(bounds) - tsize);
        rect.pos.y  = bounds.pos.y + tpos;
        rect.size.y = tsize;
    }
    return rect;
}

static void checkVisible_ScrollWidget_(iScrollWidget *d) {
    setFlags_Widget(as_Widget(d), hidden_WidgetFlag, height_Rect(thumbRect_ScrollWidget_(d)) == 0);
}

void setRange_ScrollWidget(iScrollWidget *d, iRangei range) {
    range.end = iMax(range.start, range.end);
    d->range  = range;
    checkVisible_ScrollWidget_(d);
}

void setThumb_ScrollWidget(iScrollWidget *d, int thumb, int thumbSize) {
    d->thumb     = thumb;
    d->thumbSize = thumbSize;
    checkVisible_ScrollWidget_(d);
}

static iBool processEvent_ScrollWidget_(iScrollWidget *d, const SDL_Event *ev) {
    iWidget *w = as_Widget(d);
    switch (processEvent_Click(&d->click, ev)) {
        case started_ClickResult:
            setFlags_Widget(w, pressed_WidgetFlag, iTrue);
            d->startThumb = d->thumb;
            refresh_Widget(w);
            return iTrue;
        case drag_ClickResult: {
            const iRect bounds = bounds_Widget(w);
            const int offset = delta_Click(&d->click).y;
            const int total = size_Range(&d->range);
            int dpos = (float) offset / (float) (height_Rect(bounds) - thumbSize_ScrollWidget_(d)) * total;
            d->thumb = iClamp(d->startThumb + dpos, d->range.start, d->range.end);
            postCommand_Widget(w, "scroll.moved arg:%d", d->thumb);
            refresh_Widget(w);
            return iTrue;
        }
        case finished_ClickResult:
        case aborted_ClickResult:
            if (!isMoved_Click(&d->click)) {
                /* Page up/down. */
                const iRect tr = thumbRect_ScrollWidget_(d);
                const int y = pos_Click(&d->click).y;
                int pgDir = 0;
                if (y < top_Rect(tr)) {
                    pgDir = -1;
                }
                else if (y > bottom_Rect(tr)) {
                    pgDir = +1;
                }
                if (pgDir) {
                    postCommand_Widget(w, "scroll.page arg:%d", pgDir);
                }
            }
            setFlags_Widget(w, pressed_WidgetFlag, iFalse);
            refresh_Widget(w);
            return iTrue;
        default:
            break;
    }
    return processEvent_Widget(w, ev);
}

static void draw_ScrollWidget_(const iScrollWidget *d) {
    const iWidget *w = constAs_Widget(d);
    const iRect bounds = bounds_Widget(w);
    const iBool isPressed = (flags_Widget(w) & pressed_WidgetFlag) != 0;
    if (bounds.size.x > 0) {
        iPaint p;
        init_Paint(&p);
        drawRect_Paint(&p, bounds, black_ColorId);
        fillRect_Paint(&p, shrunk_Rect(thumbRect_ScrollWidget_(d), init1_I2(gap_UI / 2)),
                       isPressed ? orange_ColorId : gray50_ColorId);
    }
}

iBeginDefineSubclass(ScrollWidget, Widget)
    .processEvent = (iAny *) processEvent_ScrollWidget_,
    .draw         = (iAny *) draw_ScrollWidget_,
iEndDefineSubclass(ScrollWidget)