diff options
-rw-r--r-- | src/gmdocument.c | 98 | ||||
-rw-r--r-- | src/gmdocument.h | 27 | ||||
-rw-r--r-- | src/ui/color.h | 6 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 23 | ||||
-rw-r--r-- | src/ui/documentwidget.h | 10 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 2 | ||||
-rw-r--r-- | src/ui/labelwidget.c | 2 | ||||
-rw-r--r-- | src/ui/paint.c | 2 | ||||
-rw-r--r-- | src/ui/paint.h | 2 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 227 | ||||
-rw-r--r-- | src/ui/sidebarwidget.h | 3 | ||||
-rw-r--r-- | src/ui/text.h | 6 |
12 files changed, 316 insertions, 92 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c index 76b5ef22..25427391 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -86,6 +86,7 @@ struct Impl_GmDocument { | |||
86 | iArray layout; /* contents of source, laid out in document space */ | 86 | iArray layout; /* contents of source, laid out in document space */ |
87 | iPtrArray links; | 87 | iPtrArray links; |
88 | iString title; /* the first top-level title */ | 88 | iString title; /* the first top-level title */ |
89 | iArray headings; | ||
89 | iPtrArray images; /* persistent across layouts, references links by ID */ | 90 | iPtrArray images; /* persistent across layouts, references links by ID */ |
90 | uint32_t themeSeed; | 91 | uint32_t themeSeed; |
91 | iChar siteIcon; | 92 | iChar siteIcon; |
@@ -98,9 +99,9 @@ enum iGmLineType { | |||
98 | bullet_GmLineType, | 99 | bullet_GmLineType, |
99 | preformatted_GmLineType, | 100 | preformatted_GmLineType, |
100 | quote_GmLineType, | 101 | quote_GmLineType, |
101 | header1_GmLineType, | 102 | heading1_GmLineType, |
102 | header2_GmLineType, | 103 | heading2_GmLineType, |
103 | header3_GmLineType, | 104 | heading3_GmLineType, |
104 | link_GmLineType, | 105 | link_GmLineType, |
105 | max_GmLineType, | 106 | max_GmLineType, |
106 | }; | 107 | }; |
@@ -116,13 +117,13 @@ static enum iGmLineType lineType_GmDocument_(const iGmDocument *d, const iRangec | |||
116 | return link_GmLineType; | 117 | return link_GmLineType; |
117 | } | 118 | } |
118 | if (startsWith_Rangecc(line, "###")) { | 119 | if (startsWith_Rangecc(line, "###")) { |
119 | return header3_GmLineType; | 120 | return heading3_GmLineType; |
120 | } | 121 | } |
121 | if (startsWith_Rangecc(line, "##")) { | 122 | if (startsWith_Rangecc(line, "##")) { |
122 | return header2_GmLineType; | 123 | return heading2_GmLineType; |
123 | } | 124 | } |
124 | if (startsWith_Rangecc(line, "#")) { | 125 | if (startsWith_Rangecc(line, "#")) { |
125 | return header1_GmLineType; | 126 | return heading1_GmLineType; |
126 | } | 127 | } |
127 | if (startsWith_Rangecc(line, "```")) { | 128 | if (startsWith_Rangecc(line, "```")) { |
128 | return preformatted_GmLineType; | 129 | return preformatted_GmLineType; |
@@ -260,9 +261,9 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
260 | paragraph_FontId, /* bullet */ | 261 | paragraph_FontId, /* bullet */ |
261 | preformatted_FontId, | 262 | preformatted_FontId, |
262 | quote_FontId, | 263 | quote_FontId, |
263 | header1_FontId, | 264 | heading1_FontId, |
264 | header2_FontId, | 265 | heading2_FontId, |
265 | header3_FontId, | 266 | heading3_FontId, |
266 | regular_FontId, | 267 | regular_FontId, |
267 | }; | 268 | }; |
268 | static const int colors[max_GmLineType] = { | 269 | static const int colors[max_GmLineType] = { |
@@ -270,9 +271,9 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
270 | tmParagraph_ColorId, | 271 | tmParagraph_ColorId, |
271 | tmPreformatted_ColorId, | 272 | tmPreformatted_ColorId, |
272 | tmQuote_ColorId, | 273 | tmQuote_ColorId, |
273 | tmHeader1_ColorId, | 274 | tmHeading1_ColorId, |
274 | tmHeader2_ColorId, | 275 | tmHeading2_ColorId, |
275 | tmHeader3_ColorId, | 276 | tmHeading3_ColorId, |
276 | tmLinkText_ColorId, | 277 | tmLinkText_ColorId, |
277 | }; | 278 | }; |
278 | static const int indents[max_GmLineType] = { | 279 | static const int indents[max_GmLineType] = { |
@@ -291,6 +292,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
291 | const float midRunSkip = 0.1f; /* extra space between wrapped text/quote lines */ | 292 | const float midRunSkip = 0.1f; /* extra space between wrapped text/quote lines */ |
292 | clear_Array(&d->layout); | 293 | clear_Array(&d->layout); |
293 | clearLinks_GmDocument_(d); | 294 | clearLinks_GmDocument_(d); |
295 | clear_Array(&d->headings); | ||
294 | clear_String(&d->title); | 296 | clear_String(&d->title); |
295 | if (d->size.x <= 0 || isEmpty_String(&d->source)) { | 297 | if (d->size.x <= 0 || isEmpty_String(&d->source)) { |
296 | return; | 298 | return; |
@@ -345,6 +347,12 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
345 | } | 347 | } |
346 | trimLine_Rangecc_(&line, type); | 348 | trimLine_Rangecc_(&line, type); |
347 | run.font = fonts[type]; | 349 | run.font = fonts[type]; |
350 | /* Remember headings for the document outline. */ | ||
351 | if (type == heading1_GmLineType || type == heading2_GmLineType || type == heading3_GmLineType) { | ||
352 | pushBack_Array( | ||
353 | &d->headings, | ||
354 | &(iGmHeading){ .text = line, .level = type - heading1_GmLineType }); | ||
355 | } | ||
348 | } | 356 | } |
349 | else { | 357 | else { |
350 | /* Preformatted line. */ | 358 | /* Preformatted line. */ |
@@ -363,7 +371,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
363 | addSiteBanner = iFalse; | 371 | addSiteBanner = iFalse; |
364 | const iRangecc bannerText = urlHost_String(&d->url); | 372 | const iRangecc bannerText = urlHost_String(&d->url); |
365 | if (!isEmpty_Range(&bannerText)) { | 373 | if (!isEmpty_Range(&bannerText)) { |
366 | iGmRun banner = { .flags = siteBanner_GmRunFlag }; | 374 | iGmRun banner = { .flags = decoration_GmRunFlag | siteBanner_GmRunFlag }; |
367 | banner.bounds = zero_Rect(); | 375 | banner.bounds = zero_Rect(); |
368 | banner.visBounds = init_Rect(0, 0, d->size.x, lineHeight_Text(banner_FontId) * 2); | 376 | banner.visBounds = init_Rect(0, 0, d->size.x, lineHeight_Text(banner_FontId) * 2); |
369 | banner.font = banner_FontId; | 377 | banner.font = banner_FontId; |
@@ -406,33 +414,37 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
406 | } | 414 | } |
407 | } | 415 | } |
408 | /* Save the document title. */ | 416 | /* Save the document title. */ |
409 | if (type == header1_GmLineType && isEmpty_String(&d->title)) { | 417 | if (type == heading1_GmLineType && isEmpty_String(&d->title)) { |
410 | setRange_String(&d->title, line); | 418 | setRange_String(&d->title, line); |
411 | } | 419 | } |
412 | /* List bullet. */ | 420 | /* List bullet. */ |
413 | run.color = colors[type]; | 421 | run.color = colors[type]; |
414 | if (type == bullet_GmLineType) { | 422 | if (type == bullet_GmLineType) { |
415 | run.visBounds.pos = addX_I2(pos, indent * gap_Text); | 423 | iGmRun bulRun = run; |
416 | run.visBounds.size = advance_Text(run.font, bullet); | 424 | bulRun.visBounds.pos = addX_I2(pos, indent * gap_Text); |
417 | run.visBounds.pos.x -= 4 * gap_Text - width_Rect(run.visBounds) / 2; | 425 | bulRun.visBounds.size = advance_Text(run.font, bullet); |
418 | run.bounds = zero_Rect(); /* just visual */ | 426 | bulRun.visBounds.pos.x -= 4 * gap_Text - width_Rect(run.visBounds) / 2; |
419 | run.text = range_CStr(bullet); | 427 | bulRun.bounds = zero_Rect(); /* just visual */ |
420 | pushBack_Array(&d->layout, &run); | 428 | bulRun.text = range_CStr(bullet); |
429 | bulRun.flags |= decoration_GmRunFlag; | ||
430 | pushBack_Array(&d->layout, &bulRun); | ||
421 | } | 431 | } |
422 | /* Link icon. */ | 432 | /* Link icon. */ |
423 | if (type == link_GmLineType) { | 433 | if (type == link_GmLineType) { |
424 | run.visBounds.pos = pos; | 434 | iGmRun icon = run; |
425 | run.visBounds.size = init_I2(indent * gap_Text, lineHeight_Text(run.font)); | 435 | icon.visBounds.pos = pos; |
426 | run.bounds = zero_Rect(); /* just visual */ | 436 | icon.visBounds.size = init_I2(indent * gap_Text, lineHeight_Text(run.font)); |
437 | icon.bounds = zero_Rect(); /* just visual */ | ||
427 | const iGmLink *link = constAt_PtrArray(&d->links, run.linkId - 1); | 438 | const iGmLink *link = constAt_PtrArray(&d->links, run.linkId - 1); |
428 | run.text = range_CStr(link->flags & file_GmLinkFlag | 439 | icon.text = range_CStr(link->flags & file_GmLinkFlag |
429 | ? folder | 440 | ? folder |
430 | : link->flags & remote_GmLinkFlag ? globe : arrow); | 441 | : link->flags & remote_GmLinkFlag ? globe : arrow); |
431 | if (link->flags & remote_GmLinkFlag) { | 442 | if (link->flags & remote_GmLinkFlag) { |
432 | run.visBounds.pos.x -= gap_Text / 2; | 443 | icon.visBounds.pos.x -= gap_Text / 2; |
433 | } | 444 | } |
434 | run.color = linkColor_GmDocument(d, run.linkId, icon_GmLinkPart); | 445 | icon.color = linkColor_GmDocument(d, run.linkId, icon_GmLinkPart); |
435 | pushBack_Array(&d->layout, &run); | 446 | icon.flags |= decoration_GmRunFlag; |
447 | pushBack_Array(&d->layout, &icon); | ||
436 | } | 448 | } |
437 | run.color = colors[type]; | 449 | run.color = colors[type]; |
438 | if (d->format == plainText_GmDocumentFormat) { | 450 | if (d->format == plainText_GmDocumentFormat) { |
@@ -446,7 +458,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
446 | bigCount = 15; /* max lines -- what if the whole document is one paragraph? */ | 458 | bigCount = 15; /* max lines -- what if the whole document is one paragraph? */ |
447 | isFirstText = iFalse; | 459 | isFirstText = iFalse; |
448 | } | 460 | } |
449 | else if (type != header1_GmLineType) { | 461 | else if (type != heading1_GmLineType) { |
450 | isFirstText = iFalse; | 462 | isFirstText = iFalse; |
451 | } | 463 | } |
452 | iRangecc runLine = line; | 464 | iRangecc runLine = line; |
@@ -531,6 +543,7 @@ void init_GmDocument(iGmDocument *d) { | |||
531 | init_Array(&d->layout, sizeof(iGmRun)); | 543 | init_Array(&d->layout, sizeof(iGmRun)); |
532 | init_PtrArray(&d->links); | 544 | init_PtrArray(&d->links); |
533 | init_String(&d->title); | 545 | init_String(&d->title); |
546 | init_Array(&d->headings, sizeof(iGmHeading)); | ||
534 | init_PtrArray(&d->images); | 547 | init_PtrArray(&d->images); |
535 | setThemeSeed_GmDocument(d, NULL); | 548 | setThemeSeed_GmDocument(d, NULL); |
536 | } | 549 | } |
@@ -539,6 +552,7 @@ void deinit_GmDocument(iGmDocument *d) { | |||
539 | deinit_String(&d->title); | 552 | deinit_String(&d->title); |
540 | clearLinks_GmDocument_(d); | 553 | clearLinks_GmDocument_(d); |
541 | deinit_PtrArray(&d->links); | 554 | deinit_PtrArray(&d->links); |
555 | deinit_Array(&d->headings); | ||
542 | deinit_Array(&d->layout); | 556 | deinit_Array(&d->layout); |
543 | deinit_String(&d->localHost); | 557 | deinit_String(&d->localHost); |
544 | deinit_String(&d->url); | 558 | deinit_String(&d->url); |
@@ -553,6 +567,7 @@ void reset_GmDocument(iGmDocument *d) { | |||
553 | clear_PtrArray(&d->images); | 567 | clear_PtrArray(&d->images); |
554 | clearLinks_GmDocument_(d); | 568 | clearLinks_GmDocument_(d); |
555 | clear_Array(&d->layout); | 569 | clear_Array(&d->layout); |
570 | clear_Array(&d->headings); | ||
556 | clear_String(&d->url); | 571 | clear_String(&d->url); |
557 | clear_String(&d->localHost); | 572 | clear_String(&d->localHost); |
558 | d->themeSeed = 0; | 573 | d->themeSeed = 0; |
@@ -576,9 +591,9 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
576 | setHsl_Color(tmFirstParagraph_ColorId, addSatLum_HSLColor(base, 0, 0.75f)); | 591 | setHsl_Color(tmFirstParagraph_ColorId, addSatLum_HSLColor(base, 0, 0.75f)); |
577 | set_Color(tmQuote_ColorId, get_Color(cyan_ColorId)); | 592 | set_Color(tmQuote_ColorId, get_Color(cyan_ColorId)); |
578 | set_Color(tmPreformatted_ColorId, get_Color(cyan_ColorId)); | 593 | set_Color(tmPreformatted_ColorId, get_Color(cyan_ColorId)); |
579 | set_Color(tmHeader1_ColorId, get_Color(white_ColorId)); | 594 | set_Color(tmHeading1_ColorId, get_Color(white_ColorId)); |
580 | setHsl_Color(tmHeader2_ColorId, addSatLum_HSLColor(base, 0, 0.7f)); | 595 | setHsl_Color(tmHeading2_ColorId, addSatLum_HSLColor(base, 0, 0.7f)); |
581 | setHsl_Color(tmHeader3_ColorId, addSatLum_HSLColor(base, 0, 0.6f)); | 596 | setHsl_Color(tmHeading3_ColorId, addSatLum_HSLColor(base, 0, 0.6f)); |
582 | set_Color(tmBannerBackground_ColorId, get_Color(black_ColorId)); | 597 | set_Color(tmBannerBackground_ColorId, get_Color(black_ColorId)); |
583 | set_Color(tmBannerTitle_ColorId, get_Color(white_ColorId)); | 598 | set_Color(tmBannerTitle_ColorId, get_Color(white_ColorId)); |
584 | set_Color(tmBannerIcon_ColorId, get_Color(orange_ColorId)); | 599 | set_Color(tmBannerIcon_ColorId, get_Color(orange_ColorId)); |
@@ -652,15 +667,15 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
652 | const float altHue2 = hues[altHues[primIndex].index[altIndex[1]]]; | 667 | const float altHue2 = hues[altHues[primIndex].index[altIndex[1]]]; |
653 | iHSLColor altBase = { altHue, base.sat, base.lum, 1 }; | 668 | iHSLColor altBase = { altHue, base.sat, base.lum, 1 }; |
654 | const float titleLum = 0.2f * ((d->themeSeed >> 17) & 0x7) / 7.0f; | 669 | const float titleLum = 0.2f * ((d->themeSeed >> 17) & 0x7) / 7.0f; |
655 | setHsl_Color(tmHeader1_ColorId, setLum_HSLColor(altBase, titleLum + 0.80f)); | 670 | setHsl_Color(tmHeading1_ColorId, setLum_HSLColor(altBase, titleLum + 0.80f)); |
656 | setHsl_Color(tmHeader2_ColorId, setLum_HSLColor(altBase, titleLum + 0.70f)); | 671 | setHsl_Color(tmHeading2_ColorId, setLum_HSLColor(altBase, titleLum + 0.70f)); |
657 | setHsl_Color(tmHeader3_ColorId, setLum_HSLColor(altBase, titleLum + 0.60f)); | 672 | setHsl_Color(tmHeading3_ColorId, setLum_HSLColor(altBase, titleLum + 0.60f)); |
658 | 673 | ||
659 | setHsl_Color(tmParagraph_ColorId, addSatLum_HSLColor(base, 0.1f, 0.6f)); | 674 | setHsl_Color(tmParagraph_ColorId, addSatLum_HSLColor(base, 0.1f, 0.6f)); |
660 | setHsl_Color(tmFirstParagraph_ColorId, addSatLum_HSLColor(base, 0.2f, 0.8f)); | 675 | setHsl_Color(tmFirstParagraph_ColorId, addSatLum_HSLColor(base, 0.2f, 0.8f)); |
661 | setHsl_Color(tmPreformatted_ColorId, (iHSLColor){ altHue2, 1.0f, 0.75f, 1.0f }); | 676 | setHsl_Color(tmPreformatted_ColorId, (iHSLColor){ altHue2, 1.0f, 0.75f, 1.0f }); |
662 | set_Color(tmQuote_ColorId, get_Color(tmPreformatted_ColorId)); | 677 | set_Color(tmQuote_ColorId, get_Color(tmPreformatted_ColorId)); |
663 | set_Color(tmInlineContentMetadata_ColorId, get_Color(tmHeader3_ColorId)); | 678 | set_Color(tmInlineContentMetadata_ColorId, get_Color(tmHeading3_ColorId)); |
664 | 679 | ||
665 | /* Adjust colors based on light/dark mode. */ | 680 | /* Adjust colors based on light/dark mode. */ |
666 | for (int i = tmFirst_ColorId; i < max_ColorId; i++) { | 681 | for (int i = tmFirst_ColorId; i < max_ColorId; i++) { |
@@ -675,7 +690,7 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
675 | color.sat = (color.sat + 1) / 2; | 690 | color.sat = (color.sat + 1) / 2; |
676 | color.lum += 0.06f; | 691 | color.lum += 0.06f; |
677 | } | 692 | } |
678 | else if (i == tmHeader3_ColorId) { | 693 | else if (i == tmHeading3_ColorId) { |
679 | color.lum *= 0.75f; | 694 | color.lum *= 0.75f; |
680 | } | 695 | } |
681 | else if (isLink_ColorId(i)) { | 696 | else if (isLink_ColorId(i)) { |
@@ -888,6 +903,10 @@ iBool hasSiteBanner_GmDocument(const iGmDocument *d) { | |||
888 | return (first->flags & siteBanner_GmRunFlag) != 0; | 903 | return (first->flags & siteBanner_GmRunFlag) != 0; |
889 | } | 904 | } |
890 | 905 | ||
906 | const iArray *headings_GmDocument(const iGmDocument *d) { | ||
907 | return &d->headings; | ||
908 | } | ||
909 | |||
891 | iRangecc findText_GmDocument(const iGmDocument *d, const iString *text, const char *start) { | 910 | iRangecc findText_GmDocument(const iGmDocument *d, const iString *text, const char *start) { |
892 | const char * src = constBegin_String(&d->source); | 911 | const char * src = constBegin_String(&d->source); |
893 | const size_t startPos = (start ? start - src : 0); | 912 | const size_t startPos = (start ? start - src : 0); |
@@ -934,6 +953,9 @@ const char *findLoc_GmDocument(const iGmDocument *d, iInt2 pos) { | |||
934 | const iGmRun *findRunAtLoc_GmDocument(const iGmDocument *d, const char *textCStr) { | 953 | const iGmRun *findRunAtLoc_GmDocument(const iGmDocument *d, const char *textCStr) { |
935 | iConstForEach(Array, i, &d->layout) { | 954 | iConstForEach(Array, i, &d->layout) { |
936 | const iGmRun *run = i.value; | 955 | const iGmRun *run = i.value; |
956 | if (run->flags & decoration_GmRunFlag) { | ||
957 | continue; | ||
958 | } | ||
937 | if (contains_Range(&run->text, textCStr) || run->text.start > textCStr /* went past */) { | 959 | if (contains_Range(&run->text, textCStr) || run->text.start > textCStr /* went past */) { |
938 | return run; | 960 | return run; |
939 | } | 961 | } |
diff --git a/src/gmdocument.h b/src/gmdocument.h index 0c6dd998..fd5882bf 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -1,13 +1,16 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include "gmutil.h" | 3 | #include "gmutil.h" |
4 | |||
5 | #include <the_Foundation/array.h> | ||
4 | #include <the_Foundation/object.h> | 6 | #include <the_Foundation/object.h> |
5 | #include <the_Foundation/rect.h> | 7 | #include <the_Foundation/rect.h> |
6 | #include <the_Foundation/string.h> | 8 | #include <the_Foundation/string.h> |
7 | #include <the_Foundation/time.h> | 9 | #include <the_Foundation/time.h> |
8 | |||
9 | #include <SDL_render.h> | 10 | #include <SDL_render.h> |
10 | 11 | ||
12 | iDeclareType(GmImageInfo) | ||
13 | iDeclareType(GmHeading) | ||
11 | iDeclareType(GmRun) | 14 | iDeclareType(GmRun) |
12 | 15 | ||
13 | typedef uint16_t iGmLinkId; | 16 | typedef uint16_t iGmLinkId; |
@@ -27,18 +30,22 @@ enum iGmLinkFlags { | |||
27 | visited_GmLinkFlag = iBit(14), /* in the history */ | 30 | visited_GmLinkFlag = iBit(14), /* in the history */ |
28 | }; | 31 | }; |
29 | 32 | ||
30 | iDeclareType(GmImageInfo) | ||
31 | |||
32 | struct Impl_GmImageInfo { | 33 | struct Impl_GmImageInfo { |
33 | iInt2 size; | 34 | iInt2 size; |
34 | size_t numBytes; | 35 | size_t numBytes; |
35 | const char *mime; | 36 | const char *mime; |
36 | }; | 37 | }; |
37 | 38 | ||
39 | struct Impl_GmHeading { | ||
40 | iRangecc text; | ||
41 | int level; /* 0, 1, 2 */ | ||
42 | }; | ||
43 | |||
38 | enum iGmRunFlags { | 44 | enum iGmRunFlags { |
39 | startOfLine_GmRunFlag = iBit(1), | 45 | decoration_GmRunFlag = iBit(1), /* not part of the source */ |
40 | endOfLine_GmRunFlag = iBit(2), | 46 | startOfLine_GmRunFlag = iBit(2), |
41 | siteBanner_GmRunFlag = iBit(3), /* area reserved for the site banner */ | 47 | endOfLine_GmRunFlag = iBit(3), |
48 | siteBanner_GmRunFlag = iBit(4), /* area reserved for the site banner */ | ||
42 | }; | 49 | }; |
43 | 50 | ||
44 | struct Impl_GmRun { | 51 | struct Impl_GmRun { |
@@ -74,9 +81,11 @@ void reset_GmDocument (iGmDocument *); /* free images */ | |||
74 | 81 | ||
75 | typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *); | 82 | typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *); |
76 | 83 | ||
77 | void render_GmDocument (const iGmDocument *, iRangei visRangeY, iGmDocumentRenderFunc render, void *); | 84 | void render_GmDocument (const iGmDocument *, iRangei visRangeY, |
78 | iInt2 size_GmDocument (const iGmDocument *); | 85 | iGmDocumentRenderFunc render, void *); |
79 | iBool hasSiteBanner_GmDocument(const iGmDocument *); | 86 | iInt2 size_GmDocument (const iGmDocument *); |
87 | iBool hasSiteBanner_GmDocument (const iGmDocument *); | ||
88 | const iArray * headings_GmDocument (const iGmDocument *); | ||
80 | 89 | ||
81 | iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); | 90 | iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); |
82 | iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before); | 91 | iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before); |
diff --git a/src/ui/color.h b/src/ui/color.h index 8b5ef306..6444ad03 100644 --- a/src/ui/color.h +++ b/src/ui/color.h | |||
@@ -27,9 +27,9 @@ enum iColorId { | |||
27 | tmFirstParagraph_ColorId, | 27 | tmFirstParagraph_ColorId, |
28 | tmQuote_ColorId, | 28 | tmQuote_ColorId, |
29 | tmPreformatted_ColorId, | 29 | tmPreformatted_ColorId, |
30 | tmHeader1_ColorId, | 30 | tmHeading1_ColorId, |
31 | tmHeader2_ColorId, | 31 | tmHeading2_ColorId, |
32 | tmHeader3_ColorId, | 32 | tmHeading3_ColorId, |
33 | tmBannerBackground_ColorId, | 33 | tmBannerBackground_ColorId, |
34 | tmBannerTitle_ColorId, | 34 | tmBannerTitle_ColorId, |
35 | tmBannerIcon_ColorId, | 35 | tmBannerIcon_ColorId, |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index b088fa74..de2abf0d 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -513,6 +513,10 @@ const iString *url_DocumentWidget(const iDocumentWidget *d) { | |||
513 | return d->url; | 513 | return d->url; |
514 | } | 514 | } |
515 | 515 | ||
516 | const iGmDocument *document_DocumentWidget(const iDocumentWidget *d) { | ||
517 | return d->doc; | ||
518 | } | ||
519 | |||
516 | void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { | 520 | void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { |
517 | if (cmpStringSc_String(d->url, url, &iCaseInsensitive)) { | 521 | if (cmpStringSc_String(d->url, url, &iCaseInsensitive)) { |
518 | set_String(d->url, url); | 522 | set_String(d->url, url); |
@@ -583,8 +587,9 @@ static void scroll_DocumentWidget_(iDocumentWidget *d, int offset) { | |||
583 | refresh_Widget(as_Widget(d)); | 587 | refresh_Widget(as_Widget(d)); |
584 | } | 588 | } |
585 | 589 | ||
586 | static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY) { | 590 | static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY, iBool centered) { |
587 | d->scrollY = documentY - documentBounds_DocumentWidget_(d).size.y / 2; | 591 | d->scrollY = documentY - (centered ? documentBounds_DocumentWidget_(d).size.y / 2 : |
592 | lineHeight_Text(paragraph_FontId)); | ||
588 | scroll_DocumentWidget_(d, 0); /* clamp it */ | 593 | scroll_DocumentWidget_(d, 0); /* clamp it */ |
589 | } | 594 | } |
590 | 595 | ||
@@ -781,7 +786,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
781 | if (midLoc) { | 786 | if (midLoc) { |
782 | mid = findRunAtLoc_GmDocument(d->doc, midLoc); | 787 | mid = findRunAtLoc_GmDocument(d->doc, midLoc); |
783 | if (mid) { | 788 | if (mid) { |
784 | scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y); | 789 | scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y, iTrue); |
785 | } | 790 | } |
786 | } | 791 | } |
787 | refresh_Widget(w); | 792 | refresh_Widget(w); |
@@ -923,6 +928,14 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
923 | arg_Command(cmd) * height_Rect(documentBounds_DocumentWidget_(d))); | 928 | arg_Command(cmd) * height_Rect(documentBounds_DocumentWidget_(d))); |
924 | return iTrue; | 929 | return iTrue; |
925 | } | 930 | } |
931 | else if (equal_Command(cmd, "document.goto") && document_App() == d) { | ||
932 | const char *loc = pointerLabel_Command(cmd, "loc"); | ||
933 | const iGmRun *run = findRunAtLoc_GmDocument(d->doc, loc); | ||
934 | if (run) { | ||
935 | scrollTo_DocumentWidget_(d, run->visBounds.pos.y, iFalse); | ||
936 | } | ||
937 | return iTrue; | ||
938 | } | ||
926 | else if ((equal_Command(cmd, "find.next") || equal_Command(cmd, "find.prev")) && | 939 | else if ((equal_Command(cmd, "find.next") || equal_Command(cmd, "find.prev")) && |
927 | document_App() == d) { | 940 | document_App() == d) { |
928 | const int dir = equal_Command(cmd, "find.next") ? +1 : -1; | 941 | const int dir = equal_Command(cmd, "find.next") ? +1 : -1; |
@@ -943,7 +956,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
943 | if (d->foundMark.start) { | 956 | if (d->foundMark.start) { |
944 | const iGmRun *found; | 957 | const iGmRun *found; |
945 | if ((found = findRunAtLoc_GmDocument(d->doc, d->foundMark.start)) != NULL) { | 958 | if ((found = findRunAtLoc_GmDocument(d->doc, d->foundMark.start)) != NULL) { |
946 | scrollTo_DocumentWidget_(d, mid_Rect(found->bounds).y); | 959 | scrollTo_DocumentWidget_(d, mid_Rect(found->bounds).y, iTrue); |
947 | } | 960 | } |
948 | } | 961 | } |
949 | } | 962 | } |
@@ -1353,7 +1366,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
1353 | fillRect_Paint(&ctx.paint, bounds, tmBackground_ColorId); | 1366 | fillRect_Paint(&ctx.paint, bounds, tmBackground_ColorId); |
1354 | setClip_Paint(&ctx.paint, bounds); | 1367 | setClip_Paint(&ctx.paint, bounds); |
1355 | render_GmDocument(d->doc, visibleRange_DocumentWidget_(d), drawRun_DrawContext_, &ctx); | 1368 | render_GmDocument(d->doc, visibleRange_DocumentWidget_(d), drawRun_DrawContext_, &ctx); |
1356 | clearClip_Paint(&ctx.paint); | 1369 | unsetClip_Paint(&ctx.paint); |
1357 | draw_Widget(w); | 1370 | draw_Widget(w); |
1358 | } | 1371 | } |
1359 | 1372 | ||
diff --git a/src/ui/documentwidget.h b/src/ui/documentwidget.h index 85b9a710..1e63034b 100644 --- a/src/ui/documentwidget.h +++ b/src/ui/documentwidget.h | |||
@@ -2,16 +2,18 @@ | |||
2 | 2 | ||
3 | #include "widget.h" | 3 | #include "widget.h" |
4 | 4 | ||
5 | iDeclareType(GmDocument) | ||
5 | iDeclareType(History) | 6 | iDeclareType(History) |
6 | 7 | ||
7 | iDeclareWidgetClass(DocumentWidget) | 8 | iDeclareWidgetClass(DocumentWidget) |
8 | iDeclareObjectConstruction(DocumentWidget) | 9 | iDeclareObjectConstruction(DocumentWidget) |
9 | 10 | ||
10 | iDocumentWidget *duplicate_DocumentWidget (const iDocumentWidget *); | 11 | iDocumentWidget * duplicate_DocumentWidget (const iDocumentWidget *); |
11 | iHistory * history_DocumentWidget (iDocumentWidget *); | 12 | iHistory * history_DocumentWidget (iDocumentWidget *); |
12 | 13 | ||
13 | const iString * url_DocumentWidget (const iDocumentWidget *); | 14 | const iString * url_DocumentWidget (const iDocumentWidget *); |
14 | iBool isRequestOngoing_DocumentWidget (const iDocumentWidget *); | 15 | iBool isRequestOngoing_DocumentWidget (const iDocumentWidget *); |
16 | const iGmDocument * document_DocumentWidget (const iDocumentWidget *); | ||
15 | 17 | ||
16 | void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); | 18 | void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); |
17 | void setUrlFromCache_DocumentWidget (iDocumentWidget *d, const iString *url, iBool isFromCache); | 19 | void setUrlFromCache_DocumentWidget (iDocumentWidget *d, const iString *url, iBool isFromCache); |
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index bfb71913..305b8c9b 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -335,7 +335,7 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
335 | white_ColorId, | 335 | white_ColorId, |
336 | "%s", | 336 | "%s", |
337 | cstr_String(&text)); | 337 | cstr_String(&text)); |
338 | clearClip_Paint(&p); | 338 | unsetClip_Paint(&p); |
339 | /* Cursor blinking. */ | 339 | /* Cursor blinking. */ |
340 | if (isFocused && (time & 256)) { | 340 | if (isFocused && (time & 256)) { |
341 | const iInt2 prefixSize = advanceN_Text(d->font, cstr_String(&text), d->cursor); | 341 | const iInt2 prefixSize = advanceN_Text(d->font, cstr_String(&text), d->cursor); |
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index 0ec1c5f8..0c9269d1 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c | |||
@@ -218,7 +218,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) { | |||
218 | else { | 218 | else { |
219 | drawCentered_Text(d->font, bounds, d->alignVisual, fg, cstr_String(&d->label)); | 219 | drawCentered_Text(d->font, bounds, d->alignVisual, fg, cstr_String(&d->label)); |
220 | } | 220 | } |
221 | clearClip_Paint(&p); | 221 | unsetClip_Paint(&p); |
222 | } | 222 | } |
223 | 223 | ||
224 | void updateSize_LabelWidget(iLabelWidget *d) { | 224 | void updateSize_LabelWidget(iLabelWidget *d) { |
diff --git a/src/ui/paint.c b/src/ui/paint.c index 0a2e6cd3..85e75f15 100644 --- a/src/ui/paint.c +++ b/src/ui/paint.c | |||
@@ -18,7 +18,7 @@ void setClip_Paint(iPaint *d, iRect rect) { | |||
18 | SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect); | 18 | SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect); |
19 | } | 19 | } |
20 | 20 | ||
21 | void clearClip_Paint(iPaint *d) { | 21 | void unsetClip_Paint(iPaint *d) { |
22 | const SDL_Rect winRect = { 0, 0, d->dst->root->rect.size.x, d->dst->root->rect.size.y }; | 22 | const SDL_Rect winRect = { 0, 0, d->dst->root->rect.size.x, d->dst->root->rect.size.y }; |
23 | SDL_RenderSetClipRect(renderer_Paint_(d), &winRect); | 23 | SDL_RenderSetClipRect(renderer_Paint_(d), &winRect); |
24 | } | 24 | } |
diff --git a/src/ui/paint.h b/src/ui/paint.h index 9535b142..90c5f504 100644 --- a/src/ui/paint.h +++ b/src/ui/paint.h | |||
@@ -14,7 +14,7 @@ struct Impl_Paint { | |||
14 | void init_Paint (iPaint *); | 14 | void init_Paint (iPaint *); |
15 | 15 | ||
16 | void setClip_Paint (iPaint *, iRect rect); | 16 | void setClip_Paint (iPaint *, iRect rect); |
17 | void clearClip_Paint (iPaint *); | 17 | void unsetClip_Paint (iPaint *); |
18 | 18 | ||
19 | void drawRect_Paint (const iPaint *, iRect rect, int color); | 19 | void drawRect_Paint (const iPaint *, iRect rect, int color); |
20 | void drawRectThickness_Paint (const iPaint *, iRect rect, int thickness, int color); | 20 | void drawRectThickness_Paint (const iPaint *, iRect rect, int thickness, int color); |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 8f1273bf..46b36434 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -1,57 +1,234 @@ | |||
1 | #include "sidebarwidget.h" | 1 | #include "sidebarwidget.h" |
2 | #include "labelwidget.h" | 2 | #include "labelwidget.h" |
3 | #include "scrollwidget.h" | 3 | #include "scrollwidget.h" |
4 | #include "documentwidget.h" | ||
4 | #include "paint.h" | 5 | #include "paint.h" |
5 | #include "util.h" | 6 | #include "util.h" |
7 | #include "command.h" | ||
8 | #include "../gmdocument.h" | ||
9 | #include "app.h" | ||
10 | |||
11 | #include <the_Foundation/array.h> | ||
12 | |||
13 | iDeclareType(SidebarItem) | ||
14 | |||
15 | struct Impl_SidebarItem { | ||
16 | int indent; | ||
17 | iChar icon; | ||
18 | iString label; | ||
19 | iString meta; | ||
20 | iString url; | ||
21 | size_t index; | ||
22 | }; | ||
23 | |||
24 | void init_SidebarItem(iSidebarItem *d) { | ||
25 | d->indent = 0; | ||
26 | d->icon = 0; | ||
27 | d->index = 0; | ||
28 | init_String(&d->label); | ||
29 | init_String(&d->meta); | ||
30 | init_String(&d->url); | ||
31 | } | ||
32 | |||
33 | void deinit_SidebarItem(iSidebarItem *d) { | ||
34 | deinit_String(&d->url); | ||
35 | deinit_String(&d->meta); | ||
36 | deinit_String(&d->label); | ||
37 | } | ||
38 | |||
39 | iDefineTypeConstruction(SidebarItem) | ||
40 | |||
41 | /*----------------------------------------------------------------------------------------------*/ | ||
6 | 42 | ||
7 | struct Impl_SidebarWidget { | 43 | struct Impl_SidebarWidget { |
8 | iWidget widget; | 44 | iWidget widget; |
9 | enum iSidebarMode mode; | 45 | enum iSidebarMode mode; |
10 | iScrollWidget *scroll; | 46 | iScrollWidget *scroll; |
47 | int scrollY; | ||
48 | iLabelWidget *modeButtons[max_SidebarMode]; | ||
49 | int itemHeight; | ||
50 | iArray items; | ||
51 | size_t hoverItem; | ||
52 | iClick click; | ||
11 | }; | 53 | }; |
12 | 54 | ||
13 | iDefineObjectConstruction(SidebarWidget) | 55 | iDefineObjectConstruction(SidebarWidget) |
14 | 56 | ||
57 | static void clearItems_SidebarWidget_(iSidebarWidget *d) { | ||
58 | iForEach(Array, i, &d->items) { | ||
59 | deinit_SidebarItem(i.value); | ||
60 | } | ||
61 | clear_Array(&d->items); | ||
62 | } | ||
63 | |||
64 | static void updateItems_SidebarWidget_(iSidebarWidget *d) { | ||
65 | clearItems_SidebarWidget_(d); | ||
66 | switch (d->mode) { | ||
67 | case documentOutline_SidebarMode: { | ||
68 | const iGmDocument *doc = document_DocumentWidget(document_App()); | ||
69 | iConstForEach(Array, i, headings_GmDocument(doc)) { | ||
70 | const iGmHeading *head = i.value; | ||
71 | iSidebarItem item; | ||
72 | init_SidebarItem(&item); | ||
73 | item.index = index_ArrayConstIterator(&i); | ||
74 | setRange_String(&item.label, head->text); | ||
75 | item.indent = head->level * 4 * gap_UI; | ||
76 | pushBack_Array(&d->items, &item); | ||
77 | } | ||
78 | break; | ||
79 | } | ||
80 | case bookmarks_SidebarMode: | ||
81 | break; | ||
82 | case history_SidebarMode: | ||
83 | break; | ||
84 | default: | ||
85 | break; | ||
86 | } | ||
87 | refresh_Widget(as_Widget(d)); | ||
88 | } | ||
89 | |||
90 | void setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { | ||
91 | if (d->mode == mode) return; | ||
92 | d->mode = mode; | ||
93 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { | ||
94 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); | ||
95 | } | ||
96 | const float heights[max_SidebarMode] = { 1.2f, 2, 3, 3 }; | ||
97 | d->itemHeight = heights[mode] * lineHeight_Text(default_FontId); | ||
98 | } | ||
99 | |||
15 | void init_SidebarWidget(iSidebarWidget *d) { | 100 | void init_SidebarWidget(iSidebarWidget *d) { |
16 | iWidget *w = as_Widget(d); | 101 | iWidget *w = as_Widget(d); |
17 | init_Widget(w); | 102 | init_Widget(w); |
18 | setFlags_Widget(w, resizeChildren_WidgetFlag, iTrue); | 103 | setBackgroundColor_Widget(w, none_ColorId); |
19 | d->mode = documentOutline_SidebarMode; | 104 | setFlags_Widget(w, hover_WidgetFlag | arrangeHorizontal_WidgetFlag, iTrue); |
20 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); | 105 | d->scrollY = 0; |
21 | w->rect.size.x = 80 * gap_UI; | 106 | d->mode = -1; |
107 | w->rect.size.x = 100 * gap_UI; | ||
108 | init_Array(&d->items, sizeof(iSidebarItem)); | ||
109 | d->hoverItem = iInvalidPos; | ||
110 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | ||
22 | setFlags_Widget(w, fixedWidth_WidgetFlag, iTrue); | 111 | setFlags_Widget(w, fixedWidth_WidgetFlag, iTrue); |
23 | iWidget *modeButtons = makeHDiv_Widget(); | 112 | const char *buttonLabels[max_SidebarMode] = { |
24 | setFlags_Widget(modeButtons, arrangeWidth_WidgetFlag | arrangeHeight_WidgetFlag, iTrue); | 113 | "\U0001f5b9 Outline", |
25 | addChild_Widget(w, iClob(modeButtons)); | 114 | "\U0001f588 Bookmarks", |
26 | addChildFlags_Widget( | 115 | "\U0001f553 History", |
27 | modeButtons, | 116 | "\U0001f464 Identities", |
28 | iClob(new_LabelWidget( | 117 | }; |
29 | "\U0001f5b9 Outline", 0, 0, format_CStr("sidebar.mode arg:%d", documentOutline_SidebarMode))), frameless_WidgetFlag); | 118 | for (int i = 0; i < max_SidebarMode; i++) { |
30 | addChildFlags_Widget( | 119 | d->modeButtons[i] = addChildFlags_Widget( |
31 | modeButtons, | 120 | w, |
32 | iClob(new_LabelWidget( | 121 | iClob(new_LabelWidget(buttonLabels[i], 0, 0, format_CStr("sidebar.mode arg:%d", i))), |
33 | "\U0001f588 Bookmarks", 0, 0, format_CStr("sidebar.mode arg:%d", bookmarks_SidebarMode))), frameless_WidgetFlag); | 122 | frameless_WidgetFlag); |
34 | addChildFlags_Widget( | 123 | } |
35 | modeButtons, | 124 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); |
36 | iClob(new_LabelWidget( | 125 | setThumb_ScrollWidget(d->scroll, 0, 0); |
37 | "\U0001f553 History", 0, 0, format_CStr("sidebar.mode arg:%d", history_SidebarMode))), frameless_WidgetFlag); | 126 | setMode_SidebarWidget(d, documentOutline_SidebarMode); |
38 | addChildFlags_Widget( | ||
39 | modeButtons, | ||
40 | iClob(new_LabelWidget( | ||
41 | "\U0001f464 Identities", 0, 0, format_CStr("sidebar.mode arg:%d", identities_SidebarMode))), frameless_WidgetFlag); | ||
42 | } | 127 | } |
43 | 128 | ||
44 | void deinit_SidebarWidget(iSidebarWidget *d) { | 129 | void deinit_SidebarWidget(iSidebarWidget *d) { |
45 | iUnused(d); | 130 | clearItems_SidebarWidget_(d); |
131 | deinit_Array(&d->items); | ||
132 | } | ||
133 | |||
134 | static iRect contentBounds_SidebarWidget_(const iSidebarWidget *d) { | ||
135 | iRect bounds = bounds_Widget(constAs_Widget(d)); | ||
136 | adjustEdges_Rect(&bounds, as_Widget(d->modeButtons[0])->rect.size.y + gap_UI, 0, 0, 0); | ||
137 | return bounds; | ||
138 | } | ||
139 | |||
140 | static size_t itemIndex_SidebarWidget_(const iSidebarWidget *d, iInt2 pos) { | ||
141 | const iRect bounds = contentBounds_SidebarWidget_(d); | ||
142 | pos.y -= top_Rect(bounds); | ||
143 | if (pos.y < 0) return iInvalidPos; | ||
144 | size_t index = pos.y / d->itemHeight; | ||
145 | if (index >= size_Array(&d->items)) return iInvalidPos; | ||
146 | return index; | ||
147 | } | ||
148 | |||
149 | static void itemClicked_SidebarWidget_(iSidebarWidget *d, size_t index) { | ||
150 | const iSidebarItem *item = constAt_Array(&d->items, index); | ||
151 | switch (d->mode) { | ||
152 | case documentOutline_SidebarMode: { | ||
153 | const iGmDocument *doc = document_DocumentWidget(document_App()); | ||
154 | const iGmHeading *head = constAt_Array(headings_GmDocument(doc), item->index); | ||
155 | postCommandf_App("document.goto loc:%p", head->text.start); | ||
156 | break; | ||
157 | } | ||
158 | } | ||
46 | } | 159 | } |
47 | 160 | ||
48 | static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { | 161 | static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { |
49 | iWidget *w = as_Widget(d); | 162 | iWidget *w = as_Widget(d); |
163 | /* Handle commands. */ | ||
164 | if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { | ||
165 | const char *cmd = command_UserEvent(ev); | ||
166 | if (isCommand_Widget(w, ev, "sidebar.mode")) { | ||
167 | setMode_SidebarWidget(d, arg_Command(cmd)); | ||
168 | updateItems_SidebarWidget_(d); | ||
169 | return iTrue; | ||
170 | } | ||
171 | else if (equal_Command(cmd, "tabs.changed") || equal_Command(cmd, "document.changed")) { | ||
172 | updateItems_SidebarWidget_(d); | ||
173 | } | ||
174 | } | ||
175 | if (ev->type == SDL_MOUSEMOTION) { | ||
176 | const iInt2 mouse = init_I2(ev->motion.x, ev->motion.y); | ||
177 | size_t hover = iInvalidPos; | ||
178 | if (contains_Widget(w, mouse)) { | ||
179 | hover = itemIndex_SidebarWidget_(d, mouse); | ||
180 | |||
181 | } | ||
182 | if (hover != d->hoverItem) { | ||
183 | d->hoverItem = hover; | ||
184 | refresh_Widget(w); | ||
185 | } | ||
186 | } | ||
187 | switch (processEvent_Click(&d->click, ev)) { | ||
188 | case started_ClickResult: | ||
189 | refresh_Widget(w); | ||
190 | break; | ||
191 | case finished_ClickResult: | ||
192 | if (contains_Rect(contentBounds_SidebarWidget_(d), pos_Click(&d->click)) && | ||
193 | d->hoverItem != iInvalidSize) { | ||
194 | //printf("click:%zu\n", d->hoverItem); fflush(stdout); | ||
195 | itemClicked_SidebarWidget_(d, d->hoverItem); | ||
196 | } | ||
197 | refresh_Widget(w); | ||
198 | break; | ||
199 | default: | ||
200 | break; | ||
201 | } | ||
50 | return processEvent_Widget(w, ev); | 202 | return processEvent_Widget(w, ev); |
51 | } | 203 | } |
52 | 204 | ||
53 | static void draw_SidebarWidget_(const iSidebarWidget *d) { | 205 | static void draw_SidebarWidget_(const iSidebarWidget *d) { |
54 | const iWidget *w = constAs_Widget(d); | 206 | const iWidget *w = constAs_Widget(d); |
207 | const iRect bounds = contentBounds_SidebarWidget_(d); | ||
208 | const iBool isPressing = d->click.isActive && contains_Rect(bounds, pos_Click(&d->click)); | ||
209 | iPaint p; | ||
210 | init_Paint(&p); | ||
211 | /* Draw the items. */ { | ||
212 | iInt2 pos = topLeft_Rect(bounds); | ||
213 | const int font = default_FontId; | ||
214 | iConstForEach(Array, i, &d->items) { | ||
215 | const iSidebarItem *item = i.value; | ||
216 | const iRect itemRect = { pos, init_I2(width_Rect(bounds), d->itemHeight) }; | ||
217 | const iBool isHover = (d->hoverItem == index_ArrayConstIterator(&i)); | ||
218 | if (isHover) { | ||
219 | fillRect_Paint(&p, itemRect, isPressing ? orange_ColorId : teal_ColorId); | ||
220 | } | ||
221 | setClip_Paint(&p, itemRect); | ||
222 | const int fg = isHover ? (isPressing ? black_ColorId : white_ColorId) : gray75_ColorId; | ||
223 | if (d->mode == documentOutline_SidebarMode) { | ||
224 | drawRange_Text(font, init_I2(pos.x + 3 * gap_UI + item->indent, | ||
225 | mid_Rect(itemRect).y - lineHeight_Text(font) / 2), | ||
226 | fg, range_String(&item->label)); | ||
227 | } | ||
228 | unsetClip_Paint(&p); | ||
229 | pos.y += d->itemHeight; | ||
230 | } | ||
231 | } | ||
55 | draw_Widget(w); | 232 | draw_Widget(w); |
56 | } | 233 | } |
57 | 234 | ||
diff --git a/src/ui/sidebarwidget.h b/src/ui/sidebarwidget.h index 3fd956a4..80f22fca 100644 --- a/src/ui/sidebarwidget.h +++ b/src/ui/sidebarwidget.h | |||
@@ -3,10 +3,11 @@ | |||
3 | #include "widget.h" | 3 | #include "widget.h" |
4 | 4 | ||
5 | enum iSidebarMode { | 5 | enum iSidebarMode { |
6 | documentOutline_SidebarMode, | ||
6 | bookmarks_SidebarMode, | 7 | bookmarks_SidebarMode, |
7 | history_SidebarMode, | 8 | history_SidebarMode, |
8 | documentOutline_SidebarMode, | ||
9 | identities_SidebarMode, | 9 | identities_SidebarMode, |
10 | max_SidebarMode | ||
10 | }; | 11 | }; |
11 | 12 | ||
12 | iDeclareWidgetClass(SidebarWidget) | 13 | iDeclareWidgetClass(SidebarWidget) |
diff --git a/src/ui/text.h b/src/ui/text.h index 713ee1e7..864173e0 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -44,9 +44,9 @@ enum iFontId { | |||
44 | preformatted_FontId = monospace_FontId, | 44 | preformatted_FontId = monospace_FontId, |
45 | preformattedSmall_FontId = monospaceSmall_FontId, | 45 | preformattedSmall_FontId = monospaceSmall_FontId, |
46 | quote_FontId = italic_FontId, | 46 | quote_FontId = italic_FontId, |
47 | header1_FontId = hugeBold_FontId, | 47 | heading1_FontId = hugeBold_FontId, |
48 | header2_FontId = largeBold_FontId, | 48 | heading2_FontId = largeBold_FontId, |
49 | header3_FontId = medium_FontId, | 49 | heading3_FontId = medium_FontId, |
50 | banner_FontId = largeLight_FontId, | 50 | banner_FontId = largeLight_FontId, |
51 | }; | 51 | }; |
52 | 52 | ||