diff options
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/documentwidget.c | 47 | ||||
-rw-r--r-- | src/ui/scrollwidget.c | 79 | ||||
-rw-r--r-- | src/ui/scrollwidget.h | 11 | ||||
-rw-r--r-- | src/ui/widget.c | 6 | ||||
-rw-r--r-- | src/ui/widget.h | 1 |
5 files changed, 127 insertions, 17 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index a21d660d..8c707206 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include "documentwidget.h" | 1 | #include "documentwidget.h" |
2 | #include "scrollwidget.h" | ||
2 | #include "paint.h" | 3 | #include "paint.h" |
3 | #include "util.h" | 4 | #include "util.h" |
4 | #include "app.h" | 5 | #include "app.h" |
@@ -33,6 +34,7 @@ struct Impl_DocumentWidget { | |||
33 | iPtrArray visibleLinks; | 34 | iPtrArray visibleLinks; |
34 | const iGmRun *hoverLink; | 35 | const iGmRun *hoverLink; |
35 | iClick click; | 36 | iClick click; |
37 | iScrollWidget *scroll; | ||
36 | }; | 38 | }; |
37 | 39 | ||
38 | iDeclareType(Url) | 40 | iDeclareType(Url) |
@@ -72,7 +74,6 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
72 | iWidget *w = as_Widget(d); | 74 | iWidget *w = as_Widget(d); |
73 | init_Widget(w); | 75 | init_Widget(w); |
74 | setId_Widget(w, "document"); | 76 | setId_Widget(w, "document"); |
75 | setBackgroundColor_Widget(w, gray25_ColorId); | ||
76 | d->state = blank_DocumentState; | 77 | d->state = blank_DocumentState; |
77 | d->url = new_String(); | 78 | d->url = new_String(); |
78 | d->statusCode = 0; | 79 | d->statusCode = 0; |
@@ -84,6 +85,7 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
84 | init_PtrArray(&d->visibleLinks); | 85 | init_PtrArray(&d->visibleLinks); |
85 | d->hoverLink = NULL; | 86 | d->hoverLink = NULL; |
86 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | 87 | init_Click(&d->click, d, SDL_BUTTON_LEFT); |
88 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); | ||
87 | } | 89 | } |
88 | 90 | ||
89 | void deinit_DocumentWidget(iDocumentWidget *d) { | 91 | void deinit_DocumentWidget(iDocumentWidget *d) { |
@@ -110,11 +112,13 @@ static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | |||
110 | return rect; | 112 | return rect; |
111 | } | 113 | } |
112 | 114 | ||
115 | #if 0 | ||
113 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | 116 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { |
114 | /* TODO: lock source during update */ | 117 | /* TODO: lock source during update */ |
115 | setSource_GmDocument(d->doc, source, documentWidth_DocumentWidget_(d)); | 118 | setSource_GmDocument(d->doc, source, documentWidth_DocumentWidget_(d)); |
116 | d->state = ready_DocumentState; | 119 | d->state = ready_DocumentState; |
117 | } | 120 | } |
121 | #endif | ||
118 | 122 | ||
119 | static iRangecc getLine_(iRangecc text) { | 123 | static iRangecc getLine_(iRangecc text) { |
120 | iRangecc line = { text.start, text.start }; | 124 | iRangecc line = { text.start, text.start }; |
@@ -194,6 +198,12 @@ void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) { | |||
194 | delete_String(newUrl); | 198 | delete_String(newUrl); |
195 | } | 199 | } |
196 | 200 | ||
201 | static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) { | ||
202 | const int margin = gap_UI * d->pageMargin; | ||
203 | return (iRangei){ d->scrollY - margin, | ||
204 | d->scrollY + height_Rect(bounds_Widget(constAs_Widget(d))) - margin }; | ||
205 | } | ||
206 | |||
197 | static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) { | 207 | static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) { |
198 | iDocumentWidget *d = context; | 208 | iDocumentWidget *d = context; |
199 | if (run->linkId) { | 209 | if (run->linkId) { |
@@ -201,19 +211,21 @@ static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) { | |||
201 | } | 211 | } |
202 | } | 212 | } |
203 | 213 | ||
204 | static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) { | 214 | static int scrollMax_DocumentWidget_(const iDocumentWidget *d) { |
205 | const int margin = gap_UI * d->pageMargin; | 215 | return size_GmDocument(d->doc).y - height_Rect(bounds_Widget(constAs_Widget(d))) + |
206 | return (iRangei){ d->scrollY - margin, | 216 | 2 * d->pageMargin * gap_UI; |
207 | d->scrollY + height_Rect(bounds_Widget(constAs_Widget(d))) - margin }; | ||
208 | } | 217 | } |
209 | 218 | ||
210 | static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | 219 | static void updateVisible_DocumentWidget_(iDocumentWidget *d) { |
220 | const iRangei visRange = visibleRange_DocumentWidget_(d); | ||
221 | const iRect bounds = bounds_Widget(as_Widget(d)); | ||
222 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_DocumentWidget_(d) }); | ||
223 | const int docSize = size_GmDocument(d->doc).y; | ||
224 | setThumb_ScrollWidget(d->scroll, | ||
225 | d->scrollY, | ||
226 | docSize > 0 ? height_Rect(bounds) * size_Range(&visRange) / docSize : 0); | ||
211 | clear_PtrArray(&d->visibleLinks); | 227 | clear_PtrArray(&d->visibleLinks); |
212 | render_GmDocument( | 228 | render_GmDocument(d->doc, visRange, addVisibleLink_DocumentWidget_, d); |
213 | d->doc, | ||
214 | visibleRange_DocumentWidget_(d), | ||
215 | addVisibleLink_DocumentWidget_, | ||
216 | d); | ||
217 | } | 229 | } |
218 | 230 | ||
219 | static iRangecc dirPath_(iRangecc path) { | 231 | static iRangecc dirPath_(iRangecc path) { |
@@ -300,8 +312,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
300 | if (d->scrollY < 0) { | 312 | if (d->scrollY < 0) { |
301 | d->scrollY = 0; | 313 | d->scrollY = 0; |
302 | } | 314 | } |
303 | const int scrollMax = | 315 | const int scrollMax = scrollMax_DocumentWidget_(d); |
304 | size_GmDocument(d->doc).y - height_Rect(bounds_Widget(w)) + 2 * d->pageMargin * gap_UI; | ||
305 | if (scrollMax > 0) { | 316 | if (scrollMax > 0) { |
306 | d->scrollY = iMin(d->scrollY, scrollMax); | 317 | d->scrollY = iMin(d->scrollY, scrollMax); |
307 | } | 318 | } |
@@ -377,21 +388,25 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
377 | draw_Widget(w); | 388 | draw_Widget(w); |
378 | /* Update the document? */ | 389 | /* Update the document? */ |
379 | if (!isEmpty_String(d->newSource)) { | 390 | if (!isEmpty_String(d->newSource)) { |
391 | iDocumentWidget *m = iConstCast(iDocumentWidget *, d); | ||
380 | /* TODO: Do this in the background. However, that requires a text metrics calculator | 392 | /* TODO: Do this in the background. However, that requires a text metrics calculator |
381 | that does not try to cache the glyph bitmaps. */ | 393 | that does not try to cache the glyph bitmaps. */ |
382 | setSource_GmDocument(d->doc, d->newSource, documentWidth_DocumentWidget_(d)); | 394 | setSource_GmDocument(m->doc, m->newSource, documentWidth_DocumentWidget_(m)); |
383 | clear_String(d->newSource); | 395 | clear_String(m->newSource); |
384 | iConstCast(iDocumentWidget *, d)->state = ready_DocumentState; | 396 | m->scrollY = 0; |
385 | updateVisible_DocumentWidget_(iConstCast(iDocumentWidget *, d)); | 397 | m->state = ready_DocumentState; |
398 | updateVisible_DocumentWidget_(m); | ||
386 | } | 399 | } |
387 | if (d->state != ready_DocumentState) return; | 400 | if (d->state != ready_DocumentState) return; |
388 | iDrawContext ctx = { .widget = d, .bounds = documentBounds_DocumentWidget_(d) }; | 401 | iDrawContext ctx = { .widget = d, .bounds = documentBounds_DocumentWidget_(d) }; |
389 | init_Paint(&ctx.paint); | 402 | init_Paint(&ctx.paint); |
403 | fillRect_Paint(&ctx.paint, bounds_Widget(w), gray25_ColorId); | ||
390 | render_GmDocument( | 404 | render_GmDocument( |
391 | d->doc, | 405 | d->doc, |
392 | visibleRange_DocumentWidget_(d), | 406 | visibleRange_DocumentWidget_(d), |
393 | drawRun_DrawContext_, | 407 | drawRun_DrawContext_, |
394 | &ctx); | 408 | &ctx); |
409 | draw_Widget(w); | ||
395 | } | 410 | } |
396 | 411 | ||
397 | iBeginDefineSubclass(DocumentWidget, Widget) | 412 | iBeginDefineSubclass(DocumentWidget, Widget) |
diff --git a/src/ui/scrollwidget.c b/src/ui/scrollwidget.c new file mode 100644 index 00000000..cbfd2e70 --- /dev/null +++ b/src/ui/scrollwidget.c | |||
@@ -0,0 +1,79 @@ | |||
1 | #include "scrollwidget.h" | ||
2 | #include "paint.h" | ||
3 | |||
4 | iDefineObjectConstruction(ScrollWidget) | ||
5 | |||
6 | struct Impl_ScrollWidget { | ||
7 | iWidget widget; | ||
8 | iRangei range; | ||
9 | int thumb; | ||
10 | int thumbSize; | ||
11 | }; | ||
12 | |||
13 | void init_ScrollWidget(iScrollWidget *d) { | ||
14 | iWidget *w = as_Widget(d); | ||
15 | init_Widget(w); | ||
16 | setId_Widget(w, "scroll"); | ||
17 | setFlags_Widget(w, | ||
18 | fixedWidth_WidgetFlag | resizeToParentHeight_WidgetFlag | | ||
19 | moveToParentRightEdge_WidgetFlag, | ||
20 | iTrue); | ||
21 | w->rect.size.x = gap_UI * 3; | ||
22 | } | ||
23 | |||
24 | void deinit_ScrollWidget(iScrollWidget *d) { | ||
25 | iUnused(d); | ||
26 | } | ||
27 | |||
28 | static iRect thumbRect_ScrollWidget_(const iScrollWidget *d) { | ||
29 | const iRect bounds = bounds_Widget(constAs_Widget(d)); | ||
30 | iRect rect = init_Rect(bounds.pos.x, bounds.pos.y, bounds.size.x, 0); | ||
31 | 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) { | ||
34 | const int tsize = iMax(gap_UI * 6, d->thumbSize); | ||
35 | const int tpos = | ||
36 | iClamp((float) d->thumb / (float) total, 0, 1) * (height_Rect(bounds) - tsize); | ||
37 | rect.pos.y = tpos; | ||
38 | rect.size.y = tsize; | ||
39 | } | ||
40 | return rect; | ||
41 | } | ||
42 | |||
43 | static void checkVisible_ScrollWidget_(iScrollWidget *d) { | ||
44 | setFlags_Widget(as_Widget(d), hidden_WidgetFlag, height_Rect(thumbRect_ScrollWidget_(d)) == 0); | ||
45 | } | ||
46 | |||
47 | void setRange_ScrollWidget(iScrollWidget *d, iRangei range) { | ||
48 | range.end = iMax(range.start, range.end); | ||
49 | d->range = range; | ||
50 | checkVisible_ScrollWidget_(d); | ||
51 | } | ||
52 | |||
53 | void setThumb_ScrollWidget(iScrollWidget *d, int thumb, int thumbSize) { | ||
54 | d->thumb = thumb; | ||
55 | d->thumbSize = thumbSize; | ||
56 | checkVisible_ScrollWidget_(d); | ||
57 | } | ||
58 | |||
59 | static iBool processEvent_ScrollWidget_(iScrollWidget *d, const SDL_Event *ev) { | ||
60 | iWidget *w = as_Widget(d); | ||
61 | return processEvent_Widget(w, ev); | ||
62 | } | ||
63 | |||
64 | static void draw_ScrollWidget_(const iScrollWidget *d) { | ||
65 | const iWidget *w = constAs_Widget(d); | ||
66 | const iRect bounds = bounds_Widget(w); | ||
67 | if (bounds.size.x > 0) { | ||
68 | iPaint p; | ||
69 | init_Paint(&p); | ||
70 | drawRect_Paint(&p, bounds, black_ColorId); | ||
71 | iRect tr = thumbRect_ScrollWidget_(d); | ||
72 | fillRect_Paint(&p, thumbRect_ScrollWidget_(d), gray50_ColorId); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | iBeginDefineSubclass(ScrollWidget, Widget) | ||
77 | .processEvent = (iAny *) processEvent_ScrollWidget_, | ||
78 | .draw = (iAny *) draw_ScrollWidget_, | ||
79 | iEndDefineSubclass(ScrollWidget) | ||
diff --git a/src/ui/scrollwidget.h b/src/ui/scrollwidget.h new file mode 100644 index 00000000..7b44dced --- /dev/null +++ b/src/ui/scrollwidget.h | |||
@@ -0,0 +1,11 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "widget.h" | ||
4 | #include <the_Foundation/range.h> | ||
5 | |||
6 | iDeclareWidgetClass(ScrollWidget) | ||
7 | iDeclareObjectConstruction(ScrollWidget) | ||
8 | |||
9 | void setRange_ScrollWidget (iScrollWidget *, iRangei range); | ||
10 | void setThumb_ScrollWidget (iScrollWidget *, int thumb, int thumbSize); | ||
11 | |||
diff --git a/src/ui/widget.c b/src/ui/widget.c index 49fcd7c0..5c23860f 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -150,6 +150,9 @@ static void setHeight_Widget_(iWidget *d, int height) { | |||
150 | } | 150 | } |
151 | 151 | ||
152 | void arrange_Widget(iWidget *d) { | 152 | void arrange_Widget(iWidget *d) { |
153 | if (d->flags & moveToParentRightEdge_WidgetFlag) { | ||
154 | d->rect.pos.x = width_Rect(d->parent->rect) - width_Rect(d->rect); | ||
155 | } | ||
153 | if (d->flags & resizeToParentWidth_WidgetFlag) { | 156 | if (d->flags & resizeToParentWidth_WidgetFlag) { |
154 | setWidth_Widget_(d, d->parent->rect.size.x); | 157 | setWidth_Widget_(d, d->parent->rect.size.x); |
155 | } | 158 | } |
@@ -250,7 +253,8 @@ void arrange_Widget(iWidget *d) { | |||
250 | /* Parent size changed, must update the children.*/ | 253 | /* Parent size changed, must update the children.*/ |
251 | iForEach(ObjectList, j, d->children) { | 254 | iForEach(ObjectList, j, d->children) { |
252 | iWidget *child = as_Widget(j.object); | 255 | iWidget *child = as_Widget(j.object); |
253 | if (child->flags & resizeToParentWidth_WidgetFlag) { | 256 | if (child->flags & |
257 | (resizeToParentWidth_WidgetFlag | moveToParentRightEdge_WidgetFlag)) { | ||
254 | arrange_Widget(child); | 258 | arrange_Widget(child); |
255 | } | 259 | } |
256 | } | 260 | } |
diff --git a/src/ui/widget.h b/src/ui/widget.h index bf5c22f1..2d13fe78 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h | |||
@@ -46,6 +46,7 @@ enum iWidgetFlag { | |||
46 | resizeChildrenToWidestChild_WidgetFlag = iBit(25), | 46 | resizeChildrenToWidestChild_WidgetFlag = iBit(25), |
47 | resizeToParentWidth_WidgetFlag = iBit(26), | 47 | resizeToParentWidth_WidgetFlag = iBit(26), |
48 | resizeToParentHeight_WidgetFlag = iBit(27), | 48 | resizeToParentHeight_WidgetFlag = iBit(27), |
49 | moveToParentRightEdge_WidgetFlag = iBit(28), | ||
49 | }; | 50 | }; |
50 | 51 | ||
51 | enum iWidgetAddPos { | 52 | enum iWidgetAddPos { |