diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-28 18:31:42 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-28 18:31:42 +0200 |
commit | 646dca0a709712e0e4ae9396a2cbd8c159d61668 (patch) | |
tree | f33169a08e303d7662624d3a2252a3026c379cc4 | |
parent | 61cfbe8d9b00616f7b4e70f050a9dc0f54e93348 (diff) |
DocumentWidget: Current heading on the side
Redraw the side icon and heading when the current top heading changes.
-rw-r--r-- | src/ui/documentwidget.c | 135 |
1 files changed, 82 insertions, 53 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index d1381a3b..07b0cc63 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -143,7 +143,8 @@ struct Impl_OutlineItem { | |||
143 | 143 | ||
144 | /*----------------------------------------------------------------------------------------------*/ | 144 | /*----------------------------------------------------------------------------------------------*/ |
145 | 145 | ||
146 | static void animatePlayers_DocumentWidget_(iDocumentWidget *d); | 146 | static void animatePlayers_DocumentWidget_ (iDocumentWidget *d); |
147 | static void updateSideIconBuf_DocumentWidget_ (iDocumentWidget *d); | ||
147 | 148 | ||
148 | static const int smoothDuration_DocumentWidget_ = 600; /* milliseconds */ | 149 | static const int smoothDuration_DocumentWidget_ = 600; /* milliseconds */ |
149 | static const int outlineMinWidth_DocumentWdiget_ = 45; /* times gap_UI */ | 150 | static const int outlineMinWidth_DocumentWdiget_ = 45; /* times gap_UI */ |
@@ -515,6 +516,24 @@ static void animatePlayers_DocumentWidget_(iDocumentWidget *d) { | |||
515 | } | 516 | } |
516 | } | 517 | } |
517 | 518 | ||
519 | static iRangecc currentHeading_DocumentWidget_(const iDocumentWidget *d) { | ||
520 | iRangecc heading = iNullRange; | ||
521 | if (d->firstVisibleRun) { | ||
522 | iConstForEach(Array, i, headings_GmDocument(d->doc)) { | ||
523 | const iGmHeading *head = i.value; | ||
524 | if (head->level == 0) { | ||
525 | if (head->text.start <= d->firstVisibleRun->text.start) { | ||
526 | heading = head->text; | ||
527 | } | ||
528 | if (d->lastVisibleRun && head->text.start > d->lastVisibleRun->text.start) { | ||
529 | break; | ||
530 | } | ||
531 | } | ||
532 | } | ||
533 | } | ||
534 | return heading; | ||
535 | } | ||
536 | |||
518 | static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | 537 | static void updateVisible_DocumentWidget_(iDocumentWidget *d) { |
519 | const iRangei visRange = visibleRange_DocumentWidget_(d); | 538 | const iRangei visRange = visibleRange_DocumentWidget_(d); |
520 | const iRect bounds = bounds_Widget(as_Widget(d)); | 539 | const iRect bounds = bounds_Widget(as_Widget(d)); |
@@ -525,8 +544,15 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | |||
525 | docSize > 0 ? height_Rect(bounds) * size_Range(&visRange) / docSize : 0); | 544 | docSize > 0 ? height_Rect(bounds) * size_Range(&visRange) / docSize : 0); |
526 | clear_PtrArray(&d->visibleLinks); | 545 | clear_PtrArray(&d->visibleLinks); |
527 | clear_PtrArray(&d->visiblePlayers); | 546 | clear_PtrArray(&d->visiblePlayers); |
528 | d->firstVisibleRun = NULL; | 547 | const iRangecc oldHeading = currentHeading_DocumentWidget_(d); |
529 | render_GmDocument(d->doc, visRange, addVisible_DocumentWidget_, d); | 548 | /* Scan for visible runs. */ { |
549 | d->firstVisibleRun = NULL; | ||
550 | render_GmDocument(d->doc, visRange, addVisible_DocumentWidget_, d); | ||
551 | } | ||
552 | const iRangecc newHeading = currentHeading_DocumentWidget_(d); | ||
553 | if (memcmp(&oldHeading, &newHeading, sizeof(oldHeading))) { | ||
554 | updateSideIconBuf_DocumentWidget_(d); | ||
555 | } | ||
530 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window())); | 556 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window())); |
531 | updateSideOpacity_DocumentWidget_(d, iTrue); | 557 | updateSideOpacity_DocumentWidget_(d, iTrue); |
532 | animatePlayers_DocumentWidget_(d); | 558 | animatePlayers_DocumentWidget_(d); |
@@ -682,6 +708,7 @@ static void setSource_DocumentWidget_(iDocumentWidget *d, const iString *source) | |||
682 | setValue_Anim(&d->outlineOpacity, 0.0f, 0); | 708 | setValue_Anim(&d->outlineOpacity, 0.0f, 0); |
683 | updateWindowTitle_DocumentWidget_(d); | 709 | updateWindowTitle_DocumentWidget_(d); |
684 | updateVisible_DocumentWidget_(d); | 710 | updateVisible_DocumentWidget_(d); |
711 | updateSideIconBuf_DocumentWidget_(d); | ||
685 | updateOutline_DocumentWidget_(d); | 712 | updateOutline_DocumentWidget_(d); |
686 | invalidate_DocumentWidget_(d); | 713 | invalidate_DocumentWidget_(d); |
687 | refresh_Widget(as_Widget(d)); | 714 | refresh_Widget(as_Widget(d)); |
@@ -938,6 +965,7 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { | |||
938 | init_Anim(&d->scrollY, d->initNormScrollY * size_GmDocument(d->doc).y); | 965 | init_Anim(&d->scrollY, d->initNormScrollY * size_GmDocument(d->doc).y); |
939 | d->state = ready_RequestState; | 966 | d->state = ready_RequestState; |
940 | updateSideOpacity_DocumentWidget_(d, iFalse); | 967 | updateSideOpacity_DocumentWidget_(d, iFalse); |
968 | updateSideIconBuf_DocumentWidget_(d); | ||
941 | updateOutline_DocumentWidget_(d); | 969 | updateOutline_DocumentWidget_(d); |
942 | updateVisible_DocumentWidget_(d); | 970 | updateVisible_DocumentWidget_(d); |
943 | postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | 971 | postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); |
@@ -1227,11 +1255,12 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1227 | scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y, iTrue); | 1255 | scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y, iTrue); |
1228 | } | 1256 | } |
1229 | } | 1257 | } |
1258 | updateSideIconBuf_DocumentWidget_(d); | ||
1230 | updateOutline_DocumentWidget_(d); | 1259 | updateOutline_DocumentWidget_(d); |
1231 | invalidate_DocumentWidget_(d); | 1260 | invalidate_DocumentWidget_(d); |
1232 | dealloc_VisBuf(d->visBuf); | 1261 | dealloc_VisBuf(d->visBuf); |
1233 | refresh_Widget(w); | ||
1234 | updateWindowTitle_DocumentWidget_(d); | 1262 | updateWindowTitle_DocumentWidget_(d); |
1263 | refresh_Widget(w); | ||
1235 | } | 1264 | } |
1236 | else if (equal_Command(cmd, "window.mouse.exited")) { | 1265 | else if (equal_Command(cmd, "window.mouse.exited")) { |
1237 | updateOutlineOpacity_DocumentWidget_(d); | 1266 | updateOutlineOpacity_DocumentWidget_(d); |
@@ -1239,6 +1268,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1239 | } | 1268 | } |
1240 | else if (equal_Command(cmd, "theme.changed") && document_App() == d) { | 1269 | else if (equal_Command(cmd, "theme.changed") && document_App() == d) { |
1241 | updateTheme_DocumentWidget_(d); | 1270 | updateTheme_DocumentWidget_(d); |
1271 | updateSideIconBuf_DocumentWidget_(d); | ||
1242 | invalidate_DocumentWidget_(d); | 1272 | invalidate_DocumentWidget_(d); |
1243 | refresh_Widget(w); | 1273 | refresh_Widget(w); |
1244 | } | 1274 | } |
@@ -1361,6 +1391,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1361 | } | 1391 | } |
1362 | iReleasePtr(&d->request); | 1392 | iReleasePtr(&d->request); |
1363 | updateVisible_DocumentWidget_(d); | 1393 | updateVisible_DocumentWidget_(d); |
1394 | updateSideIconBuf_DocumentWidget_(d); | ||
1364 | updateOutline_DocumentWidget_(d); | 1395 | updateOutline_DocumentWidget_(d); |
1365 | postCommandf_App("document.changed url:%s", cstr_String(d->mod.url)); | 1396 | postCommandf_App("document.changed url:%s", cstr_String(d->mod.url)); |
1366 | return iFalse; | 1397 | return iFalse; |
@@ -2324,51 +2355,42 @@ static int drawSideRect_(iPaint *p, iRect rect) { | |||
2324 | return fg; | 2355 | return fg; |
2325 | } | 2356 | } |
2326 | 2357 | ||
2327 | static iRangecc currentHeading_DocumentWidget_(const iDocumentWidget *d) { | ||
2328 | iRangecc heading = iNullRange; | ||
2329 | if (d->firstVisibleRun) { | ||
2330 | iConstForEach(Array, i, headings_GmDocument(d->doc)) { | ||
2331 | const iGmHeading *head = i.value; | ||
2332 | if (head->level == 0) { | ||
2333 | if (head->text.start <= d->firstVisibleRun->text.start) { | ||
2334 | heading = head->text; | ||
2335 | } | ||
2336 | if (d->lastVisibleRun && head->text.start > d->lastVisibleRun->text.start) { | ||
2337 | break; | ||
2338 | } | ||
2339 | } | ||
2340 | } | ||
2341 | } | ||
2342 | return heading; | ||
2343 | } | ||
2344 | |||
2345 | static int sideElementAvailWidth_DocumentWidget_(const iDocumentWidget *d) { | 2358 | static int sideElementAvailWidth_DocumentWidget_(const iDocumentWidget *d) { |
2346 | return left_Rect(documentBounds_DocumentWidget_(d)) - | 2359 | return left_Rect(documentBounds_DocumentWidget_(d)) - |
2347 | left_Rect(bounds_Widget(constAs_Widget(d))) - 2 * d->pageMargin * gap_UI; | 2360 | left_Rect(bounds_Widget(constAs_Widget(d))) - 2 * d->pageMargin * gap_UI; |
2348 | } | 2361 | } |
2349 | 2362 | ||
2350 | #if 0 | 2363 | static iBool isSideHeadingVisible_DocumentWidget_(const iDocumentWidget *d) { |
2364 | return sideElementAvailWidth_DocumentWidget_(d) >= lineHeight_Text(banner_FontId) * 4.5f; | ||
2365 | } | ||
2366 | |||
2351 | static void updateSideIconBuf_DocumentWidget_(iDocumentWidget *d) { | 2367 | static void updateSideIconBuf_DocumentWidget_(iDocumentWidget *d) { |
2352 | if (d->sideIconBuf) { | 2368 | if (d->sideIconBuf) { |
2353 | SDL_DestroyTexture(d->sideIconBuf); | 2369 | SDL_DestroyTexture(d->sideIconBuf); |
2354 | d->sideIconBuf = NULL; | 2370 | d->sideIconBuf = NULL; |
2355 | } | 2371 | } |
2356 | const iWidget *w = constAs_Widget(d); | 2372 | const iGmRun *banner = siteBanner_GmDocument(d->doc); |
2357 | const iRect bounds = bounds_Widget(w); | 2373 | if (!banner) { |
2358 | const int margin = gap_UI * d->pageMargin; | 2374 | return; |
2359 | const iGmRun * banner = siteBanner_GmDocument(d->doc); | 2375 | } |
2360 | const int minBannerSize = lineHeight_Text(banner_FontId) * 2; | 2376 | const int margin = gap_UI * d->pageMargin; |
2361 | const iChar icon = siteIcon_GmDocument(d->doc); | 2377 | const int minBannerSize = lineHeight_Text(banner_FontId) * 2; |
2362 | const int avail = sideElementAvailWidth_DocumentWidget_(d) - margin; | 2378 | const iChar icon = siteIcon_GmDocument(d->doc); |
2379 | const int avail = sideElementAvailWidth_DocumentWidget_(d) - margin; | ||
2380 | iBool isHeadingVisible = isSideHeadingVisible_DocumentWidget_(d); | ||
2363 | /* Determine the required size. */ | 2381 | /* Determine the required size. */ |
2364 | iInt2 bufSize = init1_I2(minBannerSize); | 2382 | iInt2 bufSize = init1_I2(minBannerSize); |
2365 | const iInt2 headingSize = advanceWrapRange_Text(heading3_FontId, avail, currentHeading_DocumentWidget_(d)); | 2383 | if (isHeadingVisible) { |
2366 | bufSize.y += gap_Text + headingSize.y; | 2384 | const iInt2 headingSize = advanceWrapRange_Text(heading3_FontId, avail, |
2367 | bufSize.x = iMax(bufSize.x, headingSize.x); | 2385 | currentHeading_DocumentWidget_(d)); |
2368 | 2386 | if (headingSize.x > 0) { | |
2369 | // iRect rect = { add_I2(topLeft_Rect(bounds), init1_I2(margin)), init1_I2(minBannerSize) }; | 2387 | bufSize.y += gap_Text + headingSize.y; |
2370 | // p.alpha = opacity * 255; | 2388 | bufSize.x = iMax(bufSize.x, headingSize.x); |
2371 | // rect.pos.y += height_Rect(bounds) / 2 - rect.size.y / 2 - (banner ? banner->visBounds.size.y / 2 : 0); | 2389 | } |
2390 | else { | ||
2391 | isHeadingVisible = iFalse; | ||
2392 | } | ||
2393 | } | ||
2372 | d->sideIconBuf = SDL_CreateTexture(renderer_Window(get_Window()), | 2394 | d->sideIconBuf = SDL_CreateTexture(renderer_Window(get_Window()), |
2373 | SDL_PIXELFORMAT_RGBA4444, | 2395 | SDL_PIXELFORMAT_RGBA4444, |
2374 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, | 2396 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, |
@@ -2376,21 +2398,21 @@ static void updateSideIconBuf_DocumentWidget_(iDocumentWidget *d) { | |||
2376 | iPaint p; | 2398 | iPaint p; |
2377 | init_Paint(&p); | 2399 | init_Paint(&p); |
2378 | beginTarget_Paint(&p, d->sideIconBuf); | 2400 | beginTarget_Paint(&p, d->sideIconBuf); |
2379 | fillRect_Paint(&p, ) | 2401 | const iRect iconRect = { zero_I2(), init1_I2(minBannerSize) }; |
2380 | int fg = drawSideRect_(&p, rect); | 2402 | int fg = drawSideRect_(&p, iconRect); |
2381 | iString str; | 2403 | iString str; |
2382 | initUnicodeN_String(&str, &icon, 1); | 2404 | initUnicodeN_String(&str, &icon, 1); |
2383 | drawCentered_Text(banner_FontId, rect, iTrue, fg, "%s", cstr_String(&str)); | 2405 | drawCentered_Text(banner_FontId, iconRect, iTrue, fg, "%s", cstr_String(&str)); |
2384 | deinit_String(&str); | 2406 | deinit_String(&str); |
2385 | if (avail >= minBannerSize * 2.25f) { | 2407 | if (isHeadingVisible) { |
2386 | iRangecc text = currentHeading_DocumentWidget_(d); | 2408 | iRangecc text = currentHeading_DocumentWidget_(d); |
2387 | iInt2 pos = addY_I2(bottomLeft_Rect(rect), gap_Text); | 2409 | iInt2 pos = addY_I2(bottomLeft_Rect(iconRect), gap_Text); |
2388 | const int font = heading3_FontId; | 2410 | const int font = heading3_FontId; |
2389 | drawWrapRange_Text(font, pos, avail - margin, tmBannerSideTitle_ColorId, text); | 2411 | drawWrapRange_Text(font, pos, avail, tmBannerSideTitle_ColorId, text); |
2390 | } | 2412 | } |
2391 | endTarget_Paint(&p); | 2413 | endTarget_Paint(&p); |
2414 | SDL_SetTextureBlendMode(d->sideIconBuf, SDL_BLENDMODE_BLEND); | ||
2392 | } | 2415 | } |
2393 | #endif | ||
2394 | 2416 | ||
2395 | static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | 2417 | static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { |
2396 | const iWidget *w = constAs_Widget(d); | 2418 | const iWidget *w = constAs_Widget(d); |
@@ -2402,16 +2424,22 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | |||
2402 | iPaint p; | 2424 | iPaint p; |
2403 | init_Paint(&p); | 2425 | init_Paint(&p); |
2404 | setClip_Paint(&p, bounds); | 2426 | setClip_Paint(&p, bounds); |
2405 | #if 0 | 2427 | /* Side icon and current heading. */ |
2406 | if (prefs_App()->sideIcon && opacity > 0 && d->sideIconBuf && avail > size_SDLTexture(d->sideIconBuf).x) { | 2428 | if (prefs_App()->sideIcon && opacity > 0 && d->sideIconBuf) { |
2407 | if (banner) { | 2429 | const iInt2 texSize = size_SDLTexture(d->sideIconBuf); |
2408 | setOpacity_Text(opacity); | 2430 | if (avail > texSize.x) { |
2409 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); | 2431 | const int minBannerSize = lineHeight_Text(banner_FontId) * 2; |
2410 | setOpacity_Text(1.0f); | 2432 | iInt2 pos = addY_I2(add_I2(topLeft_Rect(bounds), init_I2(margin, 0)), |
2411 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | 2433 | height_Rect(bounds) / 2 - minBannerSize / 2 - |
2434 | (texSize.y > minBannerSize | ||
2435 | ? (gap_Text + lineHeight_Text(heading3_FontId)) / 2 | ||
2436 | : 0)); | ||
2437 | SDL_SetTextureAlphaMod(d->sideIconBuf, 255 * opacity); | ||
2438 | SDL_RenderCopy(renderer_Window(get_Window()), | ||
2439 | d->sideIconBuf, NULL, | ||
2440 | &(SDL_Rect){ pos.x, pos.y, texSize.x, texSize.y }); | ||
2412 | } | 2441 | } |
2413 | } | 2442 | } |
2414 | #endif | ||
2415 | /* Reception timestamp. */ | 2443 | /* Reception timestamp. */ |
2416 | if (d->timestampBuf && d->timestampBuf->size.x <= avail) { | 2444 | if (d->timestampBuf && d->timestampBuf->size.x <= avail) { |
2417 | draw_TextBuf( | 2445 | draw_TextBuf( |
@@ -2479,7 +2507,7 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | |||
2479 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | 2507 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); |
2480 | } | 2508 | } |
2481 | unsetClip_Paint(&p); | 2509 | unsetClip_Paint(&p); |
2482 | } | 2510 | } |
2483 | 2511 | ||
2484 | static void drawPlayers_DocumentWidget_(const iDocumentWidget *d, iPaint *p) { | 2512 | static void drawPlayers_DocumentWidget_(const iDocumentWidget *d, iPaint *p) { |
2485 | iConstForEach(PtrArray, i, &d->visiblePlayers) { | 2513 | iConstForEach(PtrArray, i, &d->visiblePlayers) { |
@@ -2673,6 +2701,7 @@ iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { | |||
2673 | void updateSize_DocumentWidget(iDocumentWidget *d) { | 2701 | void updateSize_DocumentWidget(iDocumentWidget *d) { |
2674 | setWidth_GmDocument( | 2702 | setWidth_GmDocument( |
2675 | d->doc, documentWidth_DocumentWidget_(d), forceBreakWidth_DocumentWidget_(d)); | 2703 | d->doc, documentWidth_DocumentWidget_(d), forceBreakWidth_DocumentWidget_(d)); |
2704 | updateSideIconBuf_DocumentWidget_(d); | ||
2676 | updateOutline_DocumentWidget_(d); | 2705 | updateOutline_DocumentWidget_(d); |
2677 | updateVisible_DocumentWidget_(d); | 2706 | updateVisible_DocumentWidget_(d); |
2678 | invalidate_DocumentWidget_(d); | 2707 | invalidate_DocumentWidget_(d); |