diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 23 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 80 | ||||
-rw-r--r-- | src/ui/scrollwidget.c | 57 | ||||
-rw-r--r-- | src/ui/widget.c | 6 | ||||
-rw-r--r-- | src/ui/widget.h | 1 |
5 files changed, 129 insertions, 38 deletions
@@ -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 | ||
238 | int run_App(int argc, char **argv) { | 241 | int run_App(int argc, char **argv) { |
@@ -243,13 +246,17 @@ int run_App(int argc, char **argv) { | |||
243 | } | 246 | } |
244 | 247 | ||
245 | void postRefresh_App(void) { | 248 | void 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 | ||
255 | void postCommand_App(const char *command) { | 262 | void 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 | ||
266 | void postCommandf_App(const char *command, ...) { | 275 | void 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 | |||
24 | struct Impl_DocumentWidget { | 23 | struct 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 | ||
157 | static void fetch_DocumentWidget_(iDocumentWidget *d) { | 156 | static 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 | ||
230 | static 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 | |||
231 | static iRangecc dirPath_(iRangecc path) { | 246 | static 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 | ||
4 | iDefineObjectConstruction(ScrollWidget) | 5 | iDefineObjectConstruction(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 | ||
13 | void init_ScrollWidget(iScrollWidget *d) { | 16 | void 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 | ||
24 | void deinit_ScrollWidget(iScrollWidget *d) { | 28 | void deinit_ScrollWidget(iScrollWidget *d) { |
25 | iUnused(d); | 29 | iUnused(d); |
26 | } | 30 | } |
27 | 31 | ||
32 | static int thumbSize_ScrollWidget_(const iScrollWidget *d) { | ||
33 | return iMax(gap_UI * 6, d->thumbSize); | ||
34 | } | ||
35 | |||
28 | static iRect thumbRect_ScrollWidget_(const iScrollWidget *d) { | 36 | static 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 | ||
47 | void setRange_ScrollWidget(iScrollWidget *d, iRangei range) { | 54 | void 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 | ||
59 | static iBool processEvent_ScrollWidget_(iScrollWidget *d, const SDL_Event *ev) { | 66 | static 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 | ||
64 | static void draw_ScrollWidget_(const iScrollWidget *d) { | 110 | static 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 | ||
619 | void 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 | |||
619 | iBeginDefineClass(Widget) | 625 | iBeginDefineClass(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 *); | |||
123 | iBool dispatchEvent_Widget(iWidget *, const SDL_Event *); | 123 | iBool dispatchEvent_Widget(iWidget *, const SDL_Event *); |
124 | iBool processEvent_Widget (iWidget *, const SDL_Event *); | 124 | iBool processEvent_Widget (iWidget *, const SDL_Event *); |
125 | void postCommand_Widget (const iWidget *, const char *cmd, ...); | 125 | void postCommand_Widget (const iWidget *, const char *cmd, ...); |
126 | void refresh_Widget (const iWidget *); | ||
126 | 127 | ||
127 | void setFocus_Widget (iWidget *); | 128 | void setFocus_Widget (iWidget *); |
128 | iWidget *focus_Widget (void); | 129 | iWidget *focus_Widget (void); |