summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-07-23 15:42:12 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-07-23 15:42:12 +0300
commita624d11136f16b5799abecda2433c49500094b9b (patch)
treea9d5eee1bc72e1b641443910ea2fad3f461a232d /src
parenta3610ec020882015ae15b3b2808a443f81f651e6 (diff)
DocumentWidget: Scrolling via scrollbar and keys
Diffstat (limited to 'src')
-rw-r--r--src/app.c23
-rw-r--r--src/ui/documentwidget.c80
-rw-r--r--src/ui/scrollwidget.c57
-rw-r--r--src/ui/widget.c6
-rw-r--r--src/ui/widget.h1
5 files changed, 129 insertions, 38 deletions
diff --git a/src/app.c b/src/app.c
index 16354baf..05038a4b 100644
--- a/src/app.c
+++ b/src/app.c
@@ -45,6 +45,7 @@ struct Impl_App {
45 iBool running; 45 iBool running;
46 iWindow * window; 46 iWindow * window;
47 iSortedArray tickers; 47 iSortedArray tickers;
48 iBool pendingRefresh;
48 /* Preferences: */ 49 /* Preferences: */
49 iBool retainWindowSize; 50 iBool retainWindowSize;
50 float uiScale; 51 float uiScale;
@@ -136,6 +137,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
136 d->running = iFalse; 137 d->running = iFalse;
137 d->window = NULL; 138 d->window = NULL;
138 d->retainWindowSize = iTrue; 139 d->retainWindowSize = iTrue;
140 d->pendingRefresh = iFalse;
139 loadPrefs_App_(d); 141 loadPrefs_App_(d);
140 d->window = new_Window(); 142 d->window = new_Window();
141 /* Widget state init. */ { 143 /* Widget state init. */ {
@@ -233,6 +235,7 @@ void refresh_App(void) {
233 destroyPending_Widget(); 235 destroyPending_Widget();
234 draw_Window(d->window); 236 draw_Window(d->window);
235 recycle_Garbage(); 237 recycle_Garbage();
238 d->pendingRefresh = iFalse;
236} 239}
237 240
238int run_App(int argc, char **argv) { 241int run_App(int argc, char **argv) {
@@ -243,13 +246,17 @@ int run_App(int argc, char **argv) {
243} 246}
244 247
245void postRefresh_App(void) { 248void postRefresh_App(void) {
246 SDL_Event ev; 249 iApp *d = &app_;
247 ev.user.type = SDL_USEREVENT; 250 if (!d->pendingRefresh) {
248 ev.user.code = refresh_UserEventCode; 251 d->pendingRefresh = iTrue;
249 ev.user.windowID = get_Window() ? SDL_GetWindowID(get_Window()->win) : 0; 252 SDL_Event ev;
250 ev.user.data1 = NULL; 253 ev.user.type = SDL_USEREVENT;
251 ev.user.data2 = NULL; 254 ev.user.code = refresh_UserEventCode;
252 SDL_PushEvent(&ev); 255 ev.user.windowID = get_Window() ? SDL_GetWindowID(get_Window()->win) : 0;
256 ev.user.data1 = NULL;
257 ev.user.data2 = NULL;
258 SDL_PushEvent(&ev);
259 }
253} 260}
254 261
255void postCommand_App(const char *command) { 262void postCommand_App(const char *command) {
@@ -260,7 +267,9 @@ void postCommand_App(const char *command) {
260 ev.user.data1 = strdup(command); 267 ev.user.data1 = strdup(command);
261 ev.user.data2 = NULL; 268 ev.user.data2 = NULL;
262 SDL_PushEvent(&ev); 269 SDL_PushEvent(&ev);
270#if !defined (NDEBUG)
263 printf("[command] %s\n", command); fflush(stdout); 271 printf("[command] %s\n", command); fflush(stdout);
272#endif
264} 273}
265 274
266void postCommandf_App(const char *command, ...) { 275void postCommandf_App(const char *command, ...) {
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 8c707206..38c19fdd 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1,6 +1,7 @@
1#include "documentwidget.h" 1#include "documentwidget.h"
2#include "scrollwidget.h" 2#include "scrollwidget.h"
3#include "paint.h" 3#include "paint.h"
4#include "command.h"
4#include "util.h" 5#include "util.h"
5#include "app.h" 6#include "app.h"
6#include "../gemini.h" 7#include "../gemini.h"
@@ -19,8 +20,6 @@ enum iDocumentState {
19 ready_DocumentState, 20 ready_DocumentState,
20}; 21};
21 22
22
23
24struct Impl_DocumentWidget { 23struct Impl_DocumentWidget {
25 iWidget widget; 24 iWidget widget;
26 enum iDocumentState state; 25 enum iDocumentState state;
@@ -74,16 +73,16 @@ void init_DocumentWidget(iDocumentWidget *d) {
74 iWidget *w = as_Widget(d); 73 iWidget *w = as_Widget(d);
75 init_Widget(w); 74 init_Widget(w);
76 setId_Widget(w, "document"); 75 setId_Widget(w, "document");
77 d->state = blank_DocumentState; 76 d->state = blank_DocumentState;
78 d->url = new_String(); 77 d->url = new_String();
79 d->statusCode = 0; 78 d->statusCode = 0;
80 d->request = NULL; 79 d->request = NULL;
81 d->newSource = new_String(); 80 d->newSource = new_String();
82 d->doc = new_GmDocument(); 81 d->doc = new_GmDocument();
83 d->pageMargin = 5; 82 d->pageMargin = 5;
84 d->scrollY = 0; 83 d->scrollY = 0;
84 d->hoverLink = NULL;
85 init_PtrArray(&d->visibleLinks); 85 init_PtrArray(&d->visibleLinks);
86 d->hoverLink = NULL;
87 init_Click(&d->click, d, SDL_BUTTON_LEFT); 86 init_Click(&d->click, d, SDL_BUTTON_LEFT);
88 addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); 87 addChild_Widget(w, iClob(d->scroll = new_ScrollWidget()));
89} 88}
@@ -151,7 +150,7 @@ static void requestFinished_DocumentWidget_(iAnyObject *obj) {
151 iReleaseLater(d->request); 150 iReleaseLater(d->request);
152 d->request = NULL; 151 d->request = NULL;
153 fflush(stdout); 152 fflush(stdout);
154 postRefresh_App(); 153 refresh_Widget(constAs_Widget(d));
155} 154}
156 155
157static void fetch_DocumentWidget_(iDocumentWidget *d) { 156static void fetch_DocumentWidget_(iDocumentWidget *d) {
@@ -164,7 +163,7 @@ static void fetch_DocumentWidget_(iDocumentWidget *d) {
164 iFile *f = new_File(collect_String(newRange_String(url.path))); 163 iFile *f = new_File(collect_String(newRange_String(url.path)));
165 if (open_File(f, readOnly_FileMode)) { 164 if (open_File(f, readOnly_FileMode)) {
166 setBlock_String(d->newSource, collect_Block(readAll_File(f))); 165 setBlock_String(d->newSource, collect_Block(readAll_File(f)));
167 postRefresh_App(); 166 refresh_Widget(constAs_Widget(d));
168 } 167 }
169 iRelease(f); 168 iRelease(f);
170 return; 169 return;
@@ -228,6 +227,22 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
228 render_GmDocument(d->doc, visRange, addVisibleLink_DocumentWidget_, d); 227 render_GmDocument(d->doc, visRange, addVisibleLink_DocumentWidget_, d);
229} 228}
230 229
230static void scroll_DocumentWidget_(iDocumentWidget *d, int offset) {
231 d->scrollY += offset;
232 if (d->scrollY < 0) {
233 d->scrollY = 0;
234 }
235 const int scrollMax = scrollMax_DocumentWidget_(d);
236 if (scrollMax > 0) {
237 d->scrollY = iMin(d->scrollY, scrollMax);
238 }
239 else {
240 d->scrollY = 0;
241 }
242 updateVisible_DocumentWidget_(d);
243 refresh_Widget(as_Widget(d));
244}
245
231static iRangecc dirPath_(iRangecc path) { 246static iRangecc dirPath_(iRangecc path) {
232 const size_t pos = lastIndexOfCStr_Rangecc(&path, "/"); 247 const size_t pos = lastIndexOfCStr_Rangecc(&path, "/");
233 if (pos == iInvalidPos) return path; 248 if (pos == iInvalidPos) return path;
@@ -287,10 +302,35 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
287 setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d)); 302 setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d));
288 updateVisible_DocumentWidget_(d); 303 updateVisible_DocumentWidget_(d);
289 } 304 }
305 else if (isCommand_Widget(w, ev, "scroll.moved")) {
306 d->scrollY = arg_Command(command_UserEvent(ev));
307 updateVisible_DocumentWidget_(d);
308 return iTrue;
309 }
310 else if (isCommand_Widget(w, ev, "scroll.page")) {
311 scroll_DocumentWidget_(
312 d, arg_Command(command_UserEvent(ev)) * height_Rect(documentBounds_DocumentWidget_(d)));
313 return iTrue;
314 }
290 if (ev->type == SDL_KEYDOWN) { 315 if (ev->type == SDL_KEYDOWN) {
291 const int mods = keyMods_Sym(ev->key.keysym.mod); 316 const int mods = keyMods_Sym(ev->key.keysym.mod);
292 const int key = ev->key.keysym.sym; 317 const int key = ev->key.keysym.sym;
293 switch (key) { 318 switch (key) {
319 case SDLK_HOME:
320 d->scrollY = 0;
321 updateVisible_DocumentWidget_(d);
322 refresh_Widget(w);
323 return iTrue;
324 case SDLK_END:
325 d->scrollY = scrollMax_DocumentWidget_(d);
326 updateVisible_DocumentWidget_(d);
327 refresh_Widget(w);
328 return iTrue;
329 case SDLK_PAGEUP:
330 case SDLK_PAGEDOWN:
331 case ' ':
332 postCommand_Widget(w, "scroll.page arg:%d", key == SDLK_PAGEUP ? -1 : +1);
333 return iTrue;
294 case 'r': 334 case 'r':
295 if (mods == KMOD_PRIMARY) { 335 if (mods == KMOD_PRIMARY) {
296 fetch_DocumentWidget_(d); 336 fetch_DocumentWidget_(d);
@@ -300,7 +340,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
300 case '0': { 340 case '0': {
301 extern int enableHalfPixelGlyphs_Text; 341 extern int enableHalfPixelGlyphs_Text;
302 enableHalfPixelGlyphs_Text = !enableHalfPixelGlyphs_Text; 342 enableHalfPixelGlyphs_Text = !enableHalfPixelGlyphs_Text;
303 postRefresh_App(); 343 refresh_Widget(w);
304 printf("halfpixel: %d\n", enableHalfPixelGlyphs_Text); 344 printf("halfpixel: %d\n", enableHalfPixelGlyphs_Text);
305 fflush(stdout); 345 fflush(stdout);
306 break; 346 break;
@@ -308,19 +348,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
308 } 348 }
309 } 349 }
310 else if (ev->type == SDL_MOUSEWHEEL) { 350 else if (ev->type == SDL_MOUSEWHEEL) {
311 d->scrollY -= 3 * ev->wheel.y * lineHeight_Text(default_FontId); 351 scroll_DocumentWidget_(d, -3 * ev->wheel.y * lineHeight_Text(default_FontId));
312 if (d->scrollY < 0) {
313 d->scrollY = 0;
314 }
315 const int scrollMax = scrollMax_DocumentWidget_(d);
316 if (scrollMax > 0) {
317 d->scrollY = iMin(d->scrollY, scrollMax);
318 }
319 else {
320 d->scrollY = 0;
321 }
322 updateVisible_DocumentWidget_(d);
323 postRefresh_App();
324 return iTrue; 352 return iTrue;
325 } 353 }
326 else if (ev->type == SDL_MOUSEMOTION) { 354 else if (ev->type == SDL_MOUSEMOTION) {
@@ -339,7 +367,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
339 } 367 }
340 } 368 }
341 if (d->hoverLink != oldHoverLink) { 369 if (d->hoverLink != oldHoverLink) {
342 postRefresh_App(); 370 refresh_Widget(w);
343 } 371 }
344 } 372 }
345 switch (processEvent_Click(&d->click, ev)) { 373 switch (processEvent_Click(&d->click, ev)) {
diff --git a/src/ui/scrollwidget.c b/src/ui/scrollwidget.c
index cbfd2e70..1be62bb8 100644
--- a/src/ui/scrollwidget.c
+++ b/src/ui/scrollwidget.c
@@ -1,5 +1,6 @@
1#include "scrollwidget.h" 1#include "scrollwidget.h"
2#include "paint.h" 2#include "paint.h"
3#include "util.h"
3 4
4iDefineObjectConstruction(ScrollWidget) 5iDefineObjectConstruction(ScrollWidget)
5 6
@@ -8,6 +9,8 @@ struct Impl_ScrollWidget {
8 iRangei range; 9 iRangei range;
9 int thumb; 10 int thumb;
10 int thumbSize; 11 int thumbSize;
12 iClick click;
13 int startThumb;
11}; 14};
12 15
13void init_ScrollWidget(iScrollWidget *d) { 16void init_ScrollWidget(iScrollWidget *d) {
@@ -19,19 +22,23 @@ void init_ScrollWidget(iScrollWidget *d) {
19 moveToParentRightEdge_WidgetFlag, 22 moveToParentRightEdge_WidgetFlag,
20 iTrue); 23 iTrue);
21 w->rect.size.x = gap_UI * 3; 24 w->rect.size.x = gap_UI * 3;
25 init_Click(&d->click, d, SDL_BUTTON_LEFT);
22} 26}
23 27
24void deinit_ScrollWidget(iScrollWidget *d) { 28void deinit_ScrollWidget(iScrollWidget *d) {
25 iUnused(d); 29 iUnused(d);
26} 30}
27 31
32static int thumbSize_ScrollWidget_(const iScrollWidget *d) {
33 return iMax(gap_UI * 6, d->thumbSize);
34}
35
28static iRect thumbRect_ScrollWidget_(const iScrollWidget *d) { 36static iRect thumbRect_ScrollWidget_(const iScrollWidget *d) {
29 const iRect bounds = bounds_Widget(constAs_Widget(d)); 37 const iRect bounds = bounds_Widget(constAs_Widget(d));
30 iRect rect = init_Rect(bounds.pos.x, bounds.pos.y, bounds.size.x, 0); 38 iRect rect = init_Rect(bounds.pos.x, bounds.pos.y, bounds.size.x, 0);
31 const int total = size_Range(&d->range); 39 const int total = size_Range(&d->range);
32// printf("total: %d thumb: %d thsize: %d\n", total, d->thumb, d->thumbSize); fflush(stdout);
33 if (total > 0) { 40 if (total > 0) {
34 const int tsize = iMax(gap_UI * 6, d->thumbSize); 41 const int tsize = thumbSize_ScrollWidget_(d);
35 const int tpos = 42 const int tpos =
36 iClamp((float) d->thumb / (float) total, 0, 1) * (height_Rect(bounds) - tsize); 43 iClamp((float) d->thumb / (float) total, 0, 1) * (height_Rect(bounds) - tsize);
37 rect.pos.y = tpos; 44 rect.pos.y = tpos;
@@ -46,7 +53,7 @@ static void checkVisible_ScrollWidget_(iScrollWidget *d) {
46 53
47void setRange_ScrollWidget(iScrollWidget *d, iRangei range) { 54void setRange_ScrollWidget(iScrollWidget *d, iRangei range) {
48 range.end = iMax(range.start, range.end); 55 range.end = iMax(range.start, range.end);
49 d->range = range; 56 d->range = range;
50 checkVisible_ScrollWidget_(d); 57 checkVisible_ScrollWidget_(d);
51} 58}
52 59
@@ -58,18 +65,58 @@ void setThumb_ScrollWidget(iScrollWidget *d, int thumb, int thumbSize) {
58 65
59static iBool processEvent_ScrollWidget_(iScrollWidget *d, const SDL_Event *ev) { 66static iBool processEvent_ScrollWidget_(iScrollWidget *d, const SDL_Event *ev) {
60 iWidget *w = as_Widget(d); 67 iWidget *w = as_Widget(d);
68 switch (processEvent_Click(&d->click, ev)) {
69 case started_ClickResult:
70 setFlags_Widget(w, pressed_WidgetFlag, iTrue);
71 d->startThumb = d->thumb;
72 refresh_Widget(w);
73 return iTrue;
74 case drag_ClickResult: {
75 const iRect bounds = bounds_Widget(w);
76 const int offset = delta_Click(&d->click).y;
77 const int total = size_Range(&d->range);
78 int dpos = (float) offset / (float) (height_Rect(bounds) - thumbSize_ScrollWidget_(d)) * total;
79 d->thumb = iClamp(d->startThumb + dpos, d->range.start, d->range.end);
80 postCommand_Widget(w, "scroll.moved arg:%d", d->thumb);
81 refresh_Widget(w);
82 return iTrue;
83 }
84 case finished_ClickResult:
85 case aborted_ClickResult:
86 if (!isMoved_Click(&d->click)) {
87 /* Page up/down. */
88 const iRect tr = thumbRect_ScrollWidget_(d);
89 const int y = pos_Click(&d->click).y;
90 int pgDir = 0;
91 if (y < top_Rect(tr)) {
92 pgDir = -1;
93 }
94 else if (y > bottom_Rect(tr)) {
95 pgDir = +1;
96 }
97 if (pgDir) {
98 postCommand_Widget(w, "scroll.page arg:%d", pgDir);
99 }
100 }
101 setFlags_Widget(w, pressed_WidgetFlag, iFalse);
102 refresh_Widget(w);
103 return iTrue;
104 default:
105 break;
106 }
61 return processEvent_Widget(w, ev); 107 return processEvent_Widget(w, ev);
62} 108}
63 109
64static void draw_ScrollWidget_(const iScrollWidget *d) { 110static void draw_ScrollWidget_(const iScrollWidget *d) {
65 const iWidget *w = constAs_Widget(d); 111 const iWidget *w = constAs_Widget(d);
66 const iRect bounds = bounds_Widget(w); 112 const iRect bounds = bounds_Widget(w);
113 const iBool isPressed = (flags_Widget(w) & pressed_WidgetFlag) != 0;
67 if (bounds.size.x > 0) { 114 if (bounds.size.x > 0) {
68 iPaint p; 115 iPaint p;
69 init_Paint(&p); 116 init_Paint(&p);
70 drawRect_Paint(&p, bounds, black_ColorId); 117 drawRect_Paint(&p, bounds, black_ColorId);
71 iRect tr = thumbRect_ScrollWidget_(d); 118 fillRect_Paint(&p, shrunk_Rect(thumbRect_ScrollWidget_(d), one_I2()),
72 fillRect_Paint(&p, thumbRect_ScrollWidget_(d), gray50_ColorId); 119 isPressed ? orange_ColorId : gray50_ColorId);
73 } 120 }
74} 121}
75 122
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 5c23860f..86d2e195 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -616,6 +616,12 @@ void postCommand_Widget(const iWidget *d, const char *cmd, ...) {
616 deinit_String(&str); 616 deinit_String(&str);
617} 617}
618 618
619void refresh_Widget(const iWidget *d) {
620 /* TODO: Could be widget specific, if parts of the tree are cached. */
621 iUnused(d);
622 postRefresh_App();
623}
624
619iBeginDefineClass(Widget) 625iBeginDefineClass(Widget)
620 .processEvent = processEvent_Widget, 626 .processEvent = processEvent_Widget,
621 .draw = draw_Widget, 627 .draw = draw_Widget,
diff --git a/src/ui/widget.h b/src/ui/widget.h
index 2d13fe78..af3934e6 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -123,6 +123,7 @@ void arrange_Widget (iWidget *);
123iBool dispatchEvent_Widget(iWidget *, const SDL_Event *); 123iBool dispatchEvent_Widget(iWidget *, const SDL_Event *);
124iBool processEvent_Widget (iWidget *, const SDL_Event *); 124iBool processEvent_Widget (iWidget *, const SDL_Event *);
125void postCommand_Widget (const iWidget *, const char *cmd, ...); 125void postCommand_Widget (const iWidget *, const char *cmd, ...);
126void refresh_Widget (const iWidget *);
126 127
127void setFocus_Widget (iWidget *); 128void setFocus_Widget (iWidget *);
128iWidget *focus_Widget (void); 129iWidget *focus_Widget (void);