diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-24 08:18:46 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-24 08:18:46 +0300 |
commit | 859fad2c6d5013ace7fcb749b591468dd0b65612 (patch) | |
tree | 7a743d1bc638ec014fd5006af5b0c2aa6c8124bf /src/ui/indicatorwidget.c | |
parent | 57cc6baba013965b1e9aed244ed40ee604e8a872 (diff) |
DocumentWidget: Page load progress indicator
Show clearly that something is happening, even though we don't know the exact duration of the operation.
Diffstat (limited to 'src/ui/indicatorwidget.c')
-rw-r--r-- | src/ui/indicatorwidget.c | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/src/ui/indicatorwidget.c b/src/ui/indicatorwidget.c new file mode 100644 index 00000000..d43e23d9 --- /dev/null +++ b/src/ui/indicatorwidget.c | |||
@@ -0,0 +1,158 @@ | |||
1 | /* Copyright 2020 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #include "indicatorwidget.h" | ||
24 | #include "paint.h" | ||
25 | #include "util.h" | ||
26 | #include "app.h" | ||
27 | #include "command.h" | ||
28 | |||
29 | #include <SDL_timer.h> | ||
30 | |||
31 | static int timerId_; /* common timer for all indicators */ | ||
32 | static int animCount_; /* number of animating indicators */ | ||
33 | |||
34 | static uint32_t postRefresh_(uint32_t interval, void *context) { | ||
35 | iUnused(context); | ||
36 | postRefresh_App(); | ||
37 | return interval; | ||
38 | } | ||
39 | |||
40 | static void startTimer_(void) { | ||
41 | animCount_++; | ||
42 | if (!timerId_) { | ||
43 | timerId_ = SDL_AddTimer(1000 / 60, postRefresh_, NULL); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | static void stopTimer_(void) { | ||
48 | iAssert(animCount_ > 0); | ||
49 | if (--animCount_ == 0) { | ||
50 | iAssert(timerId_); | ||
51 | SDL_RemoveTimer(timerId_); | ||
52 | timerId_ = 0; | ||
53 | } | ||
54 | } | ||
55 | |||
56 | struct Impl_IndicatorWidget{ | ||
57 | iWidget widget; | ||
58 | iAnim pos; | ||
59 | }; | ||
60 | |||
61 | iDefineObjectConstruction(IndicatorWidget) | ||
62 | |||
63 | iLocalDef iBool isActive_IndicatorWidget_(const iIndicatorWidget *d) { | ||
64 | return isSelected_Widget(d); | ||
65 | } | ||
66 | |||
67 | static void setActive_IndicatorWidget_(iIndicatorWidget *d, iBool set) { | ||
68 | setFlags_Widget(as_Widget(d), selected_WidgetFlag, set); | ||
69 | } | ||
70 | |||
71 | void init_IndicatorWidget(iIndicatorWidget *d) { | ||
72 | iWidget *w = &d->widget; | ||
73 | init_Widget(w); | ||
74 | init_Anim(&d->pos, 0); | ||
75 | } | ||
76 | |||
77 | static void startTimer_IndicatorWidget_(iIndicatorWidget *d) { | ||
78 | if (!isActive_IndicatorWidget_(d)) { | ||
79 | startTimer_(); | ||
80 | setActive_IndicatorWidget_(d, iTrue); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | static void stopTimer_IndicatorWidget_(iIndicatorWidget *d) { | ||
85 | if (isActive_IndicatorWidget_(d)) { | ||
86 | stopTimer_(); | ||
87 | setActive_IndicatorWidget_(d, iFalse); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | void deinit_IndicatorWidget(iIndicatorWidget *d) { | ||
92 | stopTimer_IndicatorWidget_(d); | ||
93 | } | ||
94 | |||
95 | static iBool isCompleted_IndicatorWidget_(const iIndicatorWidget *d) { | ||
96 | return targetValue_Anim(&d->pos) == 1.0f; | ||
97 | } | ||
98 | |||
99 | void draw_IndicatorWidget_(const iIndicatorWidget *d) { | ||
100 | const float pos = value_Anim(&d->pos); | ||
101 | if (pos > 0.0f && pos < 1.0f) { | ||
102 | const iWidget *w = &d->widget; | ||
103 | const iRect rect = innerBounds_Widget(w); | ||
104 | iPaint p; | ||
105 | init_Paint(&p); | ||
106 | drawHLine_Paint(&p, | ||
107 | topLeft_Rect(rect), | ||
108 | pos * width_Rect(rect), | ||
109 | isCompleted_IndicatorWidget_(d) ? uiTextAction_ColorId | ||
110 | : uiTextCaution_ColorId); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | iBool processEvent_IndicatorWidget_(iIndicatorWidget *d, const SDL_Event *ev) { | ||
115 | iWidget *w = &d->widget; | ||
116 | if (ev->type == SDL_USEREVENT && ev->user.code == refresh_UserEventCode) { | ||
117 | if (isFinished_Anim(&d->pos)) { | ||
118 | stopTimer_IndicatorWidget_(d); | ||
119 | } | ||
120 | } | ||
121 | else if (isCommand_SDLEvent(ev)) { | ||
122 | const char *cmd = command_UserEvent(ev); | ||
123 | if (startsWith_CStr(cmd, "document.request.")) { | ||
124 | if (pointerLabel_Command(cmd, "doc") == parent_Widget(w)) { | ||
125 | cmd += 17; | ||
126 | if (equal_Command(cmd, "started")) { | ||
127 | setValue_Anim(&d->pos, 0, 0); | ||
128 | setValue_Anim(&d->pos, 0.75f, 4000); | ||
129 | setFlags_Anim(&d->pos, easeOut_AnimFlag, iTrue); | ||
130 | startTimer_IndicatorWidget_(d); | ||
131 | } | ||
132 | else if (equal_Command(cmd, "finished")) { | ||
133 | if (value_Anim(&d->pos) > 0.01f) { | ||
134 | setValue_Anim(&d->pos, 1.0f, 250); | ||
135 | setFlags_Anim(&d->pos, easeOut_AnimFlag, iFalse); | ||
136 | startTimer_IndicatorWidget_(d); | ||
137 | } | ||
138 | else { | ||
139 | setValue_Anim(&d->pos, 0, 0); | ||
140 | stopTimer_IndicatorWidget_(d); | ||
141 | refresh_Widget(d); | ||
142 | } | ||
143 | } | ||
144 | else if (equal_Command(cmd, "cancelled")) { | ||
145 | setValue_Anim(&d->pos, 0, 0); | ||
146 | stopTimer_IndicatorWidget_(d); | ||
147 | refresh_Widget(d); | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | return iFalse; | ||
153 | } | ||
154 | |||
155 | iBeginDefineSubclass(IndicatorWidget, Widget) | ||
156 | .draw = (iAny *) draw_IndicatorWidget_, | ||
157 | .processEvent = (iAny *) processEvent_IndicatorWidget_, | ||
158 | iEndDefineSubclass(IndicatorWidget) | ||