diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-19 08:17:50 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-19 08:17:50 +0200 |
commit | 86ec7ac6940dd4b39a43b41e70b142fd2eda0ff3 (patch) | |
tree | 59a5e55c3ec3729bcc1588b081c91b8be9f10f14 | |
parent | 8d03b1f7479e30873e8ecee4a62478c6fc0511cb (diff) |
DocumentWidget: Refactor to separate DocumentView
Work in progress, but now DocumentView at least has its own type. The relationship is a bit muddled, though.
-rw-r--r-- | src/periodic.c | 1 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 1431 |
2 files changed, 698 insertions, 734 deletions
diff --git a/src/periodic.c b/src/periodic.c index ef3d8033..c65c8b07 100644 --- a/src/periodic.c +++ b/src/periodic.c | |||
@@ -121,6 +121,7 @@ void deinit_Periodic(iPeriodic *d) { | |||
121 | } | 121 | } |
122 | 122 | ||
123 | void add_Periodic(iPeriodic *d, iAny *context, const char *command) { | 123 | void add_Periodic(iPeriodic *d, iAny *context, const char *command) { |
124 | iAssert(isInstance_Object(context, &Class_Widget)); | ||
124 | lock_Mutex(d->mutex); | 125 | lock_Mutex(d->mutex); |
125 | size_t pos; | 126 | size_t pos; |
126 | iPeriodicCommand key = { .context = context }; | 127 | iPeriodicCommand key = { .context = context }; |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index fa035d86..f5b9a4fc 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -251,6 +251,35 @@ enum iWheelSwipeState { | |||
251 | direct_WheelSwipeState, | 251 | direct_WheelSwipeState, |
252 | }; | 252 | }; |
253 | 253 | ||
254 | /* TODO: DocumentView is supposed to be useful on its own; move to a separate source file. */ | ||
255 | iDeclareType(DocumentView) | ||
256 | |||
257 | struct Impl_DocumentView { | ||
258 | iDocumentWidget *owner; /* TODO: Convert to an abstract provider of metrics? */ | ||
259 | iGmDocument * doc; | ||
260 | int pageMargin; | ||
261 | iSmoothScroll scrollY; | ||
262 | iAnim sideOpacity; | ||
263 | iAnim altTextOpacity; | ||
264 | iGmRunRange visibleRuns; | ||
265 | iPtrArray visibleLinks; | ||
266 | iPtrArray visiblePre; | ||
267 | iPtrArray visibleMedia; /* currently playing audio / ongoing downloads */ | ||
268 | iPtrArray visibleWideRuns; /* scrollable blocks; TODO: merge into `visiblePre` */ | ||
269 | const iGmRun * hoverPre; /* for clicking */ | ||
270 | const iGmRun * hoverAltPre; /* for drawing alt text */ | ||
271 | const iGmRun * hoverLink; | ||
272 | iArray wideRunOffsets; | ||
273 | iAnim animWideRunOffset; | ||
274 | uint16_t animWideRunId; | ||
275 | iGmRunRange animWideRunRange; | ||
276 | iDrawBufs * drawBufs; /* dynamic state for drawing */ | ||
277 | iVisBuf * visBuf; | ||
278 | iVisBufMeta * visBufMeta; | ||
279 | iGmRunRange renderRuns; | ||
280 | iPtrSet * invalidRuns; | ||
281 | }; | ||
282 | |||
254 | struct Impl_DocumentWidget { | 283 | struct Impl_DocumentWidget { |
255 | iWidget widget; | 284 | iWidget widget; |
256 | int flags; /* internal behavior, see enum iDocumentWidgetFlag */ | 285 | int flags; /* internal behavior, see enum iDocumentWidgetFlag */ |
@@ -264,9 +293,6 @@ struct Impl_DocumentWidget { | |||
264 | const iGmRun * grabbedPlayer; /* currently adjusting volume in a player */ | 293 | const iGmRun * grabbedPlayer; /* currently adjusting volume in a player */ |
265 | float grabbedStartVolume; | 294 | float grabbedStartVolume; |
266 | int mediaTimer; | 295 | int mediaTimer; |
267 | const iGmRun * hoverPre; /* for clicking */ | ||
268 | const iGmRun * hoverAltPre; /* for drawing alt text */ | ||
269 | const iGmRun * hoverLink; | ||
270 | const iGmRun * contextLink; | 296 | const iGmRun * contextLink; |
271 | iClick click; | 297 | iClick click; |
272 | iInt2 contextPos; /* coordinates of latest right click */ | 298 | iInt2 contextPos; /* coordinates of latest right click */ |
@@ -299,29 +325,11 @@ struct Impl_DocumentWidget { | |||
299 | iBlock sourceContent; /* original content as received, for saving; set on request finish */ | 325 | iBlock sourceContent; /* original content as received, for saving; set on request finish */ |
300 | iTime sourceTime; | 326 | iTime sourceTime; |
301 | iGempub * sourceGempub; /* NULL unless the page is Gempub content */ | 327 | iGempub * sourceGempub; /* NULL unless the page is Gempub content */ |
302 | iGmDocument * doc; | ||
303 | iBanner * banner; | 328 | iBanner * banner; |
329 | float initNormScrollY; | ||
304 | 330 | ||
305 | /* Rendering: */ | 331 | /* Rendering: */ |
306 | int pageMargin; | 332 | iDocumentView view; |
307 | float initNormScrollY; | ||
308 | iSmoothScroll scrollY; | ||
309 | iAnim sideOpacity; | ||
310 | iAnim altTextOpacity; | ||
311 | iGmRunRange visibleRuns; | ||
312 | iPtrArray visibleLinks; | ||
313 | iPtrArray visiblePre; | ||
314 | iPtrArray visibleMedia; /* currently playing audio / ongoing downloads */ | ||
315 | iPtrArray visibleWideRuns; /* scrollable blocks; TODO: merge into `visiblePre` */ | ||
316 | iArray wideRunOffsets; | ||
317 | iAnim animWideRunOffset; | ||
318 | uint16_t animWideRunId; | ||
319 | iGmRunRange animWideRunRange; | ||
320 | iDrawBufs * drawBufs; /* dynamic state for drawing */ | ||
321 | iVisBuf * visBuf; | ||
322 | iVisBufMeta * visBufMeta; | ||
323 | iGmRunRange renderRuns; | ||
324 | iPtrSet * invalidRuns; | ||
325 | iLinkInfo * linkInfo; | 333 | iLinkInfo * linkInfo; |
326 | 334 | ||
327 | /* Widget structure: */ | 335 | /* Widget structure: */ |
@@ -338,6 +346,44 @@ iDefineObjectConstruction(DocumentWidget) | |||
338 | 346 | ||
339 | static int docEnum_ = 0; | 347 | static int docEnum_ = 0; |
340 | 348 | ||
349 | void init_DocumentView(iDocumentView *d) { | ||
350 | d->owner = NULL; | ||
351 | d->doc = new_GmDocument(); | ||
352 | d->invalidRuns = new_PtrSet(); | ||
353 | d->drawBufs = new_DrawBufs(); | ||
354 | d->pageMargin = 5; | ||
355 | if (deviceType_App() != desktop_AppDeviceType) { | ||
356 | d->scrollY.flags |= pullDownAction_SmoothScrollFlag; /* pull to refresh */ | ||
357 | } | ||
358 | d->hoverPre = NULL; | ||
359 | d->hoverAltPre = NULL; | ||
360 | d->hoverLink = NULL; | ||
361 | d->animWideRunId = 0; | ||
362 | init_Anim(&d->animWideRunOffset, 0); | ||
363 | iZap(d->renderRuns); | ||
364 | iZap(d->visibleRuns); | ||
365 | d->visBuf = new_VisBuf(); { | ||
366 | d->visBufMeta = malloc(sizeof(iVisBufMeta) * numBuffers_VisBuf); | ||
367 | /* Additional metadata for each buffer. */ | ||
368 | d->visBuf->bufferInvalidated = visBufInvalidated_; | ||
369 | for (size_t i = 0; i < numBuffers_VisBuf; i++) { | ||
370 | d->visBuf->buffers[i].user = d->visBufMeta + i; | ||
371 | } | ||
372 | } | ||
373 | init_Anim(&d->sideOpacity, 0); | ||
374 | init_Anim(&d->altTextOpacity, 0); | ||
375 | init_PtrArray(&d->visibleLinks); | ||
376 | init_PtrArray(&d->visiblePre); | ||
377 | init_PtrArray(&d->visibleWideRuns); | ||
378 | init_Array(&d->wideRunOffsets, sizeof(int)); | ||
379 | init_PtrArray(&d->visibleMedia); | ||
380 | } | ||
381 | |||
382 | static void setOwner_DocumentView_(iDocumentView *d, iDocumentWidget *doc) { | ||
383 | d->owner = doc; | ||
384 | init_SmoothScroll(&d->scrollY, as_Widget(doc), scrollBegan_DocumentWidget_); | ||
385 | } | ||
386 | |||
341 | void init_DocumentWidget(iDocumentWidget *d) { | 387 | void init_DocumentWidget(iDocumentWidget *d) { |
342 | iWidget *w = as_Widget(d); | 388 | iWidget *w = as_Widget(d); |
343 | init_Widget(w); | 389 | init_Widget(w); |
@@ -365,61 +411,33 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
365 | d->request = NULL; | 411 | d->request = NULL; |
366 | d->isRequestUpdated = iFalse; | 412 | d->isRequestUpdated = iFalse; |
367 | d->media = new_ObjectList(); | 413 | d->media = new_ObjectList(); |
368 | d->doc = new_GmDocument(); | ||
369 | d->banner = new_Banner(); | 414 | d->banner = new_Banner(); |
370 | setOwner_Banner(d->banner, d); | 415 | setOwner_Banner(d->banner, d); |
371 | d->redirectCount = 0; | 416 | d->redirectCount = 0; |
372 | d->ordinalBase = 0; | 417 | d->ordinalBase = 0; |
373 | d->initNormScrollY = 0; | ||
374 | init_SmoothScroll(&d->scrollY, w, scrollBegan_DocumentWidget_); | ||
375 | if (deviceType_App() != desktop_AppDeviceType) { | ||
376 | d->scrollY.flags |= pullDownAction_SmoothScrollFlag; /* pull to refresh */ | ||
377 | } | ||
378 | d->animWideRunId = 0; | ||
379 | init_Anim(&d->animWideRunOffset, 0); | ||
380 | d->wheelSwipeState = none_WheelSwipeState; | 418 | d->wheelSwipeState = none_WheelSwipeState; |
381 | d->selectMark = iNullRange; | 419 | d->selectMark = iNullRange; |
382 | d->foundMark = iNullRange; | 420 | d->foundMark = iNullRange; |
383 | d->pageMargin = 5; | ||
384 | d->hoverPre = NULL; | ||
385 | d->hoverAltPre = NULL; | ||
386 | d->hoverLink = NULL; | ||
387 | d->contextLink = NULL; | 421 | d->contextLink = NULL; |
388 | iZap(d->renderRuns); | ||
389 | iZap(d->visibleRuns); | ||
390 | d->visBuf = new_VisBuf(); { | ||
391 | d->visBufMeta = malloc(sizeof(iVisBufMeta) * numBuffers_VisBuf); | ||
392 | /* Additional metadata for each buffer. */ | ||
393 | d->visBuf->bufferInvalidated = visBufInvalidated_; | ||
394 | for (size_t i = 0; i < numBuffers_VisBuf; i++) { | ||
395 | d->visBuf->buffers[i].user = d->visBufMeta + i; | ||
396 | } | ||
397 | } | ||
398 | d->invalidRuns = new_PtrSet(); | ||
399 | init_Anim(&d->sideOpacity, 0); | ||
400 | init_Anim(&d->altTextOpacity, 0); | ||
401 | d->sourceStatus = none_GmStatusCode; | 422 | d->sourceStatus = none_GmStatusCode; |
402 | init_String(&d->sourceHeader); | 423 | init_String(&d->sourceHeader); |
403 | init_String(&d->sourceMime); | 424 | init_String(&d->sourceMime); |
404 | init_Block(&d->sourceContent, 0); | 425 | init_Block(&d->sourceContent, 0); |
405 | iZap(d->sourceTime); | 426 | iZap(d->sourceTime); |
406 | d->sourceGempub = NULL; | 427 | d->sourceGempub = NULL; |
407 | init_PtrArray(&d->visibleLinks); | 428 | d->initNormScrollY = 0; |
408 | init_PtrArray(&d->visiblePre); | 429 | d->grabbedPlayer = NULL; |
409 | init_PtrArray(&d->visibleWideRuns); | 430 | d->mediaTimer = 0; |
410 | init_Array(&d->wideRunOffsets, sizeof(int)); | ||
411 | init_PtrArray(&d->visibleMedia); | ||
412 | d->grabbedPlayer = NULL; | ||
413 | d->mediaTimer = 0; | ||
414 | init_String(&d->pendingGotoHeading); | 431 | init_String(&d->pendingGotoHeading); |
415 | init_String(&d->linePrecedingLink); | 432 | init_String(&d->linePrecedingLink); |
416 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | 433 | init_Click(&d->click, d, SDL_BUTTON_LEFT); |
417 | d->linkInfo = (deviceType_App() == desktop_AppDeviceType ? new_LinkInfo() : NULL); | 434 | d->linkInfo = (deviceType_App() == desktop_AppDeviceType ? new_LinkInfo() : NULL); |
435 | init_DocumentView(&d->view); | ||
436 | setOwner_DocumentView_(&d->view, d); | ||
418 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); | 437 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); |
419 | d->menu = NULL; /* created when clicking */ | 438 | d->menu = NULL; /* created when clicking */ |
420 | d->playerMenu = NULL; | 439 | d->playerMenu = NULL; |
421 | d->copyMenu = NULL; | 440 | d->copyMenu = NULL; |
422 | d->drawBufs = new_DrawBufs(); | ||
423 | d->translation = NULL; | 441 | d->translation = NULL; |
424 | addChildFlags_Widget(w, | 442 | addChildFlags_Widget(w, |
425 | iClob(new_IndicatorWidget()), | 443 | iClob(new_IndicatorWidget()), |
@@ -443,22 +461,32 @@ void cancelAllRequests_DocumentWidget(iDocumentWidget *d) { | |||
443 | } | 461 | } |
444 | if (d->request) { | 462 | if (d->request) { |
445 | cancel_GmRequest(d->request); | 463 | cancel_GmRequest(d->request); |
464 | } | ||
446 | } | 465 | } |
466 | |||
467 | void deinit_DocumentView(iDocumentView *d) { | ||
468 | delete_DrawBufs(d->drawBufs); | ||
469 | delete_VisBuf(d->visBuf); | ||
470 | free(d->visBufMeta); | ||
471 | delete_PtrSet(d->invalidRuns); | ||
472 | deinit_Array(&d->wideRunOffsets); | ||
473 | deinit_PtrArray(&d->visibleMedia); | ||
474 | deinit_PtrArray(&d->visibleWideRuns); | ||
475 | deinit_PtrArray(&d->visiblePre); | ||
476 | deinit_PtrArray(&d->visibleLinks); | ||
477 | iReleasePtr(&d->doc); | ||
447 | } | 478 | } |
448 | 479 | ||
449 | void deinit_DocumentWidget(iDocumentWidget *d) { | 480 | void deinit_DocumentWidget(iDocumentWidget *d) { |
450 | // printf("\n* * * * * * * *\nDEINIT DOCUMENT: %s\n* * * * * * * *\n\n", | 481 | // printf("\n* * * * * * * *\nDEINIT DOCUMENT: %s\n* * * * * * * *\n\n", |
451 | // cstr_String(&d->widget.id)); fflush(stdout); | 482 | // cstr_String(&d->widget.id)); fflush(stdout); |
452 | cancelAllRequests_DocumentWidget(d); | 483 | cancelAllRequests_DocumentWidget(d); |
453 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); | 484 | pauseAllPlayers_Media(media_GmDocument(d->view.doc), iTrue); |
454 | removeTicker_App(animate_DocumentWidget_, d); | 485 | removeTicker_App(animate_DocumentWidget_, d); |
455 | removeTicker_App(prerender_DocumentWidget_, d); | 486 | removeTicker_App(prerender_DocumentWidget_, d); |
456 | remove_Periodic(periodic_App(), d); | 487 | remove_Periodic(periodic_App(), d); |
457 | delete_Translation(d->translation); | 488 | delete_Translation(d->translation); |
458 | delete_DrawBufs(d->drawBufs); | 489 | deinit_DocumentView(&d->view); |
459 | delete_VisBuf(d->visBuf); | ||
460 | free(d->visBufMeta); | ||
461 | delete_PtrSet(d->invalidRuns); | ||
462 | delete_LinkInfo(d->linkInfo); | 490 | delete_LinkInfo(d->linkInfo); |
463 | iRelease(d->media); | 491 | iRelease(d->media); |
464 | iRelease(d->request); | 492 | iRelease(d->request); |
@@ -469,15 +497,9 @@ void deinit_DocumentWidget(iDocumentWidget *d) { | |||
469 | deinit_String(&d->sourceMime); | 497 | deinit_String(&d->sourceMime); |
470 | deinit_String(&d->sourceHeader); | 498 | deinit_String(&d->sourceHeader); |
471 | delete_Banner(d->banner); | 499 | delete_Banner(d->banner); |
472 | iRelease(d->doc); | ||
473 | if (d->mediaTimer) { | 500 | if (d->mediaTimer) { |
474 | SDL_RemoveTimer(d->mediaTimer); | 501 | SDL_RemoveTimer(d->mediaTimer); |
475 | } | 502 | } |
476 | deinit_Array(&d->wideRunOffsets); | ||
477 | deinit_PtrArray(&d->visibleMedia); | ||
478 | deinit_PtrArray(&d->visibleWideRuns); | ||
479 | deinit_PtrArray(&d->visiblePre); | ||
480 | deinit_PtrArray(&d->visibleLinks); | ||
481 | delete_Block(d->certFingerprint); | 503 | delete_Block(d->certFingerprint); |
482 | delete_String(d->certSubject); | 504 | delete_String(d->certSubject); |
483 | delete_String(d->titleUser); | 505 | delete_String(d->titleUser); |
@@ -511,7 +533,7 @@ static void setLinkNumberMode_DocumentWidget_(iDocumentWidget *d, iBool set) { | |||
511 | } | 533 | } |
512 | } | 534 | } |
513 | 535 | ||
514 | static void resetWideRuns_DocumentWidget_(iDocumentWidget *d) { | 536 | static void resetWideRuns_DocumentView_(iDocumentView *d) { |
515 | clear_Array(&d->wideRunOffsets); | 537 | clear_Array(&d->wideRunOffsets); |
516 | d->animWideRunId = 0; | 538 | d->animWideRunId = 0; |
517 | init_Anim(&d->animWideRunOffset, 0); | 539 | init_Anim(&d->animWideRunOffset, 0); |
@@ -539,8 +561,8 @@ static void requestFinished_DocumentWidget_(iAnyObject *obj) { | |||
539 | d->request); | 561 | d->request); |
540 | } | 562 | } |
541 | 563 | ||
542 | static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { | 564 | static int documentWidth_DocumentView_(const iDocumentView *d) { |
543 | const iWidget *w = constAs_Widget(d); | 565 | const iWidget *w = constAs_Widget(d->owner); |
544 | const iRect bounds = bounds_Widget(w); | 566 | const iRect bounds = bounds_Widget(w); |
545 | const iPrefs * prefs = prefs_App(); | 567 | const iPrefs * prefs = prefs_App(); |
546 | const int minWidth = 50 * gap_UI; /* lines must fit a word at least */ | 568 | const int minWidth = 50 * gap_UI; /* lines must fit a word at least */ |
@@ -552,18 +574,18 @@ static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { | |||
552 | prefs->lineWidth * prefs->zoomPercent / 100); | 574 | prefs->lineWidth * prefs->zoomPercent / 100); |
553 | } | 575 | } |
554 | 576 | ||
555 | static int documentTopPad_DocumentWidget_(const iDocumentWidget *d) { | 577 | static int documentTopPad_DocumentView_(const iDocumentView *d) { |
556 | /* Amount of space between banner and top of the document. */ | 578 | /* Amount of space between banner and top of the document. */ |
557 | return isEmpty_Banner(d->banner) ? 0 : lineHeight_Text(paragraph_FontId); | 579 | return isEmpty_Banner(d->owner->banner) ? 0 : lineHeight_Text(paragraph_FontId); |
558 | } | 580 | } |
559 | 581 | ||
560 | static int documentTopMargin_DocumentWidget_(const iDocumentWidget *d) { | 582 | static int documentTopMargin_DocumentView_(const iDocumentView *d) { |
561 | return (isEmpty_Banner(d->banner) ? d->pageMargin * gap_UI : height_Banner(d->banner)) + | 583 | return (isEmpty_Banner(d->owner->banner) ? d->pageMargin * gap_UI : height_Banner(d->owner->banner)) + |
562 | documentTopPad_DocumentWidget_(d); | 584 | documentTopPad_DocumentView_(d); |
563 | } | 585 | } |
564 | 586 | ||
565 | static int pageHeight_DocumentWidget_(const iDocumentWidget *d) { | 587 | static int pageHeight_DocumentView_(const iDocumentView *d) { |
566 | return height_Banner(d->banner) + documentTopPad_DocumentWidget_(d) + size_GmDocument(d->doc).y; | 588 | return height_Banner(d->owner->banner) + documentTopPad_DocumentView_(d) + size_GmDocument(d->doc).y; |
567 | } | 589 | } |
568 | 590 | ||
569 | static int phoneToolbarHeight_DocumentWidget_(const iDocumentWidget *d) { | 591 | static int phoneToolbarHeight_DocumentWidget_(const iDocumentWidget *d) { |
@@ -582,39 +604,41 @@ static int footerHeight_DocumentWidget_(const iDocumentWidget *d) { | |||
582 | return hgt; | 604 | return hgt; |
583 | } | 605 | } |
584 | 606 | ||
585 | static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | 607 | static iRect documentBounds_DocumentView_(const iDocumentView *d) { |
586 | const iRect bounds = bounds_Widget(constAs_Widget(d)); | 608 | const iRect bounds = bounds_Widget(constAs_Widget(d->owner)); |
587 | const int margin = gap_UI * d->pageMargin; | 609 | const int margin = gap_UI * d->pageMargin; |
588 | iRect rect; | 610 | iRect rect; |
589 | rect.size.x = documentWidth_DocumentWidget_(d); | 611 | rect.size.x = documentWidth_DocumentView_(d); |
590 | rect.pos.x = mid_Rect(bounds).x - rect.size.x / 2; | 612 | rect.pos.x = mid_Rect(bounds).x - rect.size.x / 2; |
591 | rect.pos.y = top_Rect(bounds) + margin; | 613 | rect.pos.y = top_Rect(bounds) + margin; |
592 | rect.size.y = height_Rect(bounds) - margin; | 614 | rect.size.y = height_Rect(bounds) - margin; |
593 | iBool wasCentered = iFalse; | 615 | iBool wasCentered = iFalse; |
594 | if (d->flags & centerVertically_DocumentWidgetFlag) { | 616 | /* TODO: Further separation of View and Widget: configure header and footer heights |
617 | without involving the widget here. */ | ||
618 | if (d->owner->flags & centerVertically_DocumentWidgetFlag) { | ||
595 | const int docSize = size_GmDocument(d->doc).y + | 619 | const int docSize = size_GmDocument(d->doc).y + |
596 | documentTopMargin_DocumentWidget_(d); | 620 | documentTopMargin_DocumentView_(d); |
597 | if (size_GmDocument(d->doc).y == 0) { | 621 | if (size_GmDocument(d->doc).y == 0) { |
598 | /* Document is empty; maybe just showing an error banner. */ | 622 | /* Document is empty; maybe just showing an error banner. */ |
599 | rect.pos.y = top_Rect(bounds) + height_Rect(bounds) / 2 - | 623 | rect.pos.y = top_Rect(bounds) + height_Rect(bounds) / 2 - |
600 | documentTopPad_DocumentWidget_(d) - height_Banner(d->banner) / 2; | 624 | documentTopPad_DocumentView_(d) - height_Banner(d->owner->banner) / 2; |
601 | rect.size.y = 0; | 625 | rect.size.y = 0; |
602 | wasCentered = iTrue; | 626 | wasCentered = iTrue; |
603 | } | 627 | } |
604 | else if (docSize < rect.size.y - footerHeight_DocumentWidget_(d)) { | 628 | else if (docSize < rect.size.y - footerHeight_DocumentWidget_(d->owner)) { |
605 | /* TODO: Phone toolbar? */ | 629 | /* TODO: Phone toolbar? */ |
606 | /* Center vertically when the document is short. */ | 630 | /* Center vertically when the document is short. */ |
607 | const int relMidY = (height_Rect(bounds) - footerHeight_DocumentWidget_(d)) / 2; | 631 | const int relMidY = (height_Rect(bounds) - footerHeight_DocumentWidget_(d->owner)) / 2; |
608 | const int visHeight = size_GmDocument(d->doc).y; | 632 | const int visHeight = size_GmDocument(d->doc).y; |
609 | const int offset = -height_Banner(d->banner) - documentTopPad_DocumentWidget_(d); | 633 | const int offset = -height_Banner(d->owner->banner) - documentTopPad_DocumentView_(d); |
610 | rect.pos.y = top_Rect(bounds) + iMaxi(0, relMidY - visHeight / 2 + offset); | 634 | rect.pos.y = top_Rect(bounds) + iMaxi(0, relMidY - visHeight / 2 + offset); |
611 | rect.size.y = size_GmDocument(d->doc).y + documentTopMargin_DocumentWidget_(d); | 635 | rect.size.y = size_GmDocument(d->doc).y + documentTopMargin_DocumentView_(d); |
612 | wasCentered = iTrue; | 636 | wasCentered = iTrue; |
613 | } | 637 | } |
614 | } | 638 | } |
615 | if (!wasCentered) { | 639 | if (!wasCentered) { |
616 | /* The banner overtakes the top margin. */ | 640 | /* The banner overtakes the top margin. */ |
617 | if (!isEmpty_Banner(d->banner)) { | 641 | if (!isEmpty_Banner(d->owner->banner)) { |
618 | rect.pos.y -= margin; | 642 | rect.pos.y -= margin; |
619 | } | 643 | } |
620 | else { | 644 | else { |
@@ -624,26 +648,28 @@ static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | |||
624 | return rect; | 648 | return rect; |
625 | } | 649 | } |
626 | 650 | ||
627 | static int viewPos_DocumentWidget_(const iDocumentWidget *d) { | 651 | static int viewPos_DocumentView_(const iDocumentView *d) { |
628 | return height_Banner(d->banner) + documentTopPad_DocumentWidget_(d) - pos_SmoothScroll(&d->scrollY); | 652 | return height_Banner(d->owner->banner) + documentTopPad_DocumentView_(d) - |
653 | pos_SmoothScroll(&d->scrollY); | ||
629 | } | 654 | } |
630 | 655 | ||
631 | static iInt2 documentPos_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { | 656 | static iInt2 documentPos_DocumentView_(const iDocumentView *d, iInt2 pos) { |
632 | return addY_I2(sub_I2(pos, topLeft_Rect(documentBounds_DocumentWidget_(d))), | 657 | return addY_I2(sub_I2(pos, topLeft_Rect(documentBounds_DocumentView_(d))), |
633 | -viewPos_DocumentWidget_(d)); | 658 | -viewPos_DocumentView_(d)); |
634 | } | 659 | } |
635 | 660 | ||
636 | static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) { | 661 | static iRangei visibleRange_DocumentView_(const iDocumentView *d) { |
637 | int top = pos_SmoothScroll(&d->scrollY) - height_Banner(d->banner) - documentTopPad_DocumentWidget_(d); | 662 | int top = pos_SmoothScroll(&d->scrollY) - height_Banner(d->owner->banner) - |
638 | if (isEmpty_Banner(d->banner)) { | 663 | documentTopPad_DocumentView_(d); |
664 | if (isEmpty_Banner(d->owner->banner)) { | ||
639 | /* Top padding is not collapsed. */ | 665 | /* Top padding is not collapsed. */ |
640 | top -= d->pageMargin * gap_UI; | 666 | top -= d->pageMargin * gap_UI; |
641 | } | 667 | } |
642 | return (iRangei){ top, top + height_Rect(bounds_Widget(constAs_Widget(d))) }; | 668 | return (iRangei){ top, top + height_Rect(bounds_Widget(constAs_Widget(d->owner))) }; |
643 | } | 669 | } |
644 | 670 | ||
645 | static void addVisible_DocumentWidget_(void *context, const iGmRun *run) { | 671 | static void addVisible_DocumentView_(void *context, const iGmRun *run) { |
646 | iDocumentWidget *d = context; | 672 | iDocumentView *d = context; |
647 | if (~run->flags & decoration_GmRunFlag && !run->mediaId) { | 673 | if (~run->flags & decoration_GmRunFlag && !run->mediaId) { |
648 | if (!d->visibleRuns.start) { | 674 | if (!d->visibleRuns.start) { |
649 | d->visibleRuns.start = run; | 675 | d->visibleRuns.start = run; |
@@ -666,7 +692,7 @@ static void addVisible_DocumentWidget_(void *context, const iGmRun *run) { | |||
666 | } | 692 | } |
667 | } | 693 | } |
668 | 694 | ||
669 | static const iGmRun *lastVisibleLink_DocumentWidget_(const iDocumentWidget *d) { | 695 | static const iGmRun *lastVisibleLink_DocumentView_(const iDocumentView *d) { |
670 | iReverseConstForEach(PtrArray, i, &d->visibleLinks) { | 696 | iReverseConstForEach(PtrArray, i, &d->visibleLinks) { |
671 | const iGmRun *run = i.ptr; | 697 | const iGmRun *run = i.ptr; |
672 | if (run->flags & decoration_GmRunFlag && run->linkId) { | 698 | if (run->flags & decoration_GmRunFlag && run->linkId) { |
@@ -676,8 +702,8 @@ static const iGmRun *lastVisibleLink_DocumentWidget_(const iDocumentWidget *d) { | |||
676 | return NULL; | 702 | return NULL; |
677 | } | 703 | } |
678 | 704 | ||
679 | static float normScrollPos_DocumentWidget_(const iDocumentWidget *d) { | 705 | static float normScrollPos_DocumentView_(const iDocumentView *d) { |
680 | const int docSize = pageHeight_DocumentWidget_(d); // size_GmDocument(d->doc).y; | 706 | const int docSize = pageHeight_DocumentView_(d); |
681 | if (docSize) { | 707 | if (docSize) { |
682 | float pos = pos_SmoothScroll(&d->scrollY) / (float) docSize; | 708 | float pos = pos_SmoothScroll(&d->scrollY) / (float) docSize; |
683 | return iMax(pos, 0.0f); | 709 | return iMax(pos, 0.0f); |
@@ -685,15 +711,15 @@ static float normScrollPos_DocumentWidget_(const iDocumentWidget *d) { | |||
685 | return 0; | 711 | return 0; |
686 | } | 712 | } |
687 | 713 | ||
688 | static int scrollMax_DocumentWidget_(const iDocumentWidget *d) { | 714 | static int scrollMax_DocumentView_(const iDocumentView *d) { |
689 | const iWidget *w = constAs_Widget(d); | 715 | const iWidget *w = constAs_Widget(d->owner); |
690 | int sm = pageHeight_DocumentWidget_(d) + | 716 | int sm = pageHeight_DocumentView_(d) + |
691 | (isEmpty_Banner(d->banner) ? 2 : 1) * d->pageMargin * gap_UI + /* top and bottom margins */ | 717 | (isEmpty_Banner(d->owner->banner) ? 2 : 1) * d->pageMargin * gap_UI + /* top and bottom margins */ |
692 | footerHeight_DocumentWidget_(d) - height_Rect(bounds_Widget(w)); | 718 | footerHeight_DocumentWidget_(d->owner) - height_Rect(bounds_Widget(w)); |
693 | return sm; | 719 | return sm; |
694 | } | 720 | } |
695 | 721 | ||
696 | static void invalidateLink_DocumentWidget_(iDocumentWidget *d, iGmLinkId id) { | 722 | static void invalidateLink_DocumentView_(iDocumentView *d, iGmLinkId id) { |
697 | /* A link has multiple runs associated with it. */ | 723 | /* A link has multiple runs associated with it. */ |
698 | iConstForEach(PtrArray, i, &d->visibleLinks) { | 724 | iConstForEach(PtrArray, i, &d->visibleLinks) { |
699 | const iGmRun *run = i.ptr; | 725 | const iGmRun *run = i.ptr; |
@@ -703,7 +729,7 @@ static void invalidateLink_DocumentWidget_(iDocumentWidget *d, iGmLinkId id) { | |||
703 | } | 729 | } |
704 | } | 730 | } |
705 | 731 | ||
706 | static void invalidateVisibleLinks_DocumentWidget_(iDocumentWidget *d) { | 732 | static void invalidateVisibleLinks_DocumentView_(iDocumentView *d) { |
707 | iConstForEach(PtrArray, i, &d->visibleLinks) { | 733 | iConstForEach(PtrArray, i, &d->visibleLinks) { |
708 | const iGmRun *run = i.ptr; | 734 | const iGmRun *run = i.ptr; |
709 | if (run->linkId) { | 735 | if (run->linkId) { |
@@ -712,7 +738,7 @@ static void invalidateVisibleLinks_DocumentWidget_(iDocumentWidget *d) { | |||
712 | } | 738 | } |
713 | } | 739 | } |
714 | 740 | ||
715 | static int runOffset_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run) { | 741 | static int runOffset_DocumentView_(const iDocumentView *d, const iGmRun *run) { |
716 | if (preId_GmRun(run) && run->flags & wide_GmRunFlag) { | 742 | if (preId_GmRun(run) && run->flags & wide_GmRunFlag) { |
717 | if (d->animWideRunId == preId_GmRun(run)) { | 743 | if (d->animWideRunId == preId_GmRun(run)) { |
718 | return -value_Anim(&d->animWideRunOffset); | 744 | return -value_Anim(&d->animWideRunOffset); |
@@ -726,21 +752,20 @@ static int runOffset_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run | |||
726 | return 0; | 752 | return 0; |
727 | } | 753 | } |
728 | 754 | ||
729 | static void invalidateWideRunsWithNonzeroOffset_DocumentWidget_(iDocumentWidget *d) { | 755 | static void invalidateWideRunsWithNonzeroOffset_DocumentView_(iDocumentView *d) { |
730 | iConstForEach(PtrArray, i, &d->visibleWideRuns) { | 756 | iConstForEach(PtrArray, i, &d->visibleWideRuns) { |
731 | const iGmRun *run = i.ptr; | 757 | const iGmRun *run = i.ptr; |
732 | if (runOffset_DocumentWidget_(d, run)) { | 758 | if (runOffset_DocumentView_(d, run)) { |
733 | insert_PtrSet(d->invalidRuns, run); | 759 | insert_PtrSet(d->invalidRuns, run); |
734 | } | 760 | } |
735 | } | 761 | } |
736 | } | 762 | } |
737 | 763 | ||
738 | static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse); | ||
739 | |||
740 | static void animate_DocumentWidget_(void *ticker) { | 764 | static void animate_DocumentWidget_(void *ticker) { |
741 | iDocumentWidget *d = ticker; | 765 | iDocumentWidget *d = ticker; |
766 | iAssert(isInstance_Object(d, &Class_DocumentWidget)); | ||
742 | refresh_Widget(d); | 767 | refresh_Widget(d); |
743 | if (!isFinished_Anim(&d->sideOpacity) || !isFinished_Anim(&d->altTextOpacity) || | 768 | if (!isFinished_Anim(&d->view.sideOpacity) || !isFinished_Anim(&d->view.altTextOpacity) || |
744 | (d->linkInfo && !isFinished_Anim(&d->linkInfo->opacity))) { | 769 | (d->linkInfo && !isFinished_Anim(&d->linkInfo->opacity))) { |
745 | addTicker_App(animate_DocumentWidget_, d); | 770 | addTicker_App(animate_DocumentWidget_, d); |
746 | } | 771 | } |
@@ -768,15 +793,15 @@ static iBool isHoverAllowed_DocumentWidget_(const iDocumentWidget *d) { | |||
768 | return iTrue; | 793 | return iTrue; |
769 | } | 794 | } |
770 | 795 | ||
771 | static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { | 796 | static void updateHover_DocumentView_(iDocumentView *d, iInt2 mouse) { |
772 | const iWidget *w = constAs_Widget(d); | 797 | const iWidget *w = constAs_Widget(d->owner); |
773 | const iRect docBounds = documentBounds_DocumentWidget_(d); | 798 | const iRect docBounds = documentBounds_DocumentView_(d); |
774 | const iGmRun * oldHoverLink = d->hoverLink; | 799 | const iGmRun * oldHoverLink = d->hoverLink; |
775 | d->hoverPre = NULL; | 800 | d->hoverPre = NULL; |
776 | d->hoverLink = NULL; | 801 | d->hoverLink = NULL; |
777 | const iInt2 hoverPos = addY_I2(sub_I2(mouse, topLeft_Rect(docBounds)), | 802 | const iInt2 hoverPos = addY_I2(sub_I2(mouse, topLeft_Rect(docBounds)), |
778 | -viewPos_DocumentWidget_(d)); | 803 | -viewPos_DocumentView_(d)); |
779 | if (isHoverAllowed_DocumentWidget_(d)) { | 804 | if (isHoverAllowed_DocumentWidget_(d->owner)) { |
780 | iConstForEach(PtrArray, i, &d->visibleLinks) { | 805 | iConstForEach(PtrArray, i, &d->visibleLinks) { |
781 | const iGmRun *run = i.ptr; | 806 | const iGmRun *run = i.ptr; |
782 | /* Click targets are slightly expanded so there are no gaps between links. */ | 807 | /* Click targets are slightly expanded so there are no gaps between links. */ |
@@ -788,19 +813,21 @@ static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { | |||
788 | } | 813 | } |
789 | if (d->hoverLink != oldHoverLink) { | 814 | if (d->hoverLink != oldHoverLink) { |
790 | if (oldHoverLink) { | 815 | if (oldHoverLink) { |
791 | invalidateLink_DocumentWidget_(d, oldHoverLink->linkId); | 816 | invalidateLink_DocumentView_(d, oldHoverLink->linkId); |
792 | } | 817 | } |
793 | if (d->hoverLink) { | 818 | if (d->hoverLink) { |
794 | invalidateLink_DocumentWidget_(d, d->hoverLink->linkId); | 819 | invalidateLink_DocumentView_(d, d->hoverLink->linkId); |
795 | } | 820 | } |
796 | if (update_LinkInfo(d->linkInfo, d->doc, d->hoverLink ? d->hoverLink->linkId : 0, | 821 | if (update_LinkInfo(d->owner->linkInfo, |
822 | d->doc, | ||
823 | d->hoverLink ? d->hoverLink->linkId : 0, | ||
797 | width_Widget(w))) { | 824 | width_Widget(w))) { |
798 | animate_DocumentWidget_(d); | 825 | animate_DocumentWidget_(d->owner); |
799 | } | 826 | } |
800 | refresh_Widget(w); | 827 | refresh_Widget(w); |
801 | } | 828 | } |
802 | /* Hovering over preformatted blocks. */ | 829 | /* Hovering over preformatted blocks. */ |
803 | if (isHoverAllowed_DocumentWidget_(d)) { | 830 | if (isHoverAllowed_DocumentWidget_(d->owner)) { |
804 | iConstForEach(PtrArray, j, &d->visiblePre) { | 831 | iConstForEach(PtrArray, j, &d->visiblePre) { |
805 | const iGmRun *run = j.ptr; | 832 | const iGmRun *run = j.ptr; |
806 | if (contains_Rect(run->bounds, hoverPos)) { | 833 | if (contains_Rect(run->bounds, hoverPos)) { |
@@ -813,18 +840,18 @@ static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { | |||
813 | if (!d->hoverPre) { | 840 | if (!d->hoverPre) { |
814 | setValueSpeed_Anim(&d->altTextOpacity, 0.0f, 1.5f); | 841 | setValueSpeed_Anim(&d->altTextOpacity, 0.0f, 1.5f); |
815 | if (!isFinished_Anim(&d->altTextOpacity)) { | 842 | if (!isFinished_Anim(&d->altTextOpacity)) { |
816 | animate_DocumentWidget_(d); | 843 | animate_DocumentWidget_(d->owner); |
817 | } | 844 | } |
818 | } | 845 | } |
819 | else if (d->hoverPre && | 846 | else if (d->hoverPre && |
820 | preHasAltText_GmDocument(d->doc, preId_GmRun(d->hoverPre)) && | 847 | preHasAltText_GmDocument(d->doc, preId_GmRun(d->hoverPre)) && |
821 | ~d->flags & noHoverWhileScrolling_DocumentWidgetFlag) { | 848 | ~d->owner->flags & noHoverWhileScrolling_DocumentWidgetFlag) { |
822 | setValueSpeed_Anim(&d->altTextOpacity, 1.0f, 1.5f); | 849 | setValueSpeed_Anim(&d->altTextOpacity, 1.0f, 1.5f); |
823 | if (!isFinished_Anim(&d->altTextOpacity)) { | 850 | if (!isFinished_Anim(&d->altTextOpacity)) { |
824 | animate_DocumentWidget_(d); | 851 | animate_DocumentWidget_(d->owner); |
825 | } | 852 | } |
826 | } | 853 | } |
827 | if (isHover_Widget(w) && !contains_Widget(constAs_Widget(d->scroll), mouse)) { | 854 | if (isHover_Widget(w) && !contains_Widget(constAs_Widget(d->owner->scroll), mouse)) { |
828 | setCursor_Window(get_Window(), | 855 | setCursor_Window(get_Window(), |
829 | d->hoverLink || d->hoverPre ? SDL_SYSTEM_CURSOR_HAND | 856 | d->hoverLink || d->hoverPre ? SDL_SYSTEM_CURSOR_HAND |
830 | : SDL_SYSTEM_CURSOR_IBEAM); | 857 | : SDL_SYSTEM_CURSOR_IBEAM); |
@@ -835,15 +862,14 @@ static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { | |||
835 | } | 862 | } |
836 | } | 863 | } |
837 | 864 | ||
838 | static void updateSideOpacity_DocumentWidget_(iDocumentWidget *d, iBool isAnimated) { | 865 | static void updateSideOpacity_DocumentView_(iDocumentView *d, iBool isAnimated) { |
839 | float opacity = 0.0f; | 866 | float opacity = 0.0f; |
840 | // const iGmRun *banner = siteBanner_GmDocument(d->doc); | 867 | if (!isEmpty_Banner(d->owner->banner) && |
841 | if (!isEmpty_Banner(d->banner) && height_Banner(d->banner) < pos_SmoothScroll(&d->scrollY)) { | 868 | height_Banner(d->owner->banner) < pos_SmoothScroll(&d->scrollY)) { |
842 | // if (banner && bottom_Rect(banner->visBounds) < pos_SmoothScroll(&d->scrollY)) { | ||
843 | opacity = 1.0f; | 869 | opacity = 1.0f; |
844 | } | 870 | } |
845 | setValue_Anim(&d->sideOpacity, opacity, isAnimated ? (opacity < 0.5f ? 100 : 200) : 0); | 871 | setValue_Anim(&d->sideOpacity, opacity, isAnimated ? (opacity < 0.5f ? 100 : 200) : 0); |
846 | animate_DocumentWidget_(d); | 872 | animate_DocumentWidget_(d->owner); |
847 | } | 873 | } |
848 | 874 | ||
849 | static uint32_t mediaUpdateInterval_DocumentWidget_(const iDocumentWidget *d) { | 875 | static uint32_t mediaUpdateInterval_DocumentWidget_(const iDocumentWidget *d) { |
@@ -855,10 +881,10 @@ static uint32_t mediaUpdateInterval_DocumentWidget_(const iDocumentWidget *d) { | |||
855 | } | 881 | } |
856 | static const uint32_t invalidInterval_ = ~0u; | 882 | static const uint32_t invalidInterval_ = ~0u; |
857 | uint32_t interval = invalidInterval_; | 883 | uint32_t interval = invalidInterval_; |
858 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 884 | iConstForEach(PtrArray, i, &d->view.visibleMedia) { |
859 | const iGmRun *run = i.ptr; | 885 | const iGmRun *run = i.ptr; |
860 | if (run->mediaType == audio_MediaType) { | 886 | if (run->mediaType == audio_MediaType) { |
861 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); | 887 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->view.doc), mediaId_GmRun(run)); |
862 | if (flags_Player(plr) & adjustingVolume_PlayerFlag || | 888 | if (flags_Player(plr) & adjustingVolume_PlayerFlag || |
863 | (isStarted_Player(plr) && !isPaused_Player(plr))) { | 889 | (isStarted_Player(plr) && !isPaused_Player(plr))) { |
864 | interval = iMin(interval, 1000 / 15); | 890 | interval = iMin(interval, 1000 / 15); |
@@ -881,10 +907,10 @@ static uint32_t postMediaUpdate_DocumentWidget_(uint32_t interval, void *context | |||
881 | static void updateMedia_DocumentWidget_(iDocumentWidget *d) { | 907 | static void updateMedia_DocumentWidget_(iDocumentWidget *d) { |
882 | if (document_App() == d) { | 908 | if (document_App() == d) { |
883 | refresh_Widget(d); | 909 | refresh_Widget(d); |
884 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 910 | iConstForEach(PtrArray, i, &d->view.visibleMedia) { |
885 | const iGmRun *run = i.ptr; | 911 | const iGmRun *run = i.ptr; |
886 | if (run->mediaType == audio_MediaType) { | 912 | if (run->mediaType == audio_MediaType) { |
887 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); | 913 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->view.doc), mediaId_GmRun(run)); |
888 | if (idleTimeMs_Player(plr) > 3000 && ~flags_Player(plr) & volumeGrabbed_PlayerFlag && | 914 | if (idleTimeMs_Player(plr) > 3000 && ~flags_Player(plr) & volumeGrabbed_PlayerFlag && |
889 | flags_Player(plr) & adjustingVolume_PlayerFlag) { | 915 | flags_Player(plr) & adjustingVolume_PlayerFlag) { |
890 | setFlags_Player(plr, adjustingVolume_PlayerFlag, iFalse); | 916 | setFlags_Player(plr, adjustingVolume_PlayerFlag, iFalse); |
@@ -912,7 +938,7 @@ static void animateMedia_DocumentWidget_(iDocumentWidget *d) { | |||
912 | } | 938 | } |
913 | } | 939 | } |
914 | 940 | ||
915 | static iRangecc currentHeading_DocumentWidget_(const iDocumentWidget *d) { | 941 | static iRangecc currentHeading_DocumentView_(const iDocumentView *d) { |
916 | iRangecc heading = iNullRange; | 942 | iRangecc heading = iNullRange; |
917 | if (d->visibleRuns.start) { | 943 | if (d->visibleRuns.start) { |
918 | iConstForEach(Array, i, headings_GmDocument(d->doc)) { | 944 | iConstForEach(Array, i, headings_GmDocument(d->doc)) { |
@@ -930,65 +956,68 @@ static iRangecc currentHeading_DocumentWidget_(const iDocumentWidget *d) { | |||
930 | return heading; | 956 | return heading; |
931 | } | 957 | } |
932 | 958 | ||
933 | static int updateScrollMax_DocumentWidget_(iDocumentWidget *d) { | 959 | static int updateScrollMax_DocumentView_(iDocumentView *d) { |
934 | arrange_Widget(d->footerButtons); /* scrollMax depends on footer height */ | 960 | arrange_Widget(d->owner->footerButtons); /* scrollMax depends on footer height */ |
935 | const int scrollMax = scrollMax_DocumentWidget_(d); | 961 | const int scrollMax = scrollMax_DocumentView_(d); |
936 | setMax_SmoothScroll(&d->scrollY, scrollMax); | 962 | setMax_SmoothScroll(&d->scrollY, scrollMax); |
937 | return scrollMax; | 963 | return scrollMax; |
938 | } | 964 | } |
939 | 965 | ||
940 | static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | 966 | static void updateVisible_DocumentView_(iDocumentView *d) { |
941 | iChangeFlags(d->flags, | 967 | /* TODO: The concerns of Widget and View are too tangled together here. */ |
968 | iChangeFlags(d->owner->flags, | ||
942 | centerVertically_DocumentWidgetFlag, | 969 | centerVertically_DocumentWidgetFlag, |
943 | prefs_App()->centerShortDocs || startsWithCase_String(d->mod.url, "about:") || | 970 | prefs_App()->centerShortDocs || startsWithCase_String(d->owner->mod.url, "about:") || |
944 | !isSuccess_GmStatusCode(d->sourceStatus)); | 971 | !isSuccess_GmStatusCode(d->owner->sourceStatus)); |
945 | const iRangei visRange = visibleRange_DocumentWidget_(d); | 972 | iScrollWidget *scrollBar = d->owner->scroll; |
973 | const iRangei visRange = visibleRange_DocumentView_(d); | ||
946 | // printf("visRange: %d...%d\n", visRange.start, visRange.end); | 974 | // printf("visRange: %d...%d\n", visRange.start, visRange.end); |
947 | const iRect bounds = bounds_Widget(as_Widget(d)); | 975 | const iRect bounds = bounds_Widget(as_Widget(d->owner)); |
948 | const int scrollMax = updateScrollMax_DocumentWidget_(d); | 976 | const int scrollMax = updateScrollMax_DocumentView_(d); |
949 | /* Reposition the footer buttons as appropriate. */ | 977 | /* Reposition the footer buttons as appropriate. */ |
950 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax }); | 978 | setRange_ScrollWidget(scrollBar, (iRangei){ 0, scrollMax }); |
951 | const int docSize = pageHeight_DocumentWidget_(d) + footerHeight_DocumentWidget_(d); | 979 | const int docSize = pageHeight_DocumentView_(d) + footerHeight_DocumentWidget_(d->owner); |
952 | const float scrollPos = pos_SmoothScroll(&d->scrollY); | 980 | const float scrollPos = pos_SmoothScroll(&d->scrollY); |
953 | setThumb_ScrollWidget(d->scroll, | 981 | setThumb_ScrollWidget(scrollBar, |
954 | pos_SmoothScroll(&d->scrollY), | 982 | pos_SmoothScroll(&d->scrollY), |
955 | docSize > 0 ? height_Rect(bounds) * size_Range(&visRange) / docSize : 0); | 983 | docSize > 0 ? height_Rect(bounds) * size_Range(&visRange) / docSize : 0); |
956 | if (d->footerButtons) { | 984 | if (d->owner->footerButtons) { |
957 | const iRect bounds = bounds_Widget(as_Widget(d)); | 985 | const iRect bounds = bounds_Widget(as_Widget(d->owner)); |
958 | const iRect docBounds = documentBounds_DocumentWidget_(d); | 986 | const iRect docBounds = documentBounds_DocumentView_(d); |
959 | const int hPad = (width_Rect(bounds) - iMin(120 * gap_UI, width_Rect(docBounds))) / 2; | 987 | const int hPad = (width_Rect(bounds) - iMin(120 * gap_UI, width_Rect(docBounds))) / 2; |
960 | const int vPad = 3 * gap_UI; | 988 | const int vPad = 3 * gap_UI; |
961 | setPadding_Widget(d->footerButtons, hPad, 0, hPad, vPad); | 989 | setPadding_Widget(d->owner->footerButtons, hPad, 0, hPad, vPad); |
962 | d->footerButtons->rect.pos.y = height_Rect(bounds) - footerHeight_DocumentWidget_(d) + | 990 | d->owner->footerButtons->rect.pos.y = height_Rect(bounds) - |
963 | (scrollMax > 0 ? scrollMax - scrollPos : 0); | 991 | footerHeight_DocumentWidget_(d->owner) + |
992 | (scrollMax > 0 ? scrollMax - scrollPos : 0); | ||
964 | } | 993 | } |
965 | clear_PtrArray(&d->visibleLinks); | 994 | clear_PtrArray(&d->visibleLinks); |
966 | clear_PtrArray(&d->visibleWideRuns); | 995 | clear_PtrArray(&d->visibleWideRuns); |
967 | clear_PtrArray(&d->visiblePre); | 996 | clear_PtrArray(&d->visiblePre); |
968 | clear_PtrArray(&d->visibleMedia); | 997 | clear_PtrArray(&d->visibleMedia); |
969 | const iRangecc oldHeading = currentHeading_DocumentWidget_(d); | 998 | const iRangecc oldHeading = currentHeading_DocumentView_(d); |
970 | /* Scan for visible runs. */ { | 999 | /* Scan for visible runs. */ { |
971 | iZap(d->visibleRuns); | 1000 | iZap(d->visibleRuns); |
972 | render_GmDocument(d->doc, visRange, addVisible_DocumentWidget_, d); | 1001 | render_GmDocument(d->doc, visRange, addVisible_DocumentView_, d); |
973 | } | 1002 | } |
974 | const iRangecc newHeading = currentHeading_DocumentWidget_(d); | 1003 | const iRangecc newHeading = currentHeading_DocumentView_(d); |
975 | if (memcmp(&oldHeading, &newHeading, sizeof(oldHeading))) { | 1004 | if (memcmp(&oldHeading, &newHeading, sizeof(oldHeading))) { |
976 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; | 1005 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; |
977 | } | 1006 | } |
978 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window(), 0)); | 1007 | updateHover_DocumentView_(d, mouseCoord_Window(get_Window(), 0)); |
979 | updateSideOpacity_DocumentWidget_(d, iTrue); | 1008 | updateSideOpacity_DocumentView_(d, iTrue); |
980 | animateMedia_DocumentWidget_(d); | 1009 | animateMedia_DocumentWidget_(d->owner); |
981 | /* Remember scroll positions of recently visited pages. */ { | 1010 | /* Remember scroll positions of recently visited pages. */ { |
982 | iRecentUrl *recent = mostRecentUrl_History(d->mod.history); | 1011 | iRecentUrl *recent = mostRecentUrl_History(d->owner->mod.history); |
983 | if (recent && docSize && d->state == ready_RequestState && | 1012 | if (recent && docSize && d->owner->state == ready_RequestState && |
984 | equal_String(&recent->url, d->mod.url)) { | 1013 | equal_String(&recent->url, d->owner->mod.url)) { |
985 | recent->normScrollY = normScrollPos_DocumentWidget_(d); | 1014 | recent->normScrollY = normScrollPos_DocumentView_(d); |
986 | } | 1015 | } |
987 | } | 1016 | } |
988 | /* After scrolling/resizing stops, begin pre-rendering the visbuf contents. */ { | 1017 | /* After scrolling/resizing stops, begin pre-rendering the visbuf contents. */ { |
989 | removeTicker_App(prerender_DocumentWidget_, d); | 1018 | removeTicker_App(prerender_DocumentWidget_, d->owner); |
990 | remove_Periodic(periodic_App(), d); | 1019 | remove_Periodic(periodic_App(), d); |
991 | add_Periodic(periodic_App(), d, "document.render"); | 1020 | add_Periodic(periodic_App(), d->owner, "document.render"); |
992 | } | 1021 | } |
993 | } | 1022 | } |
994 | 1023 | ||
@@ -1000,8 +1029,8 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { | |||
1000 | return; | 1029 | return; |
1001 | } | 1030 | } |
1002 | iStringArray *title = iClob(new_StringArray()); | 1031 | iStringArray *title = iClob(new_StringArray()); |
1003 | if (!isEmpty_String(title_GmDocument(d->doc))) { | 1032 | if (!isEmpty_String(title_GmDocument(d->view.doc))) { |
1004 | pushBack_StringArray(title, title_GmDocument(d->doc)); | 1033 | pushBack_StringArray(title, title_GmDocument(d->view.doc)); |
1005 | } | 1034 | } |
1006 | if (!isEmpty_String(d->titleUser)) { | 1035 | if (!isEmpty_String(d->titleUser)) { |
1007 | pushBack_StringArray(title, d->titleUser); | 1036 | pushBack_StringArray(title, d->titleUser); |
@@ -1032,7 +1061,7 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { | |||
1032 | setTitle_MainWindow(get_MainWindow(), text); | 1061 | setTitle_MainWindow(get_MainWindow(), text); |
1033 | setWindow = iFalse; | 1062 | setWindow = iFalse; |
1034 | } | 1063 | } |
1035 | const iChar siteIcon = siteIcon_GmDocument(d->doc); | 1064 | const iChar siteIcon = siteIcon_GmDocument(d->view.doc); |
1036 | if (siteIcon) { | 1065 | if (siteIcon) { |
1037 | if (!isEmpty_String(text)) { | 1066 | if (!isEmpty_String(text)) { |
1038 | prependCStr_String(text, " " restore_ColorEscape); | 1067 | prependCStr_String(text, " " restore_ColorEscape); |
@@ -1072,7 +1101,7 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { | |||
1072 | } | 1101 | } |
1073 | } | 1102 | } |
1074 | 1103 | ||
1075 | static void updateTimestampBuf_DocumentWidget_(const iDocumentWidget *d) { | 1104 | static void updateTimestampBuf_DocumentView_(const iDocumentView *d) { |
1076 | if (!isExposed_Window(get_Window())) { | 1105 | if (!isExposed_Window(get_Window())) { |
1077 | return; | 1106 | return; |
1078 | } | 1107 | } |
@@ -1080,17 +1109,22 @@ static void updateTimestampBuf_DocumentWidget_(const iDocumentWidget *d) { | |||
1080 | delete_TextBuf(d->drawBufs->timestampBuf); | 1109 | delete_TextBuf(d->drawBufs->timestampBuf); |
1081 | d->drawBufs->timestampBuf = NULL; | 1110 | d->drawBufs->timestampBuf = NULL; |
1082 | } | 1111 | } |
1083 | if (isValid_Time(&d->sourceTime)) { | 1112 | if (isValid_Time(&d->owner->sourceTime)) { |
1084 | iString *fmt = timeFormatHourPreference_Lang("page.timestamp"); | 1113 | iString *fmt = timeFormatHourPreference_Lang("page.timestamp"); |
1085 | d->drawBufs->timestampBuf = newRange_TextBuf( | 1114 | d->drawBufs->timestampBuf = newRange_TextBuf( |
1086 | uiLabel_FontId, | 1115 | uiLabel_FontId, |
1087 | white_ColorId, | 1116 | white_ColorId, |
1088 | range_String(collect_String(format_Time(&d->sourceTime, cstr_String(fmt))))); | 1117 | range_String(collect_String(format_Time(&d->owner->sourceTime, cstr_String(fmt))))); |
1089 | delete_String(fmt); | 1118 | delete_String(fmt); |
1090 | } | 1119 | } |
1091 | d->drawBufs->flags &= ~updateTimestampBuf_DrawBufsFlag; | 1120 | d->drawBufs->flags &= ~updateTimestampBuf_DrawBufsFlag; |
1092 | } | 1121 | } |
1093 | 1122 | ||
1123 | static void invalidate_DocumentView_(iDocumentView *d) { | ||
1124 | invalidate_VisBuf(d->visBuf); | ||
1125 | clear_PtrSet(d->invalidRuns); | ||
1126 | } | ||
1127 | |||
1094 | static void invalidate_DocumentWidget_(iDocumentWidget *d) { | 1128 | static void invalidate_DocumentWidget_(iDocumentWidget *d) { |
1095 | if (flags_Widget(as_Widget(d)) & destroyPending_WidgetFlag) { | 1129 | if (flags_Widget(as_Widget(d)) & destroyPending_WidgetFlag) { |
1096 | return; | 1130 | return; |
@@ -1103,8 +1137,7 @@ static void invalidate_DocumentWidget_(iDocumentWidget *d) { | |||
1103 | return; | 1137 | return; |
1104 | } | 1138 | } |
1105 | d->flags &= ~invalidationPending_DocumentWidgetFlag; | 1139 | d->flags &= ~invalidationPending_DocumentWidgetFlag; |
1106 | invalidate_VisBuf(d->visBuf); | 1140 | invalidate_DocumentView_(&d->view); |
1107 | clear_PtrSet(d->invalidRuns); | ||
1108 | // printf("[%p] '%s' invalidated\n", d, cstr_String(id_Widget(as_Widget(d)))); | 1141 | // printf("[%p] '%s' invalidated\n", d, cstr_String(id_Widget(as_Widget(d)))); |
1109 | } | 1142 | } |
1110 | 1143 | ||
@@ -1113,17 +1146,21 @@ static iRangecc siteText_DocumentWidget_(const iDocumentWidget *d) { | |||
1113 | : range_String(d->titleUser); | 1146 | : range_String(d->titleUser); |
1114 | } | 1147 | } |
1115 | 1148 | ||
1116 | static void documentRunsInvalidated_DocumentWidget_(iDocumentWidget *d) { | 1149 | static void documentRunsInvalidated_DocumentView_(iDocumentView *d) { |
1117 | d->foundMark = iNullRange; | ||
1118 | d->selectMark = iNullRange; | ||
1119 | d->hoverPre = NULL; | 1150 | d->hoverPre = NULL; |
1120 | d->hoverAltPre = NULL; | 1151 | d->hoverAltPre = NULL; |
1121 | d->hoverLink = NULL; | 1152 | d->hoverLink = NULL; |
1122 | d->contextLink = NULL; | ||
1123 | iZap(d->visibleRuns); | 1153 | iZap(d->visibleRuns); |
1124 | iZap(d->renderRuns); | 1154 | iZap(d->renderRuns); |
1125 | } | 1155 | } |
1126 | 1156 | ||
1157 | static void documentRunsInvalidated_DocumentWidget_(iDocumentWidget *d) { | ||
1158 | d->foundMark = iNullRange; | ||
1159 | d->selectMark = iNullRange; | ||
1160 | d->contextLink = NULL; | ||
1161 | documentRunsInvalidated_DocumentView_(&d->view); | ||
1162 | } | ||
1163 | |||
1127 | iBool isPinned_DocumentWidget_(const iDocumentWidget *d) { | 1164 | iBool isPinned_DocumentWidget_(const iDocumentWidget *d) { |
1128 | if (deviceType_App() == phone_AppDeviceType) { | 1165 | if (deviceType_App() == phone_AppDeviceType) { |
1129 | return iFalse; | 1166 | return iFalse; |
@@ -1150,11 +1187,11 @@ static void showOrHidePinningIndicator_DocumentWidget_(iDocumentWidget *d) { | |||
1150 | static void documentWasChanged_DocumentWidget_(iDocumentWidget *d) { | 1187 | static void documentWasChanged_DocumentWidget_(iDocumentWidget *d) { |
1151 | iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iFalse); | 1188 | iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iFalse); |
1152 | setFlags_Widget(as_Widget(d), touchDrag_WidgetFlag, iFalse); | 1189 | setFlags_Widget(as_Widget(d), touchDrag_WidgetFlag, iFalse); |
1153 | updateVisitedLinks_GmDocument(d->doc); | 1190 | updateVisitedLinks_GmDocument(d->view.doc); |
1154 | documentRunsInvalidated_DocumentWidget_(d); | 1191 | documentRunsInvalidated_DocumentWidget_(d); |
1155 | updateWindowTitle_DocumentWidget_(d); | 1192 | updateWindowTitle_DocumentWidget_(d); |
1156 | updateVisible_DocumentWidget_(d); | 1193 | updateVisible_DocumentView_(&d->view); |
1157 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; | 1194 | d->view.drawBufs->flags |= updateSideBuf_DrawBufsFlag; |
1158 | invalidate_DocumentWidget_(d); | 1195 | invalidate_DocumentWidget_(d); |
1159 | refresh_Widget(as_Widget(d)); | 1196 | refresh_Widget(as_Widget(d)); |
1160 | /* Check for special bookmark tags. */ | 1197 | /* Check for special bookmark tags. */ |
@@ -1169,15 +1206,15 @@ static void documentWasChanged_DocumentWidget_(iDocumentWidget *d) { | |||
1169 | showOrHidePinningIndicator_DocumentWidget_(d); | 1206 | showOrHidePinningIndicator_DocumentWidget_(d); |
1170 | if (~d->flags & fromCache_DocumentWidgetFlag) { | 1207 | if (~d->flags & fromCache_DocumentWidgetFlag) { |
1171 | setCachedDocument_History(d->mod.history, | 1208 | setCachedDocument_History(d->mod.history, |
1172 | d->doc, /* keeps a ref */ | 1209 | d->view.doc, /* keeps a ref */ |
1173 | (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); | 1210 | (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); |
1174 | } | 1211 | } |
1175 | } | 1212 | } |
1176 | 1213 | ||
1177 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | 1214 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { |
1178 | setUrl_GmDocument(d->doc, d->mod.url); | 1215 | setUrl_GmDocument(d->view.doc, d->mod.url); |
1179 | const int docWidth = documentWidth_DocumentWidget_(d); | 1216 | const int docWidth = documentWidth_DocumentView_(&d->view); |
1180 | setSource_GmDocument(d->doc, | 1217 | setSource_GmDocument(d->view.doc, |
1181 | source, | 1218 | source, |
1182 | docWidth, | 1219 | docWidth, |
1183 | width_Widget(d), | 1220 | width_Widget(d), |
@@ -1188,22 +1225,21 @@ void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | |||
1188 | } | 1225 | } |
1189 | 1226 | ||
1190 | static void replaceDocument_DocumentWidget_(iDocumentWidget *d, iGmDocument *newDoc) { | 1227 | static void replaceDocument_DocumentWidget_(iDocumentWidget *d, iGmDocument *newDoc) { |
1191 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); | 1228 | pauseAllPlayers_Media(media_GmDocument(d->view.doc), iTrue); |
1192 | iRelease(d->doc); | 1229 | iRelease(d->view.doc); |
1193 | d->doc = ref_Object(newDoc); | 1230 | d->view.doc = ref_Object(newDoc); |
1194 | documentWasChanged_DocumentWidget_(d); | 1231 | documentWasChanged_DocumentWidget_(d); |
1195 | } | 1232 | } |
1196 | 1233 | ||
1197 | static void updateBanner_DocumentWidget_(iDocumentWidget *d) { | 1234 | static void updateBanner_DocumentWidget_(iDocumentWidget *d) { |
1198 | setSite_Banner(d->banner, siteText_DocumentWidget_(d), siteIcon_GmDocument(d->doc)); | 1235 | setSite_Banner(d->banner, siteText_DocumentWidget_(d), siteIcon_GmDocument(d->view.doc)); |
1199 | } | 1236 | } |
1200 | 1237 | ||
1201 | static void updateTheme_DocumentWidget_(iDocumentWidget *d) { | 1238 | static void updateTheme_DocumentWidget_(iDocumentWidget *d) { |
1202 | if (document_App() != d || category_GmStatusCode(d->sourceStatus) == categoryInput_GmStatusCode) { | 1239 | if (document_App() != d || category_GmStatusCode(d->sourceStatus) == categoryInput_GmStatusCode) { |
1203 | return; | 1240 | return; |
1204 | } | 1241 | } |
1205 | // setThemeSeed_GmDocument(d->doc, urlThemeSeed_String(d->mod.url)); /* theme palette and icon */ | 1242 | d->view.drawBufs->flags |= updateTimestampBuf_DrawBufsFlag; |
1206 | d->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag; | ||
1207 | updateBanner_DocumentWidget_(d); | 1243 | updateBanner_DocumentWidget_(d); |
1208 | } | 1244 | } |
1209 | 1245 | ||
@@ -1234,17 +1270,20 @@ static void makeFooterButtons_DocumentWidget_(iDocumentWidget *d, const iMenuIte | |||
1234 | addChild_Widget(as_Widget(d), iClob(d->footerButtons)); | 1270 | addChild_Widget(as_Widget(d), iClob(d->footerButtons)); |
1235 | arrange_Widget(d->footerButtons); | 1271 | arrange_Widget(d->footerButtons); |
1236 | arrange_Widget(w); | 1272 | arrange_Widget(w); |
1237 | updateVisible_DocumentWidget_(d); /* final placement for the buttons */ | 1273 | updateVisible_DocumentView_(&d->view); /* final placement for the buttons */ |
1274 | } | ||
1275 | |||
1276 | static void resetScroll_DocumentView_(iDocumentView *d) { | ||
1277 | reset_SmoothScroll(&d->scrollY); | ||
1278 | init_Anim(&d->sideOpacity, 0); | ||
1279 | init_Anim(&d->altTextOpacity, 0); | ||
1280 | resetWideRuns_DocumentView_(d); | ||
1238 | } | 1281 | } |
1239 | 1282 | ||
1240 | static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode code, | 1283 | static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode code, |
1241 | const iString *meta) { | 1284 | const iString *meta) { |
1242 | /* TODO: No such thing as an "error page". It should be an empty page with an error banner. */ | 1285 | iString *src = collectNew_String(); |
1243 | iString *src = collectNew_String(); | ||
1244 | const iGmError *msg = get_GmError(code); | 1286 | const iGmError *msg = get_GmError(code); |
1245 | // appendChar_String(src, msg->icon ? msg->icon : 0x2327); /* X in a box */ | ||
1246 | //appendFormat_String(src, " %s\n%s", msg->title, msg->info); | ||
1247 | // iBool useBanner = iTrue; | ||
1248 | destroy_Widget(d->footerButtons); | 1287 | destroy_Widget(d->footerButtons); |
1249 | d->footerButtons = NULL; | 1288 | d->footerButtons = NULL; |
1250 | const iString *serverErrorMsg = NULL; | 1289 | const iString *serverErrorMsg = NULL; |
@@ -1330,7 +1369,7 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
1330 | } | 1369 | } |
1331 | /* Make a new document for the error page.*/ | 1370 | /* Make a new document for the error page.*/ |
1332 | iGmDocument *errorDoc = new_GmDocument(); | 1371 | iGmDocument *errorDoc = new_GmDocument(); |
1333 | setWidth_GmDocument(errorDoc, documentWidth_DocumentWidget_(d), width_Widget(d)); | 1372 | setWidth_GmDocument(errorDoc, documentWidth_DocumentView_(&d->view), width_Widget(d)); |
1334 | setUrl_GmDocument(errorDoc, d->mod.url); | 1373 | setUrl_GmDocument(errorDoc, d->mod.url); |
1335 | setFormat_GmDocument(errorDoc, gemini_SourceFormat); | 1374 | setFormat_GmDocument(errorDoc, gemini_SourceFormat); |
1336 | replaceDocument_DocumentWidget_(d, errorDoc); | 1375 | replaceDocument_DocumentWidget_(d, errorDoc); |
@@ -1340,10 +1379,7 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
1340 | d->state = ready_RequestState; | 1379 | d->state = ready_RequestState; |
1341 | setSource_DocumentWidget(d, src); | 1380 | setSource_DocumentWidget(d, src); |
1342 | updateTheme_DocumentWidget_(d); | 1381 | updateTheme_DocumentWidget_(d); |
1343 | reset_SmoothScroll(&d->scrollY); | 1382 | resetScroll_DocumentView_(&d->view); |
1344 | init_Anim(&d->sideOpacity, 0); | ||
1345 | init_Anim(&d->altTextOpacity, 0); | ||
1346 | resetWideRuns_DocumentWidget_(d); | ||
1347 | } | 1383 | } |
1348 | 1384 | ||
1349 | static void updateFetchProgress_DocumentWidget_(iDocumentWidget *d) { | 1385 | static void updateFetchProgress_DocumentWidget_(iDocumentWidget *d) { |
@@ -1459,9 +1495,9 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool | |||
1459 | "document.save" } }, | 1495 | "document.save" } }, |
1460 | 2); | 1496 | 2); |
1461 | } | 1497 | } |
1462 | if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { | 1498 | if (preloadCoverImage_Gempub(d->sourceGempub, d->view.doc)) { |
1463 | redoLayout_GmDocument(d->doc); | 1499 | redoLayout_GmDocument(d->view.doc); |
1464 | updateVisible_DocumentWidget_(d); | 1500 | updateVisible_DocumentView_(&d->view); |
1465 | invalidate_DocumentWidget_(d); | 1501 | invalidate_DocumentWidget_(d); |
1466 | } | 1502 | } |
1467 | } | 1503 | } |
@@ -1545,6 +1581,14 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool | |||
1545 | } | 1581 | } |
1546 | } | 1582 | } |
1547 | 1583 | ||
1584 | static void updateWidth_DocumentView_(iDocumentView *d) { | ||
1585 | updateWidth_GmDocument(d->doc, documentWidth_DocumentView_(d), width_Widget(d->owner)); | ||
1586 | } | ||
1587 | |||
1588 | static void updateWidthAndRedoLayout_DocumentView_(iDocumentView *d) { | ||
1589 | setWidth_GmDocument(d->doc, documentWidth_DocumentView_(d), width_Widget(d->owner)); | ||
1590 | } | ||
1591 | |||
1548 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, | 1592 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, |
1549 | const iGmResponse *response, | 1593 | const iGmResponse *response, |
1550 | iGmDocument *cachedDoc, | 1594 | iGmDocument *cachedDoc, |
@@ -1565,7 +1609,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, | |||
1565 | } | 1609 | } |
1566 | clear_String(&d->sourceMime); | 1610 | clear_String(&d->sourceMime); |
1567 | d->sourceTime = response->when; | 1611 | d->sourceTime = response->when; |
1568 | d->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag; | 1612 | d->view.drawBufs->flags |= updateTimestampBuf_DrawBufsFlag; |
1569 | initBlock_String(&str, &response->body); /* Note: Body may be megabytes in size. */ | 1613 | initBlock_String(&str, &response->body); /* Note: Body may be megabytes in size. */ |
1570 | if (isSuccess_GmStatusCode(statusCode)) { | 1614 | if (isSuccess_GmStatusCode(statusCode)) { |
1571 | /* Check the MIME type. */ | 1615 | /* Check the MIME type. */ |
@@ -1709,16 +1753,16 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, | |||
1709 | format_String(&str, "=> %s %s\n", | 1753 | format_String(&str, "=> %s %s\n", |
1710 | cstr_String(canonicalUrl_String(d->mod.url)), | 1754 | cstr_String(canonicalUrl_String(d->mod.url)), |
1711 | linkTitle); | 1755 | linkTitle); |
1712 | setData_Media(media_GmDocument(d->doc), | 1756 | setData_Media(media_GmDocument(d->view.doc), |
1713 | imgLinkId, | 1757 | imgLinkId, |
1714 | mimeStr, | 1758 | mimeStr, |
1715 | &response->body, | 1759 | &response->body, |
1716 | !isRequestFinished ? partialData_MediaFlag : 0); | 1760 | !isRequestFinished ? partialData_MediaFlag : 0); |
1717 | redoLayout_GmDocument(d->doc); | 1761 | redoLayout_GmDocument(d->view.doc); |
1718 | } | 1762 | } |
1719 | else if (isAudio && !isInitialUpdate) { | 1763 | else if (isAudio && !isInitialUpdate) { |
1720 | /* Update the audio content. */ | 1764 | /* Update the audio content. */ |
1721 | setData_Media(media_GmDocument(d->doc), | 1765 | setData_Media(media_GmDocument(d->view.doc), |
1722 | imgLinkId, | 1766 | imgLinkId, |
1723 | mimeStr, | 1767 | mimeStr, |
1724 | &response->body, | 1768 | &response->body, |
@@ -1748,11 +1792,11 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, | |||
1748 | return; | 1792 | return; |
1749 | } | 1793 | } |
1750 | d->flags |= drawDownloadCounter_DocumentWidgetFlag; | 1794 | d->flags |= drawDownloadCounter_DocumentWidgetFlag; |
1751 | clear_PtrSet(d->invalidRuns); | 1795 | clear_PtrSet(d->view.invalidRuns); |
1752 | deinit_String(&str); | 1796 | deinit_String(&str); |
1753 | return; | 1797 | return; |
1754 | } | 1798 | } |
1755 | setFormat_GmDocument(d->doc, docFormat); | 1799 | setFormat_GmDocument(d->view.doc, docFormat); |
1756 | /* Convert the source to UTF-8 if needed. */ | 1800 | /* Convert the source to UTF-8 if needed. */ |
1757 | if (!equalCase_Rangecc(charset, "utf-8")) { | 1801 | if (!equalCase_Rangecc(charset, "utf-8")) { |
1758 | set_String(&str, | 1802 | set_String(&str, |
@@ -1761,7 +1805,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, | |||
1761 | } | 1805 | } |
1762 | if (cachedDoc) { | 1806 | if (cachedDoc) { |
1763 | replaceDocument_DocumentWidget_(d, cachedDoc); | 1807 | replaceDocument_DocumentWidget_(d, cachedDoc); |
1764 | updateWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d), width_Widget(d)); | 1808 | updateWidth_DocumentView_(&d->view); |
1765 | } | 1809 | } |
1766 | else if (setSource) { | 1810 | else if (setSource) { |
1767 | setSource_DocumentWidget(d, &str); | 1811 | setSource_DocumentWidget(d, &str); |
@@ -1840,9 +1884,9 @@ static void cacheDocumentGlyphs_DocumentWidget_(const iDocumentWidget *d) { | |||
1840 | /* Just cache the top of the document, since this is what we usually need. */ | 1884 | /* Just cache the top of the document, since this is what we usually need. */ |
1841 | int maxY = height_Widget(&d->widget) * 2; | 1885 | int maxY = height_Widget(&d->widget) * 2; |
1842 | if (maxY == 0) { | 1886 | if (maxY == 0) { |
1843 | maxY = size_GmDocument(d->doc).y; | 1887 | maxY = size_GmDocument(d->view.doc).y; |
1844 | } | 1888 | } |
1845 | render_GmDocument(d->doc, (iRangei){ 0, maxY }, cacheRunGlyphs_, NULL); | 1889 | render_GmDocument(d->view.doc, (iRangei){ 0, maxY }, cacheRunGlyphs_, NULL); |
1846 | } | 1890 | } |
1847 | } | 1891 | } |
1848 | 1892 | ||
@@ -1893,7 +1937,7 @@ static void addBannerWarnings_DocumentWidget_(iDocumentWidget *d) { | |||
1893 | value_SiteSpec(collectNewRange_String(urlRoot_String(d->mod.url)), | 1937 | value_SiteSpec(collectNewRange_String(urlRoot_String(d->mod.url)), |
1894 | dismissWarnings_SiteSpecKey) | | 1938 | dismissWarnings_SiteSpecKey) | |
1895 | (!prefs_App()->warnAboutMissingGlyphs ? missingGlyphs_GmDocumentWarning : 0); | 1939 | (!prefs_App()->warnAboutMissingGlyphs ? missingGlyphs_GmDocumentWarning : 0); |
1896 | const int warnings = warnings_GmDocument(d->doc) & ~dismissed; | 1940 | const int warnings = warnings_GmDocument(d->view.doc) & ~dismissed; |
1897 | if (warnings & missingGlyphs_GmDocumentWarning) { | 1941 | if (warnings & missingGlyphs_GmDocumentWarning) { |
1898 | add_Banner(d->banner, warning_BannerType, missingGlyphs_GmStatusCode, NULL, NULL); | 1942 | add_Banner(d->banner, warning_BannerType, missingGlyphs_GmStatusCode, NULL, NULL); |
1899 | /* TODO: List one or more of the missing characters and/or their Unicode blocks? */ | 1943 | /* TODO: List one or more of the missing characters and/or their Unicode blocks? */ |
@@ -1910,12 +1954,11 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n | |||
1910 | clear_ObjectList(d->media); | 1954 | clear_ObjectList(d->media); |
1911 | delete_Gempub(d->sourceGempub); | 1955 | delete_Gempub(d->sourceGempub); |
1912 | d->sourceGempub = NULL; | 1956 | d->sourceGempub = NULL; |
1913 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); | 1957 | pauseAllPlayers_Media(media_GmDocument(d->view.doc), iTrue); |
1914 | iRelease(d->doc); | ||
1915 | destroy_Widget(d->footerButtons); | 1958 | destroy_Widget(d->footerButtons); |
1916 | d->footerButtons = NULL; | 1959 | d->footerButtons = NULL; |
1917 | d->doc = new_GmDocument(); | 1960 | iRelease(d->view.doc); |
1918 | resetWideRuns_DocumentWidget_(d); | 1961 | d->view.doc = new_GmDocument(); |
1919 | d->state = fetching_RequestState; | 1962 | d->state = fetching_RequestState; |
1920 | d->flags |= fromCache_DocumentWidgetFlag; | 1963 | d->flags |= fromCache_DocumentWidgetFlag; |
1921 | /* Do the fetch. */ { | 1964 | /* Do the fetch. */ { |
@@ -1927,7 +1970,7 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n | |||
1927 | format_String(&d->sourceHeader, cstr_Lang("pageinfo.header.cached")); | 1970 | format_String(&d->sourceHeader, cstr_Lang("pageinfo.header.cached")); |
1928 | set_Block(&d->sourceContent, &resp->body); | 1971 | set_Block(&d->sourceContent, &resp->body); |
1929 | if (!cachedDoc) { | 1972 | if (!cachedDoc) { |
1930 | setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d), width_Widget(d)); | 1973 | updateWidthAndRedoLayout_DocumentView_(&d->view); |
1931 | } | 1974 | } |
1932 | updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue); | 1975 | updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue); |
1933 | clear_Banner(d->banner); | 1976 | clear_Banner(d->banner); |
@@ -1936,14 +1979,13 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n | |||
1936 | } | 1979 | } |
1937 | d->state = ready_RequestState; | 1980 | d->state = ready_RequestState; |
1938 | postProcessRequestContent_DocumentWidget_(d, iTrue); | 1981 | postProcessRequestContent_DocumentWidget_(d, iTrue); |
1939 | init_Anim(&d->altTextOpacity, 0); | 1982 | resetScroll_DocumentView_(&d->view); |
1940 | reset_SmoothScroll(&d->scrollY); | 1983 | init_Anim(&d->view.scrollY.pos, d->initNormScrollY * pageHeight_DocumentView_(&d->view)); |
1941 | init_Anim(&d->scrollY.pos, d->initNormScrollY * pageHeight_DocumentWidget_(d)); | 1984 | updateVisible_DocumentView_(&d->view); |
1942 | updateSideOpacity_DocumentWidget_(d, iFalse); | 1985 | moveSpan_SmoothScroll(&d->view.scrollY, 0, 0); /* clamp position to new max */ |
1943 | updateVisible_DocumentWidget_(d); | 1986 | updateSideOpacity_DocumentView_(&d->view, iFalse); |
1944 | moveSpan_SmoothScroll(&d->scrollY, 0, 0); /* clamp position to new max */ | ||
1945 | cacheDocumentGlyphs_DocumentWidget_(d); | 1987 | cacheDocumentGlyphs_DocumentWidget_(d); |
1946 | d->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; | 1988 | d->view.drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; |
1947 | d->flags &= ~(urlChanged_DocumentWidgetFlag | drawDownloadCounter_DocumentWidgetFlag); | 1989 | d->flags &= ~(urlChanged_DocumentWidgetFlag | drawDownloadCounter_DocumentWidgetFlag); |
1948 | postCommandf_Root( | 1990 | postCommandf_Root( |
1949 | as_Widget(d)->root, "document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | 1991 | as_Widget(d)->root, "document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); |
@@ -1959,7 +2001,7 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { | |||
1959 | d, recent->normScrollY, recent->cachedResponse, recent->cachedDoc); | 2001 | d, recent->normScrollY, recent->cachedResponse, recent->cachedDoc); |
1960 | if (!recent->cachedDoc) { | 2002 | if (!recent->cachedDoc) { |
1961 | /* We have a cached copy now. */ | 2003 | /* We have a cached copy now. */ |
1962 | setCachedDocument_History(d->mod.history, d->doc, iFalse); | 2004 | setCachedDocument_History(d->mod.history, d->view.doc, iFalse); |
1963 | } | 2005 | } |
1964 | return iTrue; | 2006 | return iTrue; |
1965 | } | 2007 | } |
@@ -1974,23 +2016,25 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { | |||
1974 | } | 2016 | } |
1975 | 2017 | ||
1976 | static void refreshWhileScrolling_DocumentWidget_(iAny *ptr) { | 2018 | static void refreshWhileScrolling_DocumentWidget_(iAny *ptr) { |
2019 | iAssert(isInstance_Object(ptr, &Class_DocumentWidget)); | ||
1977 | iDocumentWidget *d = ptr; | 2020 | iDocumentWidget *d = ptr; |
1978 | updateVisible_DocumentWidget_(d); | 2021 | iDocumentView *view = &d->view; |
2022 | updateVisible_DocumentView_(view); | ||
1979 | refresh_Widget(d); | 2023 | refresh_Widget(d); |
1980 | if (d->animWideRunId) { | 2024 | if (view->animWideRunId) { |
1981 | for (const iGmRun *r = d->animWideRunRange.start; r != d->animWideRunRange.end; r++) { | 2025 | for (const iGmRun *r = view->animWideRunRange.start; r != view->animWideRunRange.end; r++) { |
1982 | insert_PtrSet(d->invalidRuns, r); | 2026 | insert_PtrSet(view->invalidRuns, r); |
1983 | } | 2027 | } |
1984 | } | 2028 | } |
1985 | if (isFinished_Anim(&d->animWideRunOffset)) { | 2029 | if (isFinished_Anim(&view->animWideRunOffset)) { |
1986 | d->animWideRunId = 0; | 2030 | view->animWideRunId = 0; |
1987 | } | 2031 | } |
1988 | if (!isFinished_SmoothScroll(&d->scrollY) || !isFinished_Anim(&d->animWideRunOffset)) { | 2032 | if (!isFinished_SmoothScroll(&view->scrollY) || !isFinished_Anim(&view->animWideRunOffset)) { |
1989 | addTicker_App(refreshWhileScrolling_DocumentWidget_, d); | 2033 | addTicker_App(refreshWhileScrolling_DocumentWidget_, d); |
1990 | } | 2034 | } |
1991 | if (isFinished_SmoothScroll(&d->scrollY)) { | 2035 | if (isFinished_SmoothScroll(&view->scrollY)) { |
1992 | iChangeFlags(d->flags, noHoverWhileScrolling_DocumentWidgetFlag, iFalse); | 2036 | iChangeFlags(d->flags, noHoverWhileScrolling_DocumentWidgetFlag, iFalse); |
1993 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window(), 0)); | 2037 | updateHover_DocumentView_(view, mouseCoord_Window(get_Window(), 0)); |
1994 | } | 2038 | } |
1995 | } | 2039 | } |
1996 | 2040 | ||
@@ -1999,16 +2043,16 @@ static void scrollBegan_DocumentWidget_(iAnyObject *any, int offset, uint32_t du | |||
1999 | /* Get rid of link numbers when scrolling. */ | 2043 | /* Get rid of link numbers when scrolling. */ |
2000 | if (offset && d->flags & showLinkNumbers_DocumentWidgetFlag) { | 2044 | if (offset && d->flags & showLinkNumbers_DocumentWidgetFlag) { |
2001 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 2045 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
2002 | invalidateVisibleLinks_DocumentWidget_(d); | 2046 | invalidateVisibleLinks_DocumentView_(&d->view); |
2003 | } | 2047 | } |
2004 | /* Show and hide toolbar on scroll. */ | 2048 | /* Show and hide toolbar on scroll. */ |
2005 | if (deviceType_App() == phone_AppDeviceType) { | 2049 | if (deviceType_App() == phone_AppDeviceType) { |
2006 | const float normPos = normScrollPos_DocumentWidget_(d); | 2050 | const float normPos = normScrollPos_DocumentView_(&d->view); |
2007 | if (prefs_App()->hideToolbarOnScroll && iAbs(offset) > 5 && normPos >= 0) { | 2051 | if (prefs_App()->hideToolbarOnScroll && iAbs(offset) > 5 && normPos >= 0) { |
2008 | showToolbar_Root(as_Widget(d)->root, offset < 0); | 2052 | showToolbar_Root(as_Widget(d)->root, offset < 0); |
2009 | } | 2053 | } |
2010 | } | 2054 | } |
2011 | updateVisible_DocumentWidget_(d); | 2055 | updateVisible_DocumentView_(&d->view); |
2012 | refresh_Widget(as_Widget(d)); | 2056 | refresh_Widget(as_Widget(d)); |
2013 | if (duration > 0) { | 2057 | if (duration > 0) { |
2014 | iChangeFlags(d->flags, noHoverWhileScrolling_DocumentWidgetFlag, iTrue); | 2058 | iChangeFlags(d->flags, noHoverWhileScrolling_DocumentWidgetFlag, iTrue); |
@@ -2016,47 +2060,47 @@ static void scrollBegan_DocumentWidget_(iAnyObject *any, int offset, uint32_t du | |||
2016 | } | 2060 | } |
2017 | } | 2061 | } |
2018 | 2062 | ||
2019 | static void clampScroll_DocumentWidget_(iDocumentWidget *d) { | 2063 | static void clampScroll_DocumentView_(iDocumentView *d) { |
2020 | move_SmoothScroll(&d->scrollY, 0); | 2064 | move_SmoothScroll(&d->scrollY, 0); |
2021 | } | 2065 | } |
2022 | 2066 | ||
2023 | static void immediateScroll_DocumentWidget_(iDocumentWidget *d, int offset) { | 2067 | static void immediateScroll_DocumentView_(iDocumentView *d, int offset) { |
2024 | move_SmoothScroll(&d->scrollY, offset); | 2068 | move_SmoothScroll(&d->scrollY, offset); |
2025 | } | 2069 | } |
2026 | 2070 | ||
2027 | static void smoothScroll_DocumentWidget_(iDocumentWidget *d, int offset, int duration) { | 2071 | static void smoothScroll_DocumentView_(iDocumentView *d, int offset, int duration) { |
2028 | moveSpan_SmoothScroll(&d->scrollY, offset, duration); | 2072 | moveSpan_SmoothScroll(&d->scrollY, offset, duration); |
2029 | } | 2073 | } |
2030 | 2074 | ||
2031 | static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY, iBool centered) { | 2075 | static void scrollTo_DocumentView_(iDocumentView *d, int documentY, iBool centered) { |
2032 | if (!isEmpty_Banner(d->banner)) { | 2076 | if (!isEmpty_Banner(d->owner->banner)) { |
2033 | documentY += height_Banner(d->banner) + documentTopPad_DocumentWidget_(d); | 2077 | documentY += height_Banner(d->owner->banner) + documentTopPad_DocumentView_(d); |
2034 | } | 2078 | } |
2035 | else { | 2079 | else { |
2036 | documentY += documentTopPad_DocumentWidget_(d) + d->pageMargin * gap_UI; | 2080 | documentY += documentTopPad_DocumentView_(d) + d->pageMargin * gap_UI; |
2037 | } | 2081 | } |
2038 | init_Anim(&d->scrollY.pos, | 2082 | init_Anim(&d->scrollY.pos, |
2039 | documentY - (centered ? documentBounds_DocumentWidget_(d).size.y / 2 | 2083 | documentY - (centered ? documentBounds_DocumentView_(d).size.y / 2 |
2040 | : lineHeight_Text(paragraph_FontId))); | 2084 | : lineHeight_Text(paragraph_FontId))); |
2041 | clampScroll_DocumentWidget_(d); | 2085 | clampScroll_DocumentView_(d); |
2042 | } | 2086 | } |
2043 | 2087 | ||
2044 | static void scrollToHeading_DocumentWidget_(iDocumentWidget *d, const char *heading) { | 2088 | static void scrollToHeading_DocumentView_(iDocumentView *d, const char *heading) { |
2045 | iConstForEach(Array, h, headings_GmDocument(d->doc)) { | 2089 | iConstForEach(Array, h, headings_GmDocument(d->doc)) { |
2046 | const iGmHeading *head = h.value; | 2090 | const iGmHeading *head = h.value; |
2047 | if (startsWithCase_Rangecc(head->text, heading)) { | 2091 | if (startsWithCase_Rangecc(head->text, heading)) { |
2048 | postCommandf_Root(as_Widget(d)->root, "document.goto loc:%p", head->text.start); | 2092 | postCommandf_Root(as_Widget(d->owner)->root, "document.goto loc:%p", head->text.start); |
2049 | break; | 2093 | break; |
2050 | } | 2094 | } |
2051 | } | 2095 | } |
2052 | } | 2096 | } |
2053 | 2097 | ||
2054 | static iBool scrollWideBlock_DocumentWidget_(iDocumentWidget *d, iInt2 mousePos, int delta, | 2098 | static iBool scrollWideBlock_DocumentView_(iDocumentView *d, iInt2 mousePos, int delta, |
2055 | int duration) { | 2099 | int duration) { |
2056 | if (delta == 0 || d->flags & eitherWheelSwipe_DocumentWidgetFlag) { | 2100 | if (delta == 0 || d->owner->flags & eitherWheelSwipe_DocumentWidgetFlag) { |
2057 | return iFalse; | 2101 | return iFalse; |
2058 | } | 2102 | } |
2059 | const iInt2 docPos = documentPos_DocumentWidget_(d, mousePos); | 2103 | const iInt2 docPos = documentPos_DocumentView_(d, mousePos); |
2060 | iConstForEach(PtrArray, i, &d->visibleWideRuns) { | 2104 | iConstForEach(PtrArray, i, &d->visibleWideRuns) { |
2061 | const iGmRun *run = i.ptr; | 2105 | const iGmRun *run = i.ptr; |
2062 | if (docPos.y >= top_Rect(run->bounds) && docPos.y <= bottom_Rect(run->bounds)) { | 2106 | if (docPos.y >= top_Rect(run->bounds) && docPos.y <= bottom_Rect(run->bounds)) { |
@@ -2066,7 +2110,7 @@ static iBool scrollWideBlock_DocumentWidget_(iDocumentWidget *d, iInt2 mousePos, | |||
2066 | for (const iGmRun *r = range.start; r != range.end; r++) { | 2110 | for (const iGmRun *r = range.start; r != range.end; r++) { |
2067 | maxWidth = iMax(maxWidth, width_Rect(r->visBounds)); | 2111 | maxWidth = iMax(maxWidth, width_Rect(r->visBounds)); |
2068 | } | 2112 | } |
2069 | const int maxOffset = maxWidth - documentWidth_DocumentWidget_(d) + d->pageMargin * gap_UI; | 2113 | const int maxOffset = maxWidth - documentWidth_DocumentView_(d) + d->pageMargin * gap_UI; |
2070 | if (size_Array(&d->wideRunOffsets) <= preId_GmRun(run)) { | 2114 | if (size_Array(&d->wideRunOffsets) <= preId_GmRun(run)) { |
2071 | resize_Array(&d->wideRunOffsets, preId_GmRun(run) + 1); | 2115 | resize_Array(&d->wideRunOffsets, preId_GmRun(run) + 1); |
2072 | } | 2116 | } |
@@ -2079,8 +2123,8 @@ static iBool scrollWideBlock_DocumentWidget_(iDocumentWidget *d, iInt2 mousePos, | |||
2079 | insert_PtrSet(d->invalidRuns, r); | 2123 | insert_PtrSet(d->invalidRuns, r); |
2080 | } | 2124 | } |
2081 | refresh_Widget(d); | 2125 | refresh_Widget(d); |
2082 | d->selectMark = iNullRange; | 2126 | d->owner->selectMark = iNullRange; |
2083 | d->foundMark = iNullRange; | 2127 | d->owner->foundMark = iNullRange; |
2084 | } | 2128 | } |
2085 | if (duration) { | 2129 | if (duration) { |
2086 | if (d->animWideRunId != preId_GmRun(run) || isFinished_Anim(&d->animWideRunOffset)) { | 2130 | if (d->animWideRunId != preId_GmRun(run) || isFinished_Anim(&d->animWideRunOffset)) { |
@@ -2102,13 +2146,13 @@ static iBool scrollWideBlock_DocumentWidget_(iDocumentWidget *d, iInt2 mousePos, | |||
2102 | } | 2146 | } |
2103 | 2147 | ||
2104 | static void togglePreFold_DocumentWidget_(iDocumentWidget *d, uint16_t preId) { | 2148 | static void togglePreFold_DocumentWidget_(iDocumentWidget *d, uint16_t preId) { |
2105 | d->hoverPre = NULL; | 2149 | d->view.hoverPre = NULL; |
2106 | d->hoverAltPre = NULL; | 2150 | d->view.hoverAltPre = NULL; |
2107 | d->selectMark = iNullRange; | 2151 | d->selectMark = iNullRange; |
2108 | foldPre_GmDocument(d->doc, preId); | 2152 | foldPre_GmDocument(d->view.doc, preId); |
2109 | redoLayout_GmDocument(d->doc); | 2153 | redoLayout_GmDocument(d->view.doc); |
2110 | clampScroll_DocumentWidget_(d); | 2154 | clampScroll_DocumentView_(&d->view); |
2111 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window(), 0)); | 2155 | updateHover_DocumentView_(&d->view, mouseCoord_Window(get_Window(), 0)); |
2112 | invalidate_DocumentWidget_(d); | 2156 | invalidate_DocumentWidget_(d); |
2113 | refresh_Widget(as_Widget(d)); | 2157 | refresh_Widget(as_Widget(d)); |
2114 | } | 2158 | } |
@@ -2188,8 +2232,8 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
2188 | equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini")) { | 2232 | equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini")) { |
2189 | statusCode = tlsServerCertificateNotVerified_GmStatusCode; | 2233 | statusCode = tlsServerCertificateNotVerified_GmStatusCode; |
2190 | } | 2234 | } |
2191 | init_Anim(&d->sideOpacity, 0); | 2235 | init_Anim(&d->view.sideOpacity, 0); |
2192 | init_Anim(&d->altTextOpacity, 0); | 2236 | init_Anim(&d->view.altTextOpacity, 0); |
2193 | format_String(&d->sourceHeader, | 2237 | format_String(&d->sourceHeader, |
2194 | "%s%s", | 2238 | "%s%s", |
2195 | humanReadableStatusCode_(statusCode), | 2239 | humanReadableStatusCode_(statusCode), |
@@ -2283,17 +2327,17 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
2283 | case categorySuccess_GmStatusCode: | 2327 | case categorySuccess_GmStatusCode: |
2284 | if (d->flags & urlChanged_DocumentWidgetFlag) { | 2328 | if (d->flags & urlChanged_DocumentWidgetFlag) { |
2285 | /* Keep scroll position when reloading the same page. */ | 2329 | /* Keep scroll position when reloading the same page. */ |
2286 | reset_SmoothScroll(&d->scrollY); | 2330 | resetScroll_DocumentView_(&d->view); |
2287 | } | 2331 | } |
2288 | d->scrollY.pullActionTriggered = 0; | 2332 | d->view.scrollY.pullActionTriggered = 0; |
2289 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); | 2333 | pauseAllPlayers_Media(media_GmDocument(d->view.doc), iTrue); |
2290 | iRelease(d->doc); /* new content incoming */ | 2334 | iReleasePtr(&d->view.doc); /* new content incoming */ |
2291 | d->doc = new_GmDocument(); | ||
2292 | delete_Gempub(d->sourceGempub); | 2335 | delete_Gempub(d->sourceGempub); |
2293 | d->sourceGempub = NULL; | 2336 | d->sourceGempub = NULL; |
2294 | destroy_Widget(d->footerButtons); | 2337 | destroy_Widget(d->footerButtons); |
2295 | d->footerButtons = NULL; | 2338 | d->footerButtons = NULL; |
2296 | resetWideRuns_DocumentWidget_(d); | 2339 | d->view.doc = new_GmDocument(); |
2340 | resetWideRuns_DocumentView_(&d->view); | ||
2297 | updateDocument_DocumentWidget_(d, resp, NULL, iTrue); | 2341 | updateDocument_DocumentWidget_(d, resp, NULL, iTrue); |
2298 | break; | 2342 | break; |
2299 | case categoryRedirect_GmStatusCode: | 2343 | case categoryRedirect_GmStatusCode: |
@@ -2361,8 +2405,8 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
2361 | unlockResponse_GmRequest(d->request); | 2405 | unlockResponse_GmRequest(d->request); |
2362 | } | 2406 | } |
2363 | 2407 | ||
2364 | static iRangecc sourceLoc_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { | 2408 | static iRangecc sourceLoc_DocumentView_(const iDocumentView *d, iInt2 pos) { |
2365 | return findLoc_GmDocument(d->doc, documentPos_DocumentWidget_(d, pos)); | 2409 | return findLoc_GmDocument(d->doc, documentPos_DocumentView_(d, pos)); |
2366 | } | 2410 | } |
2367 | 2411 | ||
2368 | iDeclareType(MiddleRunParams) | 2412 | iDeclareType(MiddleRunParams) |
@@ -2385,8 +2429,8 @@ static void find_MiddleRunParams_(void *params, const iGmRun *run) { | |||
2385 | } | 2429 | } |
2386 | } | 2430 | } |
2387 | 2431 | ||
2388 | static const iGmRun *middleRun_DocumentWidget_(const iDocumentWidget *d) { | 2432 | static const iGmRun *middleRun_DocumentView_(const iDocumentView *d) { |
2389 | iRangei visRange = visibleRange_DocumentWidget_(d); | 2433 | iRangei visRange = visibleRange_DocumentView_(d); |
2390 | iMiddleRunParams params = { (visRange.start + visRange.end) / 2, NULL, 0 }; | 2434 | iMiddleRunParams params = { (visRange.start + visRange.end) / 2, NULL, 0 }; |
2391 | render_GmDocument(d->doc, visRange, find_MiddleRunParams_, ¶ms); | 2435 | render_GmDocument(d->doc, visRange, find_MiddleRunParams_, ¶ms); |
2392 | return params.closest; | 2436 | return params.closest; |
@@ -2414,7 +2458,7 @@ static iMediaRequest *findMediaRequest_DocumentWidget_(const iDocumentWidget *d, | |||
2414 | 2458 | ||
2415 | static iBool requestMedia_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId, iBool enableFilters) { | 2459 | static iBool requestMedia_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId, iBool enableFilters) { |
2416 | if (!findMediaRequest_DocumentWidget_(d, linkId)) { | 2460 | if (!findMediaRequest_DocumentWidget_(d, linkId)) { |
2417 | const iString *mediaUrl = absoluteUrl_String(d->mod.url, linkUrl_GmDocument(d->doc, linkId)); | 2461 | const iString *mediaUrl = absoluteUrl_String(d->mod.url, linkUrl_GmDocument(d->view.doc, linkId)); |
2418 | pushBack_ObjectList(d->media, iClob(new_MediaRequest(d, linkId, mediaUrl, enableFilters))); | 2462 | pushBack_ObjectList(d->media, iClob(new_MediaRequest(d, linkId, mediaUrl, enableFilters))); |
2419 | invalidate_DocumentWidget_(d); | 2463 | invalidate_DocumentWidget_(d); |
2420 | return iTrue; | 2464 | return iTrue; |
@@ -2423,7 +2467,7 @@ static iBool requestMedia_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId, | |||
2423 | } | 2467 | } |
2424 | 2468 | ||
2425 | static iBool isDownloadRequest_DocumentWidget(const iDocumentWidget *d, const iMediaRequest *req) { | 2469 | static iBool isDownloadRequest_DocumentWidget(const iDocumentWidget *d, const iMediaRequest *req) { |
2426 | return findMediaForLink_Media(constMedia_GmDocument(d->doc), req->linkId, download_MediaType).type != 0; | 2470 | return findMediaForLink_Media(constMedia_GmDocument(d->view.doc), req->linkId, download_MediaType).type != 0; |
2427 | } | 2471 | } |
2428 | 2472 | ||
2429 | static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | 2473 | static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { |
@@ -2447,21 +2491,21 @@ static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char * | |||
2447 | if (isDownloadRequest_DocumentWidget(d, req) || | 2491 | if (isDownloadRequest_DocumentWidget(d, req) || |
2448 | startsWith_String(&resp->meta, "audio/")) { | 2492 | startsWith_String(&resp->meta, "audio/")) { |
2449 | /* TODO: Use a helper? This is same as below except for the partialData flag. */ | 2493 | /* TODO: Use a helper? This is same as below except for the partialData flag. */ |
2450 | if (setData_Media(media_GmDocument(d->doc), | 2494 | if (setData_Media(media_GmDocument(d->view.doc), |
2451 | req->linkId, | 2495 | req->linkId, |
2452 | &resp->meta, | 2496 | &resp->meta, |
2453 | &resp->body, | 2497 | &resp->body, |
2454 | partialData_MediaFlag | allowHide_MediaFlag)) { | 2498 | partialData_MediaFlag | allowHide_MediaFlag)) { |
2455 | redoLayout_GmDocument(d->doc); | 2499 | redoLayout_GmDocument(d->view.doc); |
2456 | } | 2500 | } |
2457 | updateVisible_DocumentWidget_(d); | 2501 | updateVisible_DocumentView_(&d->view); |
2458 | invalidate_DocumentWidget_(d); | 2502 | invalidate_DocumentWidget_(d); |
2459 | refresh_Widget(as_Widget(d)); | 2503 | refresh_Widget(as_Widget(d)); |
2460 | } | 2504 | } |
2461 | unlockResponse_GmRequest(req->req); | 2505 | unlockResponse_GmRequest(req->req); |
2462 | } | 2506 | } |
2463 | /* Update the link's progress. */ | 2507 | /* Update the link's progress. */ |
2464 | invalidateLink_DocumentWidget_(d, req->linkId); | 2508 | invalidateLink_DocumentView_(&d->view, req->linkId); |
2465 | refresh_Widget(d); | 2509 | refresh_Widget(d); |
2466 | return iTrue; | 2510 | return iTrue; |
2467 | } | 2511 | } |
@@ -2472,14 +2516,14 @@ static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char * | |||
2472 | if (isDownloadRequest_DocumentWidget(d, req) || | 2516 | if (isDownloadRequest_DocumentWidget(d, req) || |
2473 | startsWith_String(meta_GmRequest(req->req), "image/") || | 2517 | startsWith_String(meta_GmRequest(req->req), "image/") || |
2474 | startsWith_String(meta_GmRequest(req->req), "audio/")) { | 2518 | startsWith_String(meta_GmRequest(req->req), "audio/")) { |
2475 | setData_Media(media_GmDocument(d->doc), | 2519 | setData_Media(media_GmDocument(d->view.doc), |
2476 | req->linkId, | 2520 | req->linkId, |
2477 | meta_GmRequest(req->req), | 2521 | meta_GmRequest(req->req), |
2478 | body_GmRequest(req->req), | 2522 | body_GmRequest(req->req), |
2479 | allowHide_MediaFlag); | 2523 | allowHide_MediaFlag); |
2480 | redoLayout_GmDocument(d->doc); | 2524 | redoLayout_GmDocument(d->view.doc); |
2481 | iZap(d->visibleRuns); /* pointers invalidated */ | 2525 | iZap(d->view.visibleRuns); /* pointers invalidated */ |
2482 | updateVisible_DocumentWidget_(d); | 2526 | updateVisible_DocumentView_(&d->view); |
2483 | invalidate_DocumentWidget_(d); | 2527 | invalidate_DocumentWidget_(d); |
2484 | refresh_Widget(as_Widget(d)); | 2528 | refresh_Widget(as_Widget(d)); |
2485 | } | 2529 | } |
@@ -2494,8 +2538,8 @@ static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char * | |||
2494 | return iFalse; | 2538 | return iFalse; |
2495 | } | 2539 | } |
2496 | 2540 | ||
2497 | static void allocVisBuffer_DocumentWidget_(const iDocumentWidget *d) { | 2541 | static void allocVisBuffer_DocumentView_(const iDocumentView *d) { |
2498 | const iWidget *w = constAs_Widget(d); | 2542 | const iWidget *w = constAs_Widget(d->owner); |
2499 | const iBool isVisible = isVisible_Widget(w); | 2543 | const iBool isVisible = isVisible_Widget(w); |
2500 | const iInt2 size = bounds_Widget(w).size; | 2544 | const iInt2 size = bounds_Widget(w).size; |
2501 | if (isVisible) { | 2545 | if (isVisible) { |
@@ -2507,12 +2551,12 @@ static void allocVisBuffer_DocumentWidget_(const iDocumentWidget *d) { | |||
2507 | } | 2551 | } |
2508 | 2552 | ||
2509 | static iBool fetchNextUnfetchedImage_DocumentWidget_(iDocumentWidget *d) { | 2553 | static iBool fetchNextUnfetchedImage_DocumentWidget_(iDocumentWidget *d) { |
2510 | iConstForEach(PtrArray, i, &d->visibleLinks) { | 2554 | iConstForEach(PtrArray, i, &d->view.visibleLinks) { |
2511 | const iGmRun *run = i.ptr; | 2555 | const iGmRun *run = i.ptr; |
2512 | if (run->linkId && run->mediaType == none_MediaType && | 2556 | if (run->linkId && run->mediaType == none_MediaType && |
2513 | ~run->flags & decoration_GmRunFlag) { | 2557 | ~run->flags & decoration_GmRunFlag) { |
2514 | const int linkFlags = linkFlags_GmDocument(d->doc, run->linkId); | 2558 | const int linkFlags = linkFlags_GmDocument(d->view.doc, run->linkId); |
2515 | if (isMediaLink_GmDocument(d->doc, run->linkId) && | 2559 | if (isMediaLink_GmDocument(d->view.doc, run->linkId) && |
2516 | linkFlags & imageFileExtension_GmLinkFlag && | 2560 | linkFlags & imageFileExtension_GmLinkFlag && |
2517 | ~linkFlags & content_GmLinkFlag && ~linkFlags & permanent_GmLinkFlag ) { | 2561 | ~linkFlags & content_GmLinkFlag && ~linkFlags & permanent_GmLinkFlag ) { |
2518 | if (requestMedia_DocumentWidget_(d, run->linkId, iTrue)) { | 2562 | if (requestMedia_DocumentWidget_(d, run->linkId, iTrue)) { |
@@ -2570,9 +2614,9 @@ static void addAllLinks_(void *context, const iGmRun *run) { | |||
2570 | } | 2614 | } |
2571 | } | 2615 | } |
2572 | 2616 | ||
2573 | static size_t visibleLinkOrdinal_DocumentWidget_(const iDocumentWidget *d, iGmLinkId linkId) { | 2617 | static size_t visibleLinkOrdinal_DocumentView_(const iDocumentView *d, iGmLinkId linkId) { |
2574 | size_t ord = 0; | 2618 | size_t ord = 0; |
2575 | const iRangei visRange = visibleRange_DocumentWidget_(d); | 2619 | const iRangei visRange = visibleRange_DocumentView_(d); |
2576 | iConstForEach(PtrArray, i, &d->visibleLinks) { | 2620 | iConstForEach(PtrArray, i, &d->visibleLinks) { |
2577 | const iGmRun *run = i.ptr; | 2621 | const iGmRun *run = i.ptr; |
2578 | if (top_Rect(run->visBounds) >= visRange.start + gap_UI * d->pageMargin * 4 / 5) { | 2622 | if (top_Rect(run->visBounds) >= visRange.start + gap_UI * d->pageMargin * 4 / 5) { |
@@ -2598,38 +2642,36 @@ static const int homeRowKeys_[] = { | |||
2598 | 't', 'y', | 2642 | 't', 'y', |
2599 | }; | 2643 | }; |
2600 | 2644 | ||
2601 | static iBool updateDocumentWidthRetainingScrollPosition_DocumentWidget_(iDocumentWidget *d, | 2645 | static iBool updateDocumentWidthRetainingScrollPosition_DocumentView_(iDocumentView *d, |
2602 | iBool keepCenter) { | 2646 | iBool keepCenter) { |
2603 | const int newWidth = documentWidth_DocumentWidget_(d); | 2647 | const int newWidth = documentWidth_DocumentView_(d); |
2604 | if (newWidth == size_GmDocument(d->doc).x && !keepCenter /* not a font change */) { | 2648 | if (newWidth == size_GmDocument(d->doc).x && !keepCenter /* not a font change */) { |
2605 | return iFalse; | 2649 | return iFalse; |
2606 | } | 2650 | } |
2607 | /* Font changes (i.e., zooming) will keep the view centered, otherwise keep the top | 2651 | /* Font changes (i.e., zooming) will keep the view centered, otherwise keep the top |
2608 | of the visible area fixed. */ | 2652 | of the visible area fixed. */ |
2609 | const iGmRun *run = keepCenter ? middleRun_DocumentWidget_(d) : d->visibleRuns.start; | 2653 | const iGmRun *run = keepCenter ? middleRun_DocumentView_(d) : d->visibleRuns.start; |
2610 | const char * runLoc = (run ? run->text.start : NULL); | 2654 | const char * runLoc = (run ? run->text.start : NULL); |
2611 | int voffset = 0; | 2655 | int voffset = 0; |
2612 | if (!keepCenter && run) { | 2656 | if (!keepCenter && run) { |
2613 | /* Keep the first visible run visible at the same position. */ | 2657 | /* Keep the first visible run visible at the same position. */ |
2614 | /* TODO: First *fully* visible run? */ | 2658 | /* TODO: First *fully* visible run? */ |
2615 | voffset = visibleRange_DocumentWidget_(d).start - top_Rect(run->visBounds); | 2659 | voffset = visibleRange_DocumentView_(d).start - top_Rect(run->visBounds); |
2616 | } | 2660 | } |
2617 | setWidth_GmDocument(d->doc, newWidth, width_Widget(d)); | 2661 | setWidth_GmDocument(d->doc, newWidth, width_Widget(d->owner)); |
2618 | setWidth_Banner(d->banner, newWidth); | 2662 | setWidth_Banner(d->owner->banner, newWidth); |
2619 | documentRunsInvalidated_DocumentWidget_(d); | 2663 | documentRunsInvalidated_DocumentWidget_(d->owner); |
2620 | if (runLoc && !keepCenter) { | 2664 | if (runLoc && !keepCenter) { |
2621 | run = findRunAtLoc_GmDocument(d->doc, runLoc); | 2665 | run = findRunAtLoc_GmDocument(d->doc, runLoc); |
2622 | if (run) { | 2666 | if (run) { |
2623 | scrollTo_DocumentWidget_(d, | 2667 | scrollTo_DocumentView_( |
2624 | top_Rect(run->visBounds) + | 2668 | d, top_Rect(run->visBounds) + lineHeight_Text(paragraph_FontId) + voffset, iFalse); |
2625 | lineHeight_Text(paragraph_FontId) + voffset, | ||
2626 | iFalse); | ||
2627 | } | 2669 | } |
2628 | } | 2670 | } |
2629 | else if (runLoc && keepCenter) { | 2671 | else if (runLoc && keepCenter) { |
2630 | run = findRunAtLoc_GmDocument(d->doc, runLoc); | 2672 | run = findRunAtLoc_GmDocument(d->doc, runLoc); |
2631 | if (run) { | 2673 | if (run) { |
2632 | scrollTo_DocumentWidget_(d, mid_Rect(run->bounds).y, iTrue); | 2674 | scrollTo_DocumentView_(d, mid_Rect(run->bounds).y, iTrue); |
2633 | } | 2675 | } |
2634 | } | 2676 | } |
2635 | return iTrue; | 2677 | return iTrue; |
@@ -2662,21 +2704,26 @@ static iBool handlePinch_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | |||
2662 | return iTrue; | 2704 | return iTrue; |
2663 | } | 2705 | } |
2664 | 2706 | ||
2707 | static void swap_DocumentView_(iDocumentView *d, iDocumentView *swapBuffersWith) { | ||
2708 | d->scrollY = swapBuffersWith->scrollY; | ||
2709 | d->scrollY.widget = as_Widget(d->owner); | ||
2710 | iSwap(iVisBuf *, d->visBuf, swapBuffersWith->visBuf); | ||
2711 | iSwap(iVisBufMeta *, d->visBufMeta, swapBuffersWith->visBufMeta); | ||
2712 | iSwap(iDrawBufs *, d->drawBufs, swapBuffersWith->drawBufs); | ||
2713 | updateVisible_DocumentView_(d); | ||
2714 | updateVisible_DocumentView_(swapBuffersWith); | ||
2715 | } | ||
2716 | |||
2665 | static void swap_DocumentWidget_(iDocumentWidget *d, iGmDocument *doc, | 2717 | static void swap_DocumentWidget_(iDocumentWidget *d, iGmDocument *doc, |
2666 | iDocumentWidget *swapBuffersWith) { | 2718 | iDocumentWidget *swapBuffersWith) { |
2667 | if (doc) { | 2719 | if (doc) { |
2668 | iAssert(isInstance_Object(doc, &Class_GmDocument)); | 2720 | iAssert(isInstance_Object(doc, &Class_GmDocument)); |
2669 | replaceDocument_DocumentWidget_(d, doc); | 2721 | replaceDocument_DocumentWidget_(d, doc); |
2670 | d->scrollY = swapBuffersWith->scrollY; | ||
2671 | d->scrollY.widget = as_Widget(d); | ||
2672 | iSwap(iBanner *, d->banner, swapBuffersWith->banner); | 2722 | iSwap(iBanner *, d->banner, swapBuffersWith->banner); |
2673 | setOwner_Banner(d->banner, d); | 2723 | setOwner_Banner(d->banner, d); |
2674 | setOwner_Banner(swapBuffersWith->banner, swapBuffersWith); | 2724 | setOwner_Banner(swapBuffersWith->banner, swapBuffersWith); |
2675 | iSwap(iVisBuf *, d->visBuf, swapBuffersWith->visBuf); | 2725 | swap_DocumentView_(&d->view, &swapBuffersWith->view); |
2676 | iSwap(iVisBufMeta *, d->visBufMeta, swapBuffersWith->visBufMeta); | 2726 | // invalidate_DocumentWidget_(swapBuffersWith); |
2677 | iSwap(iDrawBufs *, d->drawBufs, swapBuffersWith->drawBufs); | ||
2678 | updateVisible_DocumentWidget_(d); | ||
2679 | invalidate_DocumentWidget_(swapBuffersWith); | ||
2680 | } | 2727 | } |
2681 | } | 2728 | } |
2682 | 2729 | ||
@@ -2803,7 +2850,7 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | |||
2803 | target->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos)); | 2850 | target->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos)); |
2804 | target->widget.rect.size = d->widget.rect.size; | 2851 | target->widget.rect.size = d->widget.rect.size; |
2805 | setFlags_Widget(as_Widget(target), fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); | 2852 | setFlags_Widget(as_Widget(target), fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); |
2806 | swap_DocumentWidget_(target, d->doc, d); | 2853 | swap_DocumentWidget_(target, d->view.doc, d); |
2807 | addChildPos_Widget(swipeParent, iClob(target), front_WidgetAddPos); | 2854 | addChildPos_Widget(swipeParent, iClob(target), front_WidgetAddPos); |
2808 | setFlags_Widget(as_Widget(target), refChildrenOffset_WidgetFlag, iTrue); | 2855 | setFlags_Widget(as_Widget(target), refChildrenOffset_WidgetFlag, iTrue); |
2809 | as_Widget(target)->offsetRef = parent_Widget(w); | 2856 | as_Widget(target)->offsetRef = parent_Widget(w); |
@@ -2837,7 +2884,7 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | |||
2837 | /* TODO: Currently not animated! What exactly is the appropriate thing to do here? */ | 2884 | /* TODO: Currently not animated! What exactly is the appropriate thing to do here? */ |
2838 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); | 2885 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); |
2839 | iDocumentWidget *swipeOut = findChild_Widget(swipeParent, "swipeout"); | 2886 | iDocumentWidget *swipeOut = findChild_Widget(swipeParent, "swipeout"); |
2840 | swap_DocumentWidget_(d, swipeOut->doc, swipeOut); | 2887 | swap_DocumentWidget_(d, swipeOut->view.doc, swipeOut); |
2841 | // const int visOff = visualOffsetByReference_Widget(w); | 2888 | // const int visOff = visualOffsetByReference_Widget(w); |
2842 | w->offsetRef = NULL; | 2889 | w->offsetRef = NULL; |
2843 | // setVisualOffset_Widget(w, visOff, 0, 0); | 2890 | // setVisualOffset_Widget(w, visOff, 0, 0); |
@@ -2871,7 +2918,7 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | |||
2871 | addChildPos_Widget(swipeParent, iClob(target), back_WidgetAddPos); | 2918 | addChildPos_Widget(swipeParent, iClob(target), back_WidgetAddPos); |
2872 | setId_Widget(as_Widget(target), "swipeout"); | 2919 | setId_Widget(as_Widget(target), "swipeout"); |
2873 | setFlags_Widget(as_Widget(target), disabled_WidgetFlag, iTrue); | 2920 | setFlags_Widget(as_Widget(target), disabled_WidgetFlag, iTrue); |
2874 | swap_DocumentWidget_(target, d->doc, d); | 2921 | swap_DocumentWidget_(target, d->view.doc, d); |
2875 | setUrlAndSource_DocumentWidget(d, | 2922 | setUrlAndSource_DocumentWidget(d, |
2876 | swipeIn->mod.url, | 2923 | swipeIn->mod.url, |
2877 | collectNewCStr_String("text/gemini"), | 2924 | collectNewCStr_String("text/gemini"), |
@@ -2924,23 +2971,23 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2924 | return iFalse; | 2971 | return iFalse; |
2925 | } | 2972 | } |
2926 | /* When any tab changes its document URL, update the open link indicators. */ | 2973 | /* When any tab changes its document URL, update the open link indicators. */ |
2927 | if (updateOpenURLs_GmDocument(d->doc)) { | 2974 | if (updateOpenURLs_GmDocument(d->view.doc)) { |
2928 | invalidate_DocumentWidget_(d); | 2975 | invalidate_DocumentWidget_(d); |
2929 | refresh_Widget(d); | 2976 | refresh_Widget(d); |
2930 | } | 2977 | } |
2931 | return iFalse; | 2978 | return iFalse; |
2932 | } | 2979 | } |
2933 | if (equal_Command(cmd, "visited.changed")) { | 2980 | if (equal_Command(cmd, "visited.changed")) { |
2934 | updateVisitedLinks_GmDocument(d->doc); | 2981 | updateVisitedLinks_GmDocument(d->view.doc); |
2935 | invalidateVisibleLinks_DocumentWidget_(d); | 2982 | invalidateVisibleLinks_DocumentView_(&d->view); |
2936 | return iFalse; | 2983 | return iFalse; |
2937 | } | 2984 | } |
2938 | if (equal_Command(cmd, "document.render")) /* `Periodic` makes direct dispatch to here */ { | 2985 | if (equal_Command(cmd, "document.render")) /* `Periodic` makes direct dispatch to here */ { |
2939 | // printf("%u: document.render\n", SDL_GetTicks()); | 2986 | // printf("%u: document.render\n", SDL_GetTicks()); |
2940 | if (SDL_GetTicks() - d->drawBufs->lastRenderTime > 150) { | 2987 | if (SDL_GetTicks() - d->view.drawBufs->lastRenderTime > 150) { |
2941 | remove_Periodic(periodic_App(), d); | 2988 | remove_Periodic(periodic_App(), d); |
2942 | /* Scrolling has stopped, begin filling up the buffer. */ | 2989 | /* Scrolling has stopped, begin filling up the buffer. */ |
2943 | if (d->visBuf->buffers[0].texture) { | 2990 | if (d->view.visBuf->buffers[0].texture) { |
2944 | addTicker_App(prerender_DocumentWidget_, d); | 2991 | addTicker_App(prerender_DocumentWidget_, d); |
2945 | } | 2992 | } |
2946 | } | 2993 | } |
@@ -2955,11 +3002,11 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2955 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 3002 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
2956 | d->phoneToolbar = findWidget_App("toolbar"); | 3003 | d->phoneToolbar = findWidget_App("toolbar"); |
2957 | const iBool keepCenter = equal_Command(cmd, "font.changed"); | 3004 | const iBool keepCenter = equal_Command(cmd, "font.changed"); |
2958 | updateDocumentWidthRetainingScrollPosition_DocumentWidget_(d, keepCenter); | 3005 | updateDocumentWidthRetainingScrollPosition_DocumentView_(&d->view, keepCenter); |
2959 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; | 3006 | d->view.drawBufs->flags |= updateSideBuf_DrawBufsFlag; |
2960 | updateVisible_DocumentWidget_(d); | 3007 | updateVisible_DocumentView_(&d->view); |
2961 | invalidate_DocumentWidget_(d); | 3008 | invalidate_DocumentWidget_(d); |
2962 | dealloc_VisBuf(d->visBuf); | 3009 | dealloc_VisBuf(d->view.visBuf); |
2963 | updateWindowTitle_DocumentWidget_(d); | 3010 | updateWindowTitle_DocumentWidget_(d); |
2964 | showOrHidePinningIndicator_DocumentWidget_(d); | 3011 | showOrHidePinningIndicator_DocumentWidget_(d); |
2965 | refresh_Widget(w); | 3012 | refresh_Widget(w); |
@@ -2967,7 +3014,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2967 | else if (equal_Command(cmd, "window.focus.lost")) { | 3014 | else if (equal_Command(cmd, "window.focus.lost")) { |
2968 | if (d->flags & showLinkNumbers_DocumentWidgetFlag) { | 3015 | if (d->flags & showLinkNumbers_DocumentWidgetFlag) { |
2969 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 3016 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
2970 | invalidateVisibleLinks_DocumentWidget_(d); | 3017 | invalidateVisibleLinks_DocumentView_(&d->view); |
2971 | refresh_Widget(w); | 3018 | refresh_Widget(w); |
2972 | } | 3019 | } |
2973 | return iFalse; | 3020 | return iFalse; |
@@ -2979,16 +3026,16 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2979 | invalidateTheme_History(d->mod.history); /* forget cached color palettes */ | 3026 | invalidateTheme_History(d->mod.history); /* forget cached color palettes */ |
2980 | if (document_App() == d) { | 3027 | if (document_App() == d) { |
2981 | updateTheme_DocumentWidget_(d); | 3028 | updateTheme_DocumentWidget_(d); |
2982 | updateVisible_DocumentWidget_(d); | 3029 | updateVisible_DocumentView_(&d->view); |
2983 | updateTrust_DocumentWidget_(d, NULL); | 3030 | updateTrust_DocumentWidget_(d, NULL); |
2984 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; | 3031 | d->view.drawBufs->flags |= updateSideBuf_DrawBufsFlag; |
2985 | invalidate_DocumentWidget_(d); | 3032 | invalidate_DocumentWidget_(d); |
2986 | refresh_Widget(w); | 3033 | refresh_Widget(w); |
2987 | } | 3034 | } |
2988 | } | 3035 | } |
2989 | else if (equal_Command(cmd, "document.layout.changed") && document_Root(get_Root()) == d) { | 3036 | else if (equal_Command(cmd, "document.layout.changed") && document_Root(get_Root()) == d) { |
2990 | if (argLabel_Command(cmd, "redo")) { | 3037 | if (argLabel_Command(cmd, "redo")) { |
2991 | redoLayout_GmDocument(d->doc); | 3038 | redoLayout_GmDocument(d->view.doc); |
2992 | } | 3039 | } |
2993 | updateSize_DocumentWidget(d); | 3040 | updateSize_DocumentWidget(d); |
2994 | } | 3041 | } |
@@ -3011,11 +3058,11 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3011 | updateFetchProgress_DocumentWidget_(d); | 3058 | updateFetchProgress_DocumentWidget_(d); |
3012 | updateHover_Window(window_Widget(w)); | 3059 | updateHover_Window(window_Widget(w)); |
3013 | } | 3060 | } |
3014 | init_Anim(&d->sideOpacity, 0); | 3061 | init_Anim(&d->view.sideOpacity, 0); |
3015 | init_Anim(&d->altTextOpacity, 0); | 3062 | init_Anim(&d->view.altTextOpacity, 0); |
3016 | updateSideOpacity_DocumentWidget_(d, iFalse); | 3063 | updateSideOpacity_DocumentView_(&d->view, iFalse); |
3017 | updateWindowTitle_DocumentWidget_(d); | 3064 | updateWindowTitle_DocumentWidget_(d); |
3018 | allocVisBuffer_DocumentWidget_(d); | 3065 | allocVisBuffer_DocumentView_(&d->view); |
3019 | animateMedia_DocumentWidget_(d); | 3066 | animateMedia_DocumentWidget_(d); |
3020 | remove_Periodic(periodic_App(), d); | 3067 | remove_Periodic(periodic_App(), d); |
3021 | removeTicker_App(prerender_DocumentWidget_, d); | 3068 | removeTicker_App(prerender_DocumentWidget_, d); |
@@ -3039,8 +3086,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3039 | selectWords_DocumentWidgetFlag; /* finger-based selection is imprecise */ | 3086 | selectWords_DocumentWidgetFlag; /* finger-based selection is imprecise */ |
3040 | d->flags &= ~selectLines_DocumentWidgetFlag; | 3087 | d->flags &= ~selectLines_DocumentWidgetFlag; |
3041 | setFadeEnabled_ScrollWidget(d->scroll, iFalse); | 3088 | setFadeEnabled_ScrollWidget(d->scroll, iFalse); |
3042 | d->selectMark = sourceLoc_DocumentWidget_(d, d->contextPos); | 3089 | d->selectMark = sourceLoc_DocumentView_(&d->view, d->contextPos); |
3043 | extendRange_Rangecc(&d->selectMark, range_String(source_GmDocument(d->doc)), | 3090 | extendRange_Rangecc(&d->selectMark, range_String(source_GmDocument(d->view.doc)), |
3044 | word_RangeExtension | bothStartAndEnd_RangeExtension); | 3091 | word_RangeExtension | bothStartAndEnd_RangeExtension); |
3045 | d->initialSelectMark = d->selectMark; | 3092 | d->initialSelectMark = d->selectMark; |
3046 | } | 3093 | } |
@@ -3183,7 +3230,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3183 | } | 3230 | } |
3184 | else { | 3231 | else { |
3185 | /* Full document. */ | 3232 | /* Full document. */ |
3186 | copied = copy_String(source_GmDocument(d->doc)); | 3233 | copied = copy_String(source_GmDocument(d->view.doc)); |
3187 | } | 3234 | } |
3188 | SDL_SetClipboardText(cstr_String(copied)); | 3235 | SDL_SetClipboardText(cstr_String(copied)); |
3189 | delete_String(copied); | 3236 | delete_String(copied); |
@@ -3195,7 +3242,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3195 | else if (equal_Command(cmd, "document.copylink") && document_App() == d) { | 3242 | else if (equal_Command(cmd, "document.copylink") && document_App() == d) { |
3196 | if (d->contextLink) { | 3243 | if (d->contextLink) { |
3197 | SDL_SetClipboardText(cstr_String(canonicalUrl_String(absoluteUrl_String( | 3244 | SDL_SetClipboardText(cstr_String(canonicalUrl_String(absoluteUrl_String( |
3198 | d->mod.url, linkUrl_GmDocument(d->doc, d->contextLink->linkId))))); | 3245 | d->mod.url, linkUrl_GmDocument(d->view.doc, d->contextLink->linkId))))); |
3199 | } | 3246 | } |
3200 | else { | 3247 | else { |
3201 | SDL_SetClipboardText(cstr_String(canonicalUrl_String(d->mod.url))); | 3248 | SDL_SetClipboardText(cstr_String(canonicalUrl_String(d->mod.url))); |
@@ -3205,13 +3252,13 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3205 | else if (equalWidget_Command(cmd, w, "document.downloadlink")) { | 3252 | else if (equalWidget_Command(cmd, w, "document.downloadlink")) { |
3206 | if (d->contextLink) { | 3253 | if (d->contextLink) { |
3207 | const iGmLinkId linkId = d->contextLink->linkId; | 3254 | const iGmLinkId linkId = d->contextLink->linkId; |
3208 | setUrl_Media(media_GmDocument(d->doc), | 3255 | setUrl_Media(media_GmDocument(d->view.doc), |
3209 | linkId, | 3256 | linkId, |
3210 | download_MediaType, | 3257 | download_MediaType, |
3211 | linkUrl_GmDocument(d->doc, linkId)); | 3258 | linkUrl_GmDocument(d->view.doc, linkId)); |
3212 | requestMedia_DocumentWidget_(d, linkId, iFalse /* no filters */); | 3259 | requestMedia_DocumentWidget_(d, linkId, iFalse /* no filters */); |
3213 | redoLayout_GmDocument(d->doc); /* inline downloader becomes visible */ | 3260 | redoLayout_GmDocument(d->view.doc); /* inline downloader becomes visible */ |
3214 | updateVisible_DocumentWidget_(d); | 3261 | updateVisible_DocumentView_(&d->view); |
3215 | invalidate_DocumentWidget_(d); | 3262 | invalidate_DocumentWidget_(d); |
3216 | refresh_Widget(w); | 3263 | refresh_Widget(w); |
3217 | } | 3264 | } |
@@ -3256,7 +3303,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3256 | updateFetchProgress_DocumentWidget_(d); | 3303 | updateFetchProgress_DocumentWidget_(d); |
3257 | checkResponse_DocumentWidget_(d); | 3304 | checkResponse_DocumentWidget_(d); |
3258 | if (category_GmStatusCode(status_GmRequest(d->request)) == categorySuccess_GmStatusCode) { | 3305 | if (category_GmStatusCode(status_GmRequest(d->request)) == categorySuccess_GmStatusCode) { |
3259 | init_Anim(&d->scrollY.pos, d->initNormScrollY * pageHeight_DocumentWidget_(d)); /* TODO: unless user already scrolled! */ | 3306 | init_Anim(&d->view.scrollY.pos, d->initNormScrollY * pageHeight_DocumentView_(&d->view)); |
3307 | /* TODO: unless user already scrolled! */ | ||
3260 | } | 3308 | } |
3261 | addBannerWarnings_DocumentWidget_(d); | 3309 | addBannerWarnings_DocumentWidget_(d); |
3262 | iChangeFlags(d->flags, | 3310 | iChangeFlags(d->flags, |
@@ -3276,8 +3324,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3276 | } | 3324 | } |
3277 | } | 3325 | } |
3278 | iReleasePtr(&d->request); | 3326 | iReleasePtr(&d->request); |
3279 | updateVisible_DocumentWidget_(d); | 3327 | updateVisible_DocumentView_(&d->view); |
3280 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; | 3328 | d->view.drawBufs->flags |= updateSideBuf_DrawBufsFlag; |
3281 | postCommandf_Root(w->root, | 3329 | postCommandf_Root(w->root, |
3282 | "document.changed doc:%p status:%d url:%s", | 3330 | "document.changed doc:%p status:%d url:%s", |
3283 | d, | 3331 | d, |
@@ -3285,7 +3333,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3285 | cstr_String(d->mod.url)); | 3333 | cstr_String(d->mod.url)); |
3286 | /* Check for a pending goto. */ | 3334 | /* Check for a pending goto. */ |
3287 | if (!isEmpty_String(&d->pendingGotoHeading)) { | 3335 | if (!isEmpty_String(&d->pendingGotoHeading)) { |
3288 | scrollToHeading_DocumentWidget_(d, cstr_String(&d->pendingGotoHeading)); | 3336 | scrollToHeading_DocumentView_(&d->view, cstr_String(&d->pendingGotoHeading)); |
3289 | clear_String(&d->pendingGotoHeading); | 3337 | clear_String(&d->pendingGotoHeading); |
3290 | } | 3338 | } |
3291 | cacheDocumentGlyphs_DocumentWidget_(d); | 3339 | cacheDocumentGlyphs_DocumentWidget_(d); |
@@ -3331,7 +3379,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3331 | else if (equal_Command(cmd, "media.player.started")) { | 3379 | else if (equal_Command(cmd, "media.player.started")) { |
3332 | /* When one media player starts, pause the others that may be playing. */ | 3380 | /* When one media player starts, pause the others that may be playing. */ |
3333 | const iPlayer *startedPlr = pointerLabel_Command(cmd, "player"); | 3381 | const iPlayer *startedPlr = pointerLabel_Command(cmd, "player"); |
3334 | const iMedia * media = media_GmDocument(d->doc); | 3382 | const iMedia * media = media_GmDocument(d->view.doc); |
3335 | const size_t num = numAudio_Media(media); | 3383 | const size_t num = numAudio_Media(media); |
3336 | for (size_t id = 1; id <= num; id++) { | 3384 | for (size_t id = 1; id <= num; id++) { |
3337 | iPlayer *plr = audioPlayer_Media(media, (iMediaId){ audio_MediaType, id }); | 3385 | iPlayer *plr = audioPlayer_Media(media, (iMediaId){ audio_MediaType, id }); |
@@ -3374,7 +3422,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3374 | return iTrue; | 3422 | return iTrue; |
3375 | } | 3423 | } |
3376 | else if (equal_Command(cmd, "document.reload") && document_Command(cmd) == d) { | 3424 | else if (equal_Command(cmd, "document.reload") && document_Command(cmd) == d) { |
3377 | d->initNormScrollY = normScrollPos_DocumentWidget_(d); | 3425 | d->initNormScrollY = normScrollPos_DocumentView_(&d->view); |
3378 | if (equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")) { | 3426 | if (equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")) { |
3379 | /* Reopen so the Upload dialog gets shown. */ | 3427 | /* Reopen so the Upload dialog gets shown. */ |
3380 | postCommandf_App("open url:%s", cstr_String(d->mod.url)); | 3428 | postCommandf_App("open url:%s", cstr_String(d->mod.url)); |
@@ -3391,13 +3439,13 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3391 | if (d->flags & showLinkNumbers_DocumentWidgetFlag && | 3439 | if (d->flags & showLinkNumbers_DocumentWidgetFlag && |
3392 | d->ordinalMode == homeRow_DocumentLinkOrdinalMode) { | 3440 | d->ordinalMode == homeRow_DocumentLinkOrdinalMode) { |
3393 | const size_t numKeys = iElemCount(homeRowKeys_); | 3441 | const size_t numKeys = iElemCount(homeRowKeys_); |
3394 | const iGmRun *last = lastVisibleLink_DocumentWidget_(d); | 3442 | const iGmRun *last = lastVisibleLink_DocumentView_(&d->view); |
3395 | if (!last) { | 3443 | if (!last) { |
3396 | d->ordinalBase = 0; | 3444 | d->ordinalBase = 0; |
3397 | } | 3445 | } |
3398 | else { | 3446 | else { |
3399 | d->ordinalBase += numKeys; | 3447 | d->ordinalBase += numKeys; |
3400 | if (visibleLinkOrdinal_DocumentWidget_(d, last->linkId) < d->ordinalBase) { | 3448 | if (visibleLinkOrdinal_DocumentView_(&d->view, last->linkId) < d->ordinalBase) { |
3401 | d->ordinalBase = 0; | 3449 | d->ordinalBase = 0; |
3402 | } | 3450 | } |
3403 | } | 3451 | } |
@@ -3417,25 +3465,11 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3417 | iChangeFlags(d->flags, newTabViaHomeKeys_DocumentWidgetFlag, | 3465 | iChangeFlags(d->flags, newTabViaHomeKeys_DocumentWidgetFlag, |
3418 | argLabel_Command(cmd, "newtab") != 0); | 3466 | argLabel_Command(cmd, "newtab") != 0); |
3419 | } | 3467 | } |
3420 | invalidateVisibleLinks_DocumentWidget_(d); | 3468 | invalidateVisibleLinks_DocumentView_(&d->view); |
3421 | refresh_Widget(d); | 3469 | refresh_Widget(d); |
3422 | return iTrue; | 3470 | return iTrue; |
3423 | } | 3471 | } |
3424 | else if (equal_Command(cmd, "navigate.back") && document_App() == d) { | 3472 | else if (equal_Command(cmd, "navigate.back") && document_App() == d) { |
3425 | #if 0 | ||
3426 | if (isPortraitPhone_App()) { | ||
3427 | if (d->flags & openedFromSidebar_DocumentWidgetFlag && | ||
3428 | !isVisible_Widget(findWidget_App("sidebar"))) { | ||
3429 | postCommand_App("sidebar.toggle"); | ||
3430 | showToolbar_Root(get_Root(), iTrue); | ||
3431 | #if defined (iPlatformAppleMobile) | ||
3432 | playHapticEffect_iOS(gentleTap_HapticEffect); | ||
3433 | #endif | ||
3434 | return iTrue; | ||
3435 | } | ||
3436 | d->flags &= ~openedFromSidebar_DocumentWidgetFlag; | ||
3437 | } | ||
3438 | #endif | ||
3439 | if (d->request) { | 3473 | if (d->request) { |
3440 | postCommandf_Root(w->root, | 3474 | postCommandf_Root(w->root, |
3441 | "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); | 3475 | "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); |
@@ -3472,8 +3506,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3472 | return iTrue; | 3506 | return iTrue; |
3473 | } | 3507 | } |
3474 | else if (equalWidget_Command(cmd, w, "scroll.moved")) { | 3508 | else if (equalWidget_Command(cmd, w, "scroll.moved")) { |
3475 | init_Anim(&d->scrollY.pos, arg_Command(cmd)); | 3509 | init_Anim(&d->view.scrollY.pos, arg_Command(cmd)); |
3476 | updateVisible_DocumentWidget_(d); | 3510 | updateVisible_DocumentView_(&d->view); |
3477 | return iTrue; | 3511 | return iTrue; |
3478 | } | 3512 | } |
3479 | else if (equal_Command(cmd, "scroll.page") && document_App() == d) { | 3513 | else if (equal_Command(cmd, "scroll.page") && document_App() == d) { |
@@ -3484,25 +3518,26 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3484 | return iTrue; | 3518 | return iTrue; |
3485 | } | 3519 | } |
3486 | const float amount = argLabel_Command(cmd, "full") != 0 ? 1.0f : 0.5f; | 3520 | const float amount = argLabel_Command(cmd, "full") != 0 ? 1.0f : 0.5f; |
3487 | smoothScroll_DocumentWidget_(d, | 3521 | smoothScroll_DocumentView_(&d->view, |
3488 | dir * amount * height_Rect(documentBounds_DocumentWidget_(d)), | 3522 | dir * amount * |
3489 | smoothDuration_DocumentWidget_(keyboard_ScrollType)); | 3523 | height_Rect(documentBounds_DocumentView_(&d->view)), |
3524 | smoothDuration_DocumentWidget_(keyboard_ScrollType)); | ||
3490 | return iTrue; | 3525 | return iTrue; |
3491 | } | 3526 | } |
3492 | else if (equal_Command(cmd, "scroll.top") && document_App() == d) { | 3527 | else if (equal_Command(cmd, "scroll.top") && document_App() == d) { |
3493 | init_Anim(&d->scrollY.pos, 0); | 3528 | init_Anim(&d->view.scrollY.pos, 0); |
3494 | invalidate_VisBuf(d->visBuf); | 3529 | invalidate_VisBuf(d->view.visBuf); |
3495 | clampScroll_DocumentWidget_(d); | 3530 | clampScroll_DocumentView_(&d->view); |
3496 | updateVisible_DocumentWidget_(d); | 3531 | updateVisible_DocumentView_(&d->view); |
3497 | refresh_Widget(w); | 3532 | refresh_Widget(w); |
3498 | return iTrue; | 3533 | return iTrue; |
3499 | } | 3534 | } |
3500 | else if (equal_Command(cmd, "scroll.bottom") && document_App() == d) { | 3535 | else if (equal_Command(cmd, "scroll.bottom") && document_App() == d) { |
3501 | updateScrollMax_DocumentWidget_(d); /* scrollY.max might not be fully updated */ | 3536 | updateScrollMax_DocumentView_(&d->view); /* scrollY.max might not be fully updated */ |
3502 | init_Anim(&d->scrollY.pos, d->scrollY.max); | 3537 | init_Anim(&d->view.scrollY.pos, d->view.scrollY.max); |
3503 | invalidate_VisBuf(d->visBuf); | 3538 | invalidate_VisBuf(d->view.visBuf); |
3504 | clampScroll_DocumentWidget_(d); | 3539 | clampScroll_DocumentView_(&d->view); |
3505 | updateVisible_DocumentWidget_(d); | 3540 | updateVisible_DocumentView_(&d->view); |
3506 | refresh_Widget(w); | 3541 | refresh_Widget(w); |
3507 | return iTrue; | 3542 | return iTrue; |
3508 | } | 3543 | } |
@@ -3513,9 +3548,9 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3513 | fetchNextUnfetchedImage_DocumentWidget_(d)) { | 3548 | fetchNextUnfetchedImage_DocumentWidget_(d)) { |
3514 | return iTrue; | 3549 | return iTrue; |
3515 | } | 3550 | } |
3516 | smoothScroll_DocumentWidget_(d, | 3551 | smoothScroll_DocumentView_(&d->view, |
3517 | 3 * lineHeight_Text(paragraph_FontId) * dir, | 3552 | 3 * lineHeight_Text(paragraph_FontId) * dir, |
3518 | smoothDuration_DocumentWidget_(keyboard_ScrollType)); | 3553 | smoothDuration_DocumentWidget_(keyboard_ScrollType)); |
3519 | return iTrue; | 3554 | return iTrue; |
3520 | } | 3555 | } |
3521 | else if (equal_Command(cmd, "document.goto") && document_App() == d) { | 3556 | else if (equal_Command(cmd, "document.goto") && document_App() == d) { |
@@ -3526,13 +3561,13 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3526 | setCStr_String(&d->pendingGotoHeading, heading); | 3561 | setCStr_String(&d->pendingGotoHeading, heading); |
3527 | return iTrue; | 3562 | return iTrue; |
3528 | } | 3563 | } |
3529 | scrollToHeading_DocumentWidget_(d, heading); | 3564 | scrollToHeading_DocumentView_(&d->view, heading); |
3530 | return iTrue; | 3565 | return iTrue; |
3531 | } | 3566 | } |
3532 | const char *loc = pointerLabel_Command(cmd, "loc"); | 3567 | const char *loc = pointerLabel_Command(cmd, "loc"); |
3533 | const iGmRun *run = findRunAtLoc_GmDocument(d->doc, loc); | 3568 | const iGmRun *run = findRunAtLoc_GmDocument(d->view.doc, loc); |
3534 | if (run) { | 3569 | if (run) { |
3535 | scrollTo_DocumentWidget_(d, run->visBounds.pos.y, iFalse); | 3570 | scrollTo_DocumentView_(&d->view, run->visBounds.pos.y, iFalse); |
3536 | } | 3571 | } |
3537 | return iTrue; | 3572 | return iTrue; |
3538 | } | 3573 | } |
@@ -3547,24 +3582,24 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3547 | } | 3582 | } |
3548 | else { | 3583 | else { |
3549 | const iBool wrap = d->foundMark.start != NULL; | 3584 | const iBool wrap = d->foundMark.start != NULL; |
3550 | d->foundMark = finder(d->doc, text_InputWidget(find), dir > 0 ? d->foundMark.end | 3585 | d->foundMark = finder(d->view.doc, text_InputWidget(find), dir > 0 ? d->foundMark.end |
3551 | : d->foundMark.start); | 3586 | : d->foundMark.start); |
3552 | if (!d->foundMark.start && wrap) { | 3587 | if (!d->foundMark.start && wrap) { |
3553 | /* Wrap around. */ | 3588 | /* Wrap around. */ |
3554 | d->foundMark = finder(d->doc, text_InputWidget(find), NULL); | 3589 | d->foundMark = finder(d->view.doc, text_InputWidget(find), NULL); |
3555 | } | 3590 | } |
3556 | if (d->foundMark.start) { | 3591 | if (d->foundMark.start) { |
3557 | const iGmRun *found; | 3592 | const iGmRun *found; |
3558 | if ((found = findRunAtLoc_GmDocument(d->doc, d->foundMark.start)) != NULL) { | 3593 | if ((found = findRunAtLoc_GmDocument(d->view.doc, d->foundMark.start)) != NULL) { |
3559 | scrollTo_DocumentWidget_(d, mid_Rect(found->bounds).y, iTrue); | 3594 | scrollTo_DocumentView_(&d->view, mid_Rect(found->bounds).y, iTrue); |
3560 | } | 3595 | } |
3561 | } | 3596 | } |
3562 | } | 3597 | } |
3563 | if (flags_Widget(w) & touchDrag_WidgetFlag) { | 3598 | if (flags_Widget(w) & touchDrag_WidgetFlag) { |
3564 | postCommand_Root(w->root, "document.select arg:0"); /* we can't handle both at the same time */ | 3599 | postCommand_Root(w->root, "document.select arg:0"); /* we can't handle both at the same time */ |
3565 | } | 3600 | } |
3566 | invalidateWideRunsWithNonzeroOffset_DocumentWidget_(d); /* markers don't support offsets */ | 3601 | invalidateWideRunsWithNonzeroOffset_DocumentView_(&d->view); /* markers don't support offsets */ |
3567 | resetWideRuns_DocumentWidget_(d); | 3602 | resetWideRuns_DocumentView_(&d->view); |
3568 | refresh_Widget(w); | 3603 | refresh_Widget(w); |
3569 | return iTrue; | 3604 | return iTrue; |
3570 | } | 3605 | } |
@@ -3577,13 +3612,13 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3577 | } | 3612 | } |
3578 | else if (equal_Command(cmd, "bookmark.links") && document_App() == d) { | 3613 | else if (equal_Command(cmd, "bookmark.links") && document_App() == d) { |
3579 | iPtrArray *links = collectNew_PtrArray(); | 3614 | iPtrArray *links = collectNew_PtrArray(); |
3580 | render_GmDocument(d->doc, (iRangei){ 0, size_GmDocument(d->doc).y }, addAllLinks_, links); | 3615 | render_GmDocument(d->view.doc, (iRangei){ 0, size_GmDocument(d->view.doc).y }, addAllLinks_, links); |
3581 | /* Find links that aren't already bookmarked. */ | 3616 | /* Find links that aren't already bookmarked. */ |
3582 | iForEach(PtrArray, i, links) { | 3617 | iForEach(PtrArray, i, links) { |
3583 | const iGmRun *run = i.ptr; | 3618 | const iGmRun *run = i.ptr; |
3584 | uint32_t bmid; | 3619 | uint32_t bmid; |
3585 | if ((bmid = findUrl_Bookmarks(bookmarks_App(), | 3620 | if ((bmid = findUrl_Bookmarks(bookmarks_App(), |
3586 | linkUrl_GmDocument(d->doc, run->linkId))) != 0) { | 3621 | linkUrl_GmDocument(d->view.doc, run->linkId))) != 0) { |
3587 | const iBookmark *bm = get_Bookmarks(bookmarks_App(), bmid); | 3622 | const iBookmark *bm = get_Bookmarks(bookmarks_App(), bmid); |
3588 | /* We can import local copies of remote bookmarks. */ | 3623 | /* We can import local copies of remote bookmarks. */ |
3589 | if (~bm->flags & remote_BookmarkFlag) { | 3624 | if (~bm->flags & remote_BookmarkFlag) { |
@@ -3610,7 +3645,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3610 | iConstForEach(PtrArray, j, links) { | 3645 | iConstForEach(PtrArray, j, links) { |
3611 | const iGmRun *run = j.ptr; | 3646 | const iGmRun *run = j.ptr; |
3612 | add_Bookmarks(bookmarks_App(), | 3647 | add_Bookmarks(bookmarks_App(), |
3613 | linkUrl_GmDocument(d->doc, run->linkId), | 3648 | linkUrl_GmDocument(d->view.doc, run->linkId), |
3614 | collect_String(newRange_String(run->text)), | 3649 | collect_String(newRange_String(run->text)), |
3615 | NULL, | 3650 | NULL, |
3616 | 0x1f588 /* pin */); | 3651 | 0x1f588 /* pin */); |
@@ -3625,7 +3660,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3625 | return iTrue; | 3660 | return iTrue; |
3626 | } | 3661 | } |
3627 | else if (equalWidget_Command(cmd, w, "menu.closed")) { | 3662 | else if (equalWidget_Command(cmd, w, "menu.closed")) { |
3628 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window(), 0)); | 3663 | updateHover_DocumentView_(&d->view, mouseCoord_Window(get_Window(), 0)); |
3629 | } | 3664 | } |
3630 | else if (equal_Command(cmd, "document.autoreload")) { | 3665 | else if (equal_Command(cmd, "document.autoreload")) { |
3631 | if (d->mod.reloadInterval) { | 3666 | if (d->mod.reloadInterval) { |
@@ -3695,14 +3730,14 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3695 | return iFalse; | 3730 | return iFalse; |
3696 | } | 3731 | } |
3697 | 3732 | ||
3698 | static iRect runRect_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run) { | 3733 | static iRect runRect_DocumentView_(const iDocumentView *d, const iGmRun *run) { |
3699 | const iRect docBounds = documentBounds_DocumentWidget_(d); | 3734 | const iRect docBounds = documentBounds_DocumentView_(d); |
3700 | return moved_Rect(run->bounds, addY_I2(topLeft_Rect(docBounds), viewPos_DocumentWidget_(d))); | 3735 | return moved_Rect(run->bounds, addY_I2(topLeft_Rect(docBounds), viewPos_DocumentView_(d))); |
3701 | } | 3736 | } |
3702 | 3737 | ||
3703 | static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *run) { | 3738 | static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *run) { |
3704 | if (run && run->mediaType == audio_MediaType) { | 3739 | if (run && run->mediaType == audio_MediaType) { |
3705 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); | 3740 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->view.doc), mediaId_GmRun(run)); |
3706 | setFlags_Player(plr, volumeGrabbed_PlayerFlag, iTrue); | 3741 | setFlags_Player(plr, volumeGrabbed_PlayerFlag, iTrue); |
3707 | d->grabbedStartVolume = volume_Player(plr); | 3742 | d->grabbedStartVolume = volume_Player(plr); |
3708 | d->grabbedPlayer = run; | 3743 | d->grabbedPlayer = run; |
@@ -3710,7 +3745,7 @@ static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *r | |||
3710 | } | 3745 | } |
3711 | else if (d->grabbedPlayer) { | 3746 | else if (d->grabbedPlayer) { |
3712 | setFlags_Player( | 3747 | setFlags_Player( |
3713 | audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(d->grabbedPlayer)), | 3748 | audioPlayer_Media(media_GmDocument(d->view.doc), mediaId_GmRun(d->grabbedPlayer)), |
3714 | volumeGrabbed_PlayerFlag, | 3749 | volumeGrabbed_PlayerFlag, |
3715 | iFalse); | 3750 | iFalse); |
3716 | d->grabbedPlayer = NULL; | 3751 | d->grabbedPlayer = NULL; |
@@ -3736,14 +3771,14 @@ static iBool processMediaEvents_DocumentWidget_(iDocumentWidget *d, const SDL_Ev | |||
3736 | return iFalse; | 3771 | return iFalse; |
3737 | } | 3772 | } |
3738 | const iInt2 mouse = init_I2(ev->button.x, ev->button.y); | 3773 | const iInt2 mouse = init_I2(ev->button.x, ev->button.y); |
3739 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 3774 | iConstForEach(PtrArray, i, &d->view.visibleMedia) { |
3740 | const iGmRun *run = i.ptr; | 3775 | const iGmRun *run = i.ptr; |
3741 | if (run->mediaType != audio_MediaType) { | 3776 | if (run->mediaType != audio_MediaType) { |
3742 | continue; | 3777 | continue; |
3743 | } | 3778 | } |
3744 | /* TODO: move this to mediaui.c */ | 3779 | /* TODO: move this to mediaui.c */ |
3745 | const iRect rect = runRect_DocumentWidget_(d, run); | 3780 | const iRect rect = runRect_DocumentView_(&d->view, run); |
3746 | iPlayer * plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); | 3781 | iPlayer * plr = audioPlayer_Media(media_GmDocument(d->view.doc), mediaId_GmRun(run)); |
3747 | if (contains_Rect(rect, mouse)) { | 3782 | if (contains_Rect(rect, mouse)) { |
3748 | iPlayerUI ui; | 3783 | iPlayerUI ui; |
3749 | init_PlayerUI(&ui, plr, rect); | 3784 | init_PlayerUI(&ui, plr, rect); |
@@ -3869,20 +3904,20 @@ static iChar linkOrdinalChar_DocumentWidget_(const iDocumentWidget *d, size_t or | |||
3869 | 3904 | ||
3870 | static void beginMarkingSelection_DocumentWidget_(iDocumentWidget *d, iInt2 pos) { | 3905 | static void beginMarkingSelection_DocumentWidget_(iDocumentWidget *d, iInt2 pos) { |
3871 | setFocus_Widget(NULL); /* TODO: Focus this document? */ | 3906 | setFocus_Widget(NULL); /* TODO: Focus this document? */ |
3872 | invalidateWideRunsWithNonzeroOffset_DocumentWidget_(d); | 3907 | invalidateWideRunsWithNonzeroOffset_DocumentView_(&d->view); |
3873 | resetWideRuns_DocumentWidget_(d); /* Selections don't support horizontal scrolling. */ | 3908 | resetWideRuns_DocumentView_(&d->view); /* Selections don't support horizontal scrolling. */ |
3874 | iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iTrue); | 3909 | iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iTrue); |
3875 | d->initialSelectMark = d->selectMark = sourceLoc_DocumentWidget_(d, pos); | 3910 | d->initialSelectMark = d->selectMark = sourceLoc_DocumentView_(&d->view, pos); |
3876 | refresh_Widget(as_Widget(d)); | 3911 | refresh_Widget(as_Widget(d)); |
3877 | } | 3912 | } |
3878 | 3913 | ||
3879 | static void interactingWithLink_DocumentWidget_(iDocumentWidget *d, iGmLinkId id) { | 3914 | static void interactingWithLink_DocumentWidget_(iDocumentWidget *d, iGmLinkId id) { |
3880 | iRangecc loc = linkUrlRange_GmDocument(d->doc, id); | 3915 | iRangecc loc = linkUrlRange_GmDocument(d->view.doc, id); |
3881 | if (!loc.start) { | 3916 | if (!loc.start) { |
3882 | clear_String(&d->linePrecedingLink); | 3917 | clear_String(&d->linePrecedingLink); |
3883 | return; | 3918 | return; |
3884 | } | 3919 | } |
3885 | const char *start = range_String(source_GmDocument(d->doc)).start; | 3920 | const char *start = range_String(source_GmDocument(d->view.doc)).start; |
3886 | /* Find the preceding line. This is offered as a prefill option for a possible input query. */ | 3921 | /* Find the preceding line. This is offered as a prefill option for a possible input query. */ |
3887 | while (loc.start > start && *loc.start != '\n') { | 3922 | while (loc.start > start && *loc.start != '\n') { |
3888 | loc.start--; | 3923 | loc.start--; |
@@ -3987,11 +4022,12 @@ static iBool handleWheelSwipe_DocumentWidget_(iDocumentWidget *d, const SDL_Mous | |||
3987 | } | 4022 | } |
3988 | 4023 | ||
3989 | static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { | 4024 | static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { |
3990 | iWidget *w = as_Widget(d); | 4025 | iWidget *w = as_Widget(d); |
4026 | iDocumentView *view = &d->view; | ||
3991 | if (isMetricsChange_UserEvent(ev)) { | 4027 | if (isMetricsChange_UserEvent(ev)) { |
3992 | updateSize_DocumentWidget(d); | 4028 | updateSize_DocumentWidget(d); |
3993 | } | 4029 | } |
3994 | else if (processEvent_SmoothScroll(&d->scrollY, ev)) { | 4030 | else if (processEvent_SmoothScroll(&d->view.scrollY, ev)) { |
3995 | return iTrue; | 4031 | return iTrue; |
3996 | } | 4032 | } |
3997 | else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { | 4033 | else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { |
@@ -4010,28 +4046,28 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4010 | if ((d->flags & showLinkNumbers_DocumentWidgetFlag) && | 4046 | if ((d->flags & showLinkNumbers_DocumentWidgetFlag) && |
4011 | ((key >= '1' && key <= '9') || (key >= 'a' && key <= 'z'))) { | 4047 | ((key >= '1' && key <= '9') || (key >= 'a' && key <= 'z'))) { |
4012 | const size_t ord = linkOrdinalFromKey_DocumentWidget_(d, key) + d->ordinalBase; | 4048 | const size_t ord = linkOrdinalFromKey_DocumentWidget_(d, key) + d->ordinalBase; |
4013 | iConstForEach(PtrArray, i, &d->visibleLinks) { | 4049 | iConstForEach(PtrArray, i, &d->view.visibleLinks) { |
4014 | if (ord == iInvalidPos) break; | 4050 | if (ord == iInvalidPos) break; |
4015 | const iGmRun *run = i.ptr; | 4051 | const iGmRun *run = i.ptr; |
4016 | if (run->flags & decoration_GmRunFlag && | 4052 | if (run->flags & decoration_GmRunFlag && |
4017 | visibleLinkOrdinal_DocumentWidget_(d, run->linkId) == ord) { | 4053 | visibleLinkOrdinal_DocumentView_(view, run->linkId) == ord) { |
4018 | if (d->flags & setHoverViaKeys_DocumentWidgetFlag) { | 4054 | if (d->flags & setHoverViaKeys_DocumentWidgetFlag) { |
4019 | d->hoverLink = run; | 4055 | view->hoverLink = run; |
4020 | } | 4056 | } |
4021 | else { | 4057 | else { |
4022 | postCommandf_Root(w->root, | 4058 | postCommandf_Root( |
4023 | "open newtab:%d url:%s", | 4059 | w->root, |
4024 | (isPinned_DocumentWidget_(d) ? otherRoot_OpenTabFlag : 0) ^ | 4060 | "open newtab:%d url:%s", |
4025 | (d->ordinalMode == | 4061 | (isPinned_DocumentWidget_(d) ? otherRoot_OpenTabFlag : 0) ^ |
4026 | numbersAndAlphabet_DocumentLinkOrdinalMode | 4062 | (d->ordinalMode == numbersAndAlphabet_DocumentLinkOrdinalMode |
4027 | ? openTabMode_Sym(modState_Keys()) | 4063 | ? openTabMode_Sym(modState_Keys()) |
4028 | : (d->flags & newTabViaHomeKeys_DocumentWidgetFlag ? 1 : 0)), | 4064 | : (d->flags & newTabViaHomeKeys_DocumentWidgetFlag ? 1 : 0)), |
4029 | cstr_String(absoluteUrl_String( | 4065 | cstr_String(absoluteUrl_String( |
4030 | d->mod.url, linkUrl_GmDocument(d->doc, run->linkId)))); | 4066 | d->mod.url, linkUrl_GmDocument(view->doc, run->linkId)))); |
4031 | interactingWithLink_DocumentWidget_(d, run->linkId); | 4067 | interactingWithLink_DocumentWidget_(d, run->linkId); |
4032 | } | 4068 | } |
4033 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 4069 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
4034 | invalidateVisibleLinks_DocumentWidget_(d); | 4070 | invalidateVisibleLinks_DocumentView_(view); |
4035 | refresh_Widget(d); | 4071 | refresh_Widget(d); |
4036 | return iTrue; | 4072 | return iTrue; |
4037 | } | 4073 | } |
@@ -4041,7 +4077,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4041 | case SDLK_ESCAPE: | 4077 | case SDLK_ESCAPE: |
4042 | if (d->flags & showLinkNumbers_DocumentWidgetFlag && document_App() == d) { | 4078 | if (d->flags & showLinkNumbers_DocumentWidgetFlag && document_App() == d) { |
4043 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 4079 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
4044 | invalidateVisibleLinks_DocumentWidget_(d); | 4080 | invalidateVisibleLinks_DocumentView_(view); |
4045 | refresh_Widget(d); | 4081 | refresh_Widget(d); |
4046 | return iTrue; | 4082 | return iTrue; |
4047 | } | 4083 | } |
@@ -4053,7 +4089,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4053 | for (size_t i = 0; i < 64; ++i) { | 4089 | for (size_t i = 0; i < 64; ++i) { |
4054 | setByte_Block(seed, i, iRandom(0, 256)); | 4090 | setByte_Block(seed, i, iRandom(0, 256)); |
4055 | } | 4091 | } |
4056 | setThemeSeed_GmDocument(d->doc, seed); | 4092 | setThemeSeed_GmDocument(view->doc, seed); |
4057 | delete_Block(seed); | 4093 | delete_Block(seed); |
4058 | invalidate_DocumentWidget_(d); | 4094 | invalidate_DocumentWidget_(d); |
4059 | refresh_Widget(w); | 4095 | refresh_Widget(w); |
@@ -4095,9 +4131,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4095 | const iInt2 mouseCoord = coord_MouseWheelEvent(&ev->wheel); | 4131 | const iInt2 mouseCoord = coord_MouseWheelEvent(&ev->wheel); |
4096 | if (isPerPixel_MouseWheelEvent(&ev->wheel)) { | 4132 | if (isPerPixel_MouseWheelEvent(&ev->wheel)) { |
4097 | const iInt2 wheel = init_I2(ev->wheel.x, ev->wheel.y); | 4133 | const iInt2 wheel = init_I2(ev->wheel.x, ev->wheel.y); |
4098 | stop_Anim(&d->scrollY.pos); | 4134 | stop_Anim(&d->view.scrollY.pos); |
4099 | immediateScroll_DocumentWidget_(d, -wheel.y); | 4135 | immediateScroll_DocumentView_(view, -wheel.y); |
4100 | if (!scrollWideBlock_DocumentWidget_(d, mouseCoord, -wheel.x, 0) && | 4136 | if (!scrollWideBlock_DocumentView_(view, mouseCoord, -wheel.x, 0) && |
4101 | wheel.x) { | 4137 | wheel.x) { |
4102 | handleWheelSwipe_DocumentWidget_(d, &ev->wheel); | 4138 | handleWheelSwipe_DocumentWidget_(d, &ev->wheel); |
4103 | } | 4139 | } |
@@ -4109,16 +4145,11 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4109 | postCommandf_App("zoom.delta arg:%d", amount > 0 ? 10 : -10); | 4145 | postCommandf_App("zoom.delta arg:%d", amount > 0 ? 10 : -10); |
4110 | return iTrue; | 4146 | return iTrue; |
4111 | } | 4147 | } |
4112 | smoothScroll_DocumentWidget_( | 4148 | smoothScroll_DocumentView_(view, |
4113 | d, | 4149 | -3 * amount * lineHeight_Text(paragraph_FontId), |
4114 | -3 * amount * lineHeight_Text(paragraph_FontId), | 4150 | smoothDuration_DocumentWidget_(mouse_ScrollType)); |
4115 | smoothDuration_DocumentWidget_(mouse_ScrollType)); | 4151 | scrollWideBlock_DocumentView_( |
4116 | /* accelerated speed for repeated wheelings */ | 4152 | view, mouseCoord, -3 * ev->wheel.x * lineHeight_Text(paragraph_FontId), 167); |
4117 | // * (!isFinished_SmoothScroll(&d->scrollY) && pos_Anim(&d->scrollY.pos) < 0.25f | ||
4118 | // ? 0.5f | ||
4119 | // : 1.0f)); | ||
4120 | scrollWideBlock_DocumentWidget_( | ||
4121 | d, mouseCoord, -3 * ev->wheel.x * lineHeight_Text(paragraph_FontId), 167); | ||
4122 | } | 4153 | } |
4123 | iChangeFlags(d->flags, noHoverWhileScrolling_DocumentWidgetFlag, iTrue); | 4154 | iChangeFlags(d->flags, noHoverWhileScrolling_DocumentWidgetFlag, iTrue); |
4124 | return iTrue; | 4155 | return iTrue; |
@@ -4137,10 +4168,10 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4137 | } | 4168 | } |
4138 | #endif | 4169 | #endif |
4139 | else { | 4170 | else { |
4140 | if (value_Anim(&d->altTextOpacity) < 0.833f) { | 4171 | if (value_Anim(&view->altTextOpacity) < 0.833f) { |
4141 | setValue_Anim(&d->altTextOpacity, 0, 0); /* keep it hidden while moving */ | 4172 | setValue_Anim(&view->altTextOpacity, 0, 0); /* keep it hidden while moving */ |
4142 | } | 4173 | } |
4143 | updateHover_DocumentWidget_(d, mpos); | 4174 | updateHover_DocumentView_(view, mpos); |
4144 | } | 4175 | } |
4145 | } | 4176 | } |
4146 | if (ev->type == SDL_USEREVENT && ev->user.code == widgetTapBegins_UserEventCode) { | 4177 | if (ev->type == SDL_USEREVENT && ev->user.code == widgetTapBegins_UserEventCode) { |
@@ -4156,18 +4187,18 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4156 | postCommand_Root(w->root, "navigate.forward"); | 4187 | postCommand_Root(w->root, "navigate.forward"); |
4157 | return iTrue; | 4188 | return iTrue; |
4158 | } | 4189 | } |
4159 | if (ev->button.button == SDL_BUTTON_MIDDLE && d->hoverLink) { | 4190 | if (ev->button.button == SDL_BUTTON_MIDDLE && view->hoverLink) { |
4160 | interactingWithLink_DocumentWidget_(d, d->hoverLink->linkId); | 4191 | interactingWithLink_DocumentWidget_(d, view->hoverLink->linkId); |
4161 | postCommandf_Root(w->root, "open newtab:%d url:%s", | 4192 | postCommandf_Root(w->root, "open newtab:%d url:%s", |
4162 | (isPinned_DocumentWidget_(d) ? otherRoot_OpenTabFlag : 0) | | 4193 | (isPinned_DocumentWidget_(d) ? otherRoot_OpenTabFlag : 0) | |
4163 | (modState_Keys() & KMOD_SHIFT ? new_OpenTabFlag : newBackground_OpenTabFlag), | 4194 | (modState_Keys() & KMOD_SHIFT ? new_OpenTabFlag : newBackground_OpenTabFlag), |
4164 | cstr_String(linkUrl_GmDocument(d->doc, d->hoverLink->linkId))); | 4195 | cstr_String(linkUrl_GmDocument(view->doc, view->hoverLink->linkId))); |
4165 | return iTrue; | 4196 | return iTrue; |
4166 | } | 4197 | } |
4167 | if (ev->button.button == SDL_BUTTON_RIGHT && | 4198 | if (ev->button.button == SDL_BUTTON_RIGHT && |
4168 | contains_Widget(w, init_I2(ev->button.x, ev->button.y))) { | 4199 | contains_Widget(w, init_I2(ev->button.x, ev->button.y))) { |
4169 | if (!isVisible_Widget(d->menu)) { | 4200 | if (!isVisible_Widget(d->menu)) { |
4170 | d->contextLink = d->hoverLink; | 4201 | d->contextLink = view->hoverLink; |
4171 | d->contextPos = init_I2(ev->button.x, ev->button.y); | 4202 | d->contextPos = init_I2(ev->button.x, ev->button.y); |
4172 | if (d->menu) { | 4203 | if (d->menu) { |
4173 | destroy_Widget(d->menu); | 4204 | destroy_Widget(d->menu); |
@@ -4179,7 +4210,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4179 | if (d->contextLink) { | 4210 | if (d->contextLink) { |
4180 | /* Context menu for a link. */ | 4211 | /* Context menu for a link. */ |
4181 | interactingWithLink_DocumentWidget_(d, d->contextLink->linkId); /* perhaps will be triggered */ | 4212 | interactingWithLink_DocumentWidget_(d, d->contextLink->linkId); /* perhaps will be triggered */ |
4182 | const iString *linkUrl = linkUrl_GmDocument(d->doc, d->contextLink->linkId); | 4213 | const iString *linkUrl = linkUrl_GmDocument(view->doc, d->contextLink->linkId); |
4183 | // const int linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId); | 4214 | // const int linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId); |
4184 | const iRangecc scheme = urlScheme_String(linkUrl); | 4215 | const iRangecc scheme = urlScheme_String(linkUrl); |
4185 | const iBool isGemini = equalCase_Rangecc(scheme, "gemini"); | 4216 | const iBool isGemini = equalCase_Rangecc(scheme, "gemini"); |
@@ -4249,7 +4280,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4249 | 2); | 4280 | 2); |
4250 | } | 4281 | } |
4251 | iString *linkLabel = collectNewRange_String( | 4282 | iString *linkLabel = collectNewRange_String( |
4252 | linkLabel_GmDocument(d->doc, d->contextLink->linkId)); | 4283 | linkLabel_GmDocument(view->doc, d->contextLink->linkId)); |
4253 | urlEncodeSpaces_String(linkLabel); | 4284 | urlEncodeSpaces_String(linkLabel); |
4254 | pushBackN_Array(&items, | 4285 | pushBackN_Array(&items, |
4255 | (iMenuItem[]){ { "---" }, | 4286 | (iMenuItem[]){ { "---" }, |
@@ -4370,7 +4401,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4370 | /* Enable hover state now that scrolling has surely finished. */ | 4401 | /* Enable hover state now that scrolling has surely finished. */ |
4371 | if (d->flags & noHoverWhileScrolling_DocumentWidgetFlag) { | 4402 | if (d->flags & noHoverWhileScrolling_DocumentWidgetFlag) { |
4372 | d->flags &= ~noHoverWhileScrolling_DocumentWidgetFlag; | 4403 | d->flags &= ~noHoverWhileScrolling_DocumentWidgetFlag; |
4373 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window(), ev->button.which)); | 4404 | updateHover_DocumentView_(view, mouseCoord_Window(get_Window(), ev->button.which)); |
4374 | } | 4405 | } |
4375 | if (~flags_Widget(w) & touchDrag_WidgetFlag) { | 4406 | if (~flags_Widget(w) & touchDrag_WidgetFlag) { |
4376 | iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iFalse); | 4407 | iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iFalse); |
@@ -4381,7 +4412,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4381 | beginMarkingSelection_DocumentWidget_(d, d->click.startPos); | 4412 | beginMarkingSelection_DocumentWidget_(d, d->click.startPos); |
4382 | extendRange_Rangecc( | 4413 | extendRange_Rangecc( |
4383 | &d->selectMark, | 4414 | &d->selectMark, |
4384 | range_String(source_GmDocument(d->doc)), | 4415 | range_String(source_GmDocument(view->doc)), |
4385 | bothStartAndEnd_RangeExtension | | 4416 | bothStartAndEnd_RangeExtension | |
4386 | (d->click.count == 2 ? word_RangeExtension : line_RangeExtension)); | 4417 | (d->click.count == 2 ? word_RangeExtension : line_RangeExtension)); |
4387 | d->initialSelectMark = d->selectMark; | 4418 | d->initialSelectMark = d->selectMark; |
@@ -4395,24 +4426,24 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4395 | case drag_ClickResult: { | 4426 | case drag_ClickResult: { |
4396 | if (d->grabbedPlayer) { | 4427 | if (d->grabbedPlayer) { |
4397 | iPlayer *plr = | 4428 | iPlayer *plr = |
4398 | audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(d->grabbedPlayer)); | 4429 | audioPlayer_Media(media_GmDocument(view->doc), mediaId_GmRun(d->grabbedPlayer)); |
4399 | iPlayerUI ui; | 4430 | iPlayerUI ui; |
4400 | init_PlayerUI(&ui, plr, runRect_DocumentWidget_(d, d->grabbedPlayer)); | 4431 | init_PlayerUI(&ui, plr, runRect_DocumentView_(view, d->grabbedPlayer)); |
4401 | float off = (float) delta_Click(&d->click).x / (float) width_Rect(ui.volumeSlider); | 4432 | float off = (float) delta_Click(&d->click).x / (float) width_Rect(ui.volumeSlider); |
4402 | setVolume_Player(plr, d->grabbedStartVolume + off); | 4433 | setVolume_Player(plr, d->grabbedStartVolume + off); |
4403 | refresh_Widget(w); | 4434 | refresh_Widget(w); |
4404 | return iTrue; | 4435 | return iTrue; |
4405 | } | 4436 | } |
4406 | /* Fold/unfold a preformatted block. */ | 4437 | /* Fold/unfold a preformatted block. */ |
4407 | if (~d->flags & selecting_DocumentWidgetFlag && d->hoverPre && | 4438 | if (~d->flags & selecting_DocumentWidgetFlag && view->hoverPre && |
4408 | preIsFolded_GmDocument(d->doc, preId_GmRun(d->hoverPre))) { | 4439 | preIsFolded_GmDocument(view->doc, preId_GmRun(view->hoverPre))) { |
4409 | return iTrue; | 4440 | return iTrue; |
4410 | } | 4441 | } |
4411 | /* Begin selecting a range of text. */ | 4442 | /* Begin selecting a range of text. */ |
4412 | if (~d->flags & selecting_DocumentWidgetFlag) { | 4443 | if (~d->flags & selecting_DocumentWidgetFlag) { |
4413 | beginMarkingSelection_DocumentWidget_(d, d->click.startPos); | 4444 | beginMarkingSelection_DocumentWidget_(d, d->click.startPos); |
4414 | } | 4445 | } |
4415 | iRangecc loc = sourceLoc_DocumentWidget_(d, pos_Click(&d->click)); | 4446 | iRangecc loc = sourceLoc_DocumentView_(view, pos_Click(&d->click)); |
4416 | if (d->selectMark.start == NULL) { | 4447 | if (d->selectMark.start == NULL) { |
4417 | d->selectMark = loc; | 4448 | d->selectMark = loc; |
4418 | } | 4449 | } |
@@ -4423,7 +4454,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4423 | movingSelectMarkEnd_DocumentWidgetFlag))) { | 4454 | movingSelectMarkEnd_DocumentWidgetFlag))) { |
4424 | const iRangecc mark = selectMark_DocumentWidget_(d); | 4455 | const iRangecc mark = selectMark_DocumentWidget_(d); |
4425 | const char * midMark = mark.start + size_Range(&mark) / 2; | 4456 | const char * midMark = mark.start + size_Range(&mark) / 2; |
4426 | const iRangecc loc = sourceLoc_DocumentWidget_(d, pos_Click(&d->click)); | 4457 | const iRangecc loc = sourceLoc_DocumentView_(view, pos_Click(&d->click)); |
4427 | const iBool isCloserToStart = d->selectMark.start > d->selectMark.end ? | 4458 | const iBool isCloserToStart = d->selectMark.start > d->selectMark.end ? |
4428 | (loc.start > midMark) : (loc.start < midMark); | 4459 | (loc.start > midMark) : (loc.start < midMark); |
4429 | iChangeFlags(d->flags, movingSelectMarkStart_DocumentWidgetFlag, isCloserToStart); | 4460 | iChangeFlags(d->flags, movingSelectMarkStart_DocumentWidgetFlag, isCloserToStart); |
@@ -4453,7 +4484,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4453 | if (d->flags & (selectWords_DocumentWidgetFlag | selectLines_DocumentWidgetFlag)) { | 4484 | if (d->flags & (selectWords_DocumentWidgetFlag | selectLines_DocumentWidgetFlag)) { |
4454 | extendRange_Rangecc( | 4485 | extendRange_Rangecc( |
4455 | &d->selectMark, | 4486 | &d->selectMark, |
4456 | range_String(source_GmDocument(d->doc)), | 4487 | range_String(source_GmDocument(view->doc)), |
4457 | (d->flags & movingSelectMarkStart_DocumentWidgetFlag ? moveStart_RangeExtension | 4488 | (d->flags & movingSelectMarkStart_DocumentWidgetFlag ? moveStart_RangeExtension |
4458 | : moveEnd_RangeExtension) | | 4489 | : moveEnd_RangeExtension) | |
4459 | (d->flags & selectWords_DocumentWidgetFlag ? word_RangeExtension | 4490 | (d->flags & selectWords_DocumentWidgetFlag ? word_RangeExtension |
@@ -4491,7 +4522,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4491 | setFocus_Widget(NULL); | 4522 | setFocus_Widget(NULL); |
4492 | /* Tap in tap selection mode. */ | 4523 | /* Tap in tap selection mode. */ |
4493 | if (flags_Widget(w) & touchDrag_WidgetFlag) { | 4524 | if (flags_Widget(w) & touchDrag_WidgetFlag) { |
4494 | const iRangecc tapLoc = sourceLoc_DocumentWidget_(d, pos_Click(&d->click)); | 4525 | const iRangecc tapLoc = sourceLoc_DocumentView_(view, pos_Click(&d->click)); |
4495 | /* Tapping on the selection will show a menu. */ | 4526 | /* Tapping on the selection will show a menu. */ |
4496 | const iRangecc mark = selectMark_DocumentWidget_(d); | 4527 | const iRangecc mark = selectMark_DocumentWidget_(d); |
4497 | if (tapLoc.start >= mark.start && tapLoc.end <= mark.end) { | 4528 | if (tapLoc.start >= mark.start && tapLoc.end <= mark.end) { |
@@ -4515,18 +4546,18 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4515 | return iTrue; | 4546 | return iTrue; |
4516 | } | 4547 | } |
4517 | } | 4548 | } |
4518 | if (d->hoverPre) { | 4549 | if (view->hoverPre) { |
4519 | togglePreFold_DocumentWidget_(d, preId_GmRun(d->hoverPre)); | 4550 | togglePreFold_DocumentWidget_(d, preId_GmRun(view->hoverPre)); |
4520 | return iTrue; | 4551 | return iTrue; |
4521 | } | 4552 | } |
4522 | if (d->hoverLink) { | 4553 | if (view->hoverLink) { |
4523 | /* TODO: Move this to a method. */ | 4554 | /* TODO: Move this to a method. */ |
4524 | const iGmLinkId linkId = d->hoverLink->linkId; | 4555 | const iGmLinkId linkId = view->hoverLink->linkId; |
4525 | const iMediaId linkMedia = mediaId_GmRun(d->hoverLink); | 4556 | const iMediaId linkMedia = mediaId_GmRun(view->hoverLink); |
4526 | const int linkFlags = linkFlags_GmDocument(d->doc, linkId); | 4557 | const int linkFlags = linkFlags_GmDocument(view->doc, linkId); |
4527 | iAssert(linkId); | 4558 | iAssert(linkId); |
4528 | /* Media links are opened inline by default. */ | 4559 | /* Media links are opened inline by default. */ |
4529 | if (isMediaLink_GmDocument(d->doc, linkId)) { | 4560 | if (isMediaLink_GmDocument(view->doc, linkId)) { |
4530 | if (linkFlags & content_GmLinkFlag && linkFlags & permanent_GmLinkFlag) { | 4561 | if (linkFlags & content_GmLinkFlag && linkFlags & permanent_GmLinkFlag) { |
4531 | /* We have the content and it cannot be dismissed, so nothing | 4562 | /* We have the content and it cannot be dismissed, so nothing |
4532 | further to do. */ | 4563 | further to do. */ |
@@ -4535,7 +4566,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4535 | if (!requestMedia_DocumentWidget_(d, linkId, iTrue)) { | 4566 | if (!requestMedia_DocumentWidget_(d, linkId, iTrue)) { |
4536 | if (linkFlags & content_GmLinkFlag) { | 4567 | if (linkFlags & content_GmLinkFlag) { |
4537 | /* Dismiss shown content on click. */ | 4568 | /* Dismiss shown content on click. */ |
4538 | setData_Media(media_GmDocument(d->doc), | 4569 | setData_Media(media_GmDocument(view->doc), |
4539 | linkId, | 4570 | linkId, |
4540 | NULL, | 4571 | NULL, |
4541 | NULL, | 4572 | NULL, |
@@ -4549,10 +4580,10 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4549 | be redone. */ | 4580 | be redone. */ |
4550 | } | 4581 | } |
4551 | } | 4582 | } |
4552 | redoLayout_GmDocument(d->doc); | 4583 | redoLayout_GmDocument(view->doc); |
4553 | d->hoverLink = NULL; | 4584 | view->hoverLink = NULL; |
4554 | clampScroll_DocumentWidget_(d); | 4585 | clampScroll_DocumentView_(view); |
4555 | updateVisible_DocumentWidget_(d); | 4586 | updateVisible_DocumentView_(view); |
4556 | invalidate_DocumentWidget_(d); | 4587 | invalidate_DocumentWidget_(d); |
4557 | refresh_Widget(w); | 4588 | refresh_Widget(w); |
4558 | return iTrue; | 4589 | return iTrue; |
@@ -4561,13 +4592,13 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4561 | /* Show the existing content again if we have it. */ | 4592 | /* Show the existing content again if we have it. */ |
4562 | iMediaRequest *req = findMediaRequest_DocumentWidget_(d, linkId); | 4593 | iMediaRequest *req = findMediaRequest_DocumentWidget_(d, linkId); |
4563 | if (req) { | 4594 | if (req) { |
4564 | setData_Media(media_GmDocument(d->doc), | 4595 | setData_Media(media_GmDocument(view->doc), |
4565 | linkId, | 4596 | linkId, |
4566 | meta_GmRequest(req->req), | 4597 | meta_GmRequest(req->req), |
4567 | body_GmRequest(req->req), | 4598 | body_GmRequest(req->req), |
4568 | allowHide_MediaFlag); | 4599 | allowHide_MediaFlag); |
4569 | redoLayout_GmDocument(d->doc); | 4600 | redoLayout_GmDocument(view->doc); |
4570 | updateVisible_DocumentWidget_(d); | 4601 | updateVisible_DocumentView_(view); |
4571 | invalidate_DocumentWidget_(d); | 4602 | invalidate_DocumentWidget_(d); |
4572 | refresh_Widget(w); | 4603 | refresh_Widget(w); |
4573 | return iTrue; | 4604 | return iTrue; |
@@ -4591,11 +4622,11 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4591 | postCommandf_Root(w->root, "open newtab:%d url:%s", | 4622 | postCommandf_Root(w->root, "open newtab:%d url:%s", |
4592 | tabMode, | 4623 | tabMode, |
4593 | cstr_String(absoluteUrl_String( | 4624 | cstr_String(absoluteUrl_String( |
4594 | d->mod.url, linkUrl_GmDocument(d->doc, linkId)))); | 4625 | d->mod.url, linkUrl_GmDocument(view->doc, linkId)))); |
4595 | } | 4626 | } |
4596 | else { | 4627 | else { |
4597 | const iString *url = absoluteUrl_String( | 4628 | const iString *url = absoluteUrl_String( |
4598 | d->mod.url, linkUrl_GmDocument(d->doc, linkId)); | 4629 | d->mod.url, linkUrl_GmDocument(view->doc, linkId)); |
4599 | makeQuestion_Widget( | 4630 | makeQuestion_Widget( |
4600 | uiTextCaution_ColorEscape "${heading.openlink}", | 4631 | uiTextCaution_ColorEscape "${heading.openlink}", |
4601 | format_CStr( | 4632 | format_CStr( |
@@ -4633,7 +4664,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4633 | iDeclareType(DrawContext) | 4664 | iDeclareType(DrawContext) |
4634 | 4665 | ||
4635 | struct Impl_DrawContext { | 4666 | struct Impl_DrawContext { |
4636 | const iDocumentWidget *widget; | 4667 | const iDocumentView *view; |
4637 | iRect widgetBounds; | 4668 | iRect widgetBounds; |
4638 | iRect docBounds; | 4669 | iRect docBounds; |
4639 | iRangei vis; | 4670 | iRangei vis; |
@@ -4677,7 +4708,7 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
4677 | } | 4708 | } |
4678 | if (~run->flags & decoration_GmRunFlag) { | 4709 | if (~run->flags & decoration_GmRunFlag) { |
4679 | const iInt2 visPos = | 4710 | const iInt2 visPos = |
4680 | add_I2(run->bounds.pos, addY_I2(d->viewPos, viewPos_DocumentWidget_(d->widget))); | 4711 | add_I2(run->bounds.pos, addY_I2(d->viewPos, viewPos_DocumentView_(d->view))); |
4681 | const iRect rangeRect = { addX_I2(visPos, x), init_I2(w, height_Rect(run->bounds)) }; | 4712 | const iRect rangeRect = { addX_I2(visPos, x), init_I2(w, height_Rect(run->bounds)) }; |
4682 | if (rangeRect.size.x) { | 4713 | if (rangeRect.size.x) { |
4683 | fillRect_Paint(&d->paint, rangeRect, color); | 4714 | fillRect_Paint(&d->paint, rangeRect, color); |
@@ -4692,12 +4723,12 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
4692 | /* Link URLs are not part of the visible document, so they are ignored above. Handle | 4723 | /* Link URLs are not part of the visible document, so they are ignored above. Handle |
4693 | these ranges as a special case. */ | 4724 | these ranges as a special case. */ |
4694 | if (run->linkId && run->flags & decoration_GmRunFlag) { | 4725 | if (run->linkId && run->flags & decoration_GmRunFlag) { |
4695 | const iRangecc url = linkUrlRange_GmDocument(d->widget->doc, run->linkId); | 4726 | const iRangecc url = linkUrlRange_GmDocument(d->view->doc, run->linkId); |
4696 | if (contains_Range(&url, mark.start) && | 4727 | if (contains_Range(&url, mark.start) && |
4697 | (contains_Range(&url, mark.end) || url.end == mark.end)) { | 4728 | (contains_Range(&url, mark.end) || url.end == mark.end)) { |
4698 | fillRect_Paint( | 4729 | fillRect_Paint( |
4699 | &d->paint, | 4730 | &d->paint, |
4700 | moved_Rect(run->visBounds, addY_I2(d->viewPos, viewPos_DocumentWidget_(d->widget))), | 4731 | moved_Rect(run->visBounds, addY_I2(d->viewPos, viewPos_DocumentView_(d->view))), |
4701 | color); | 4732 | color); |
4702 | } | 4733 | } |
4703 | } | 4734 | } |
@@ -4706,8 +4737,8 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
4706 | static void drawMark_DrawContext_(void *context, const iGmRun *run) { | 4737 | static void drawMark_DrawContext_(void *context, const iGmRun *run) { |
4707 | iDrawContext *d = context; | 4738 | iDrawContext *d = context; |
4708 | if (!isMedia_GmRun(run)) { | 4739 | if (!isMedia_GmRun(run)) { |
4709 | fillRange_DrawContext_(d, run, uiMatching_ColorId, d->widget->foundMark, &d->inFoundMark); | 4740 | fillRange_DrawContext_(d, run, uiMatching_ColorId, d->view->owner->foundMark, &d->inFoundMark); |
4710 | fillRange_DrawContext_(d, run, uiMarked_ColorId, d->widget->selectMark, &d->inSelectMark); | 4741 | fillRange_DrawContext_(d, run, uiMarked_ColorId, d->view->owner->selectMark, &d->inSelectMark); |
4711 | } | 4742 | } |
4712 | } | 4743 | } |
4713 | 4744 | ||
@@ -4723,7 +4754,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4723 | } | 4754 | } |
4724 | } | 4755 | } |
4725 | if (run->mediaType == image_MediaType) { | 4756 | if (run->mediaType == image_MediaType) { |
4726 | SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), mediaId_GmRun(run)); | 4757 | SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->view->doc), mediaId_GmRun(run)); |
4727 | const iRect dst = moved_Rect(run->visBounds, origin); | 4758 | const iRect dst = moved_Rect(run->visBounds, origin); |
4728 | if (tex) { | 4759 | if (tex) { |
4729 | fillRect_Paint(&d->paint, dst, tmBackground_ColorId); /* in case the image has alpha */ | 4760 | fillRect_Paint(&d->paint, dst, tmBackground_ColorId); /* in case the image has alpha */ |
@@ -4745,16 +4776,16 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4745 | return; | 4776 | return; |
4746 | } | 4777 | } |
4747 | enum iColorId fg = run->color; | 4778 | enum iColorId fg = run->color; |
4748 | const iGmDocument *doc = d->widget->doc; | 4779 | const iGmDocument *doc = d->view->doc; |
4749 | const int linkFlags = linkFlags_GmDocument(doc, run->linkId); | 4780 | const int linkFlags = linkFlags_GmDocument(doc, run->linkId); |
4750 | /* Hover state of a link. */ | 4781 | /* Hover state of a link. */ |
4751 | iBool isHover = | 4782 | iBool isHover = |
4752 | (run->linkId && d->widget->hoverLink && run->linkId == d->widget->hoverLink->linkId && | 4783 | (run->linkId && d->view->hoverLink && run->linkId == d->view->hoverLink->linkId && |
4753 | ~run->flags & decoration_GmRunFlag); | 4784 | ~run->flags & decoration_GmRunFlag); |
4754 | /* Visible (scrolled) position of the run. */ | 4785 | /* Visible (scrolled) position of the run. */ |
4755 | const iInt2 visPos = addX_I2(add_I2(run->visBounds.pos, origin), | 4786 | const iInt2 visPos = addX_I2(add_I2(run->visBounds.pos, origin), |
4756 | /* Preformatted runs can be scrolled. */ | 4787 | /* Preformatted runs can be scrolled. */ |
4757 | runOffset_DocumentWidget_(d->widget, run)); | 4788 | runOffset_DocumentView_(d->view, run)); |
4758 | const iRect visRect = { visPos, run->visBounds.size }; | 4789 | const iRect visRect = { visPos, run->visBounds.size }; |
4759 | /* Fill the background. */ { | 4790 | /* Fill the background. */ { |
4760 | #if 0 | 4791 | #if 0 |
@@ -4824,10 +4855,10 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4824 | } | 4855 | } |
4825 | else { | 4856 | else { |
4826 | if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) { | 4857 | if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) { |
4827 | const size_t ord = visibleLinkOrdinal_DocumentWidget_(d->widget, run->linkId); | 4858 | const size_t ord = visibleLinkOrdinal_DocumentView_(d->view, run->linkId); |
4828 | if (ord >= d->widget->ordinalBase) { | 4859 | if (ord >= d->view->owner->ordinalBase) { |
4829 | const iChar ordChar = | 4860 | const iChar ordChar = |
4830 | linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); | 4861 | linkOrdinalChar_DocumentWidget_(d->view->owner, ord - d->view->owner->ordinalBase); |
4831 | if (ordChar) { | 4862 | if (ordChar) { |
4832 | const char *circle = "\u25ef"; /* Large Circle */ | 4863 | const char *circle = "\u25ef"; /* Large Circle */ |
4833 | const int circleFont = FONT_ID(default_FontId, regular_FontStyle, contentRegular_FontSize); | 4864 | const int circleFont = FONT_ID(default_FontId, regular_FontStyle, contentRegular_FontSize); |
@@ -4901,8 +4932,8 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4901 | break; | 4932 | break; |
4902 | } | 4933 | } |
4903 | if (linkMedia.type != download_MediaType && /* can't cancel downloads currently */ | 4934 | if (linkMedia.type != download_MediaType && /* can't cancel downloads currently */ |
4904 | linkMedia.type != image_MediaType && | 4935 | linkMedia.type != image_MediaType && |
4905 | findMediaRequest_DocumentWidget_(d->widget, run->linkId)) { | 4936 | findMediaRequest_DocumentWidget_(d->view->owner, run->linkId)) { |
4906 | appendFormat_String( | 4937 | appendFormat_String( |
4907 | &text, " %s" close_Icon, isHover ? escape_Color(tmLinkText_ColorId) : ""); | 4938 | &text, " %s" close_Icon, isHover ? escape_Color(tmLinkText_ColorId) : ""); |
4908 | } | 4939 | } |
@@ -4922,7 +4953,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4922 | deinit_String(&text); | 4953 | deinit_String(&text); |
4923 | } | 4954 | } |
4924 | else if (run->flags & endOfLine_GmRunFlag && | 4955 | else if (run->flags & endOfLine_GmRunFlag && |
4925 | (mr = findMediaRequest_DocumentWidget_(d->widget, run->linkId)) != NULL) { | 4956 | (mr = findMediaRequest_DocumentWidget_(d->view->owner, run->linkId)) != NULL) { |
4926 | if (!isFinished_GmRequest(mr->req)) { | 4957 | if (!isFinished_GmRequest(mr->req)) { |
4927 | draw_Text(metaFont, | 4958 | draw_Text(metaFont, |
4928 | topRight_Rect(linkRect), | 4959 | topRight_Rect(linkRect), |
@@ -4931,87 +4962,6 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4931 | (float) bodySize_GmRequest(mr->req) / 1.0e6f); | 4962 | (float) bodySize_GmRequest(mr->req) / 1.0e6f); |
4932 | } | 4963 | } |
4933 | } | 4964 | } |
4934 | #if 0 | ||
4935 | else if (isHover) { | ||
4936 | /* TODO: Make this a dynamic overlay, not part of the VisBuf content. */ | ||
4937 | const iGmLinkId linkId = d->widget->hoverLink->linkId; | ||
4938 | const iString * url = linkUrl_GmDocument(doc, linkId); | ||
4939 | const int flags = linkFlags; | ||
4940 | iUrl parts; | ||
4941 | init_Url(&parts, url); | ||
4942 | fg = linkColor_GmDocument(doc, linkId, textHover_GmLinkPart); | ||
4943 | const enum iGmLinkScheme scheme = scheme_GmLinkFlag(flags); | ||
4944 | const iBool showHost = (flags & humanReadable_GmLinkFlag && | ||
4945 | (!isEmpty_Range(&parts.host) || | ||
4946 | scheme == mailto_GmLinkScheme)); | ||
4947 | const iBool showImage = (flags & imageFileExtension_GmLinkFlag) != 0; | ||
4948 | const iBool showAudio = (flags & audioFileExtension_GmLinkFlag) != 0; | ||
4949 | iString str; | ||
4950 | init_String(&str); | ||
4951 | /* Show scheme and host. */ | ||
4952 | if (run->flags & endOfLine_GmRunFlag && | ||
4953 | (flags & (imageFileExtension_GmLinkFlag | audioFileExtension_GmLinkFlag) || | ||
4954 | showHost)) { | ||
4955 | format_String( | ||
4956 | &str, | ||
4957 | "%s%s%s%s%s", | ||
4958 | showHost ? "" : "", | ||
4959 | showHost | ||
4960 | ? (scheme == mailto_GmLinkScheme ? cstr_String(url) | ||
4961 | : scheme != gemini_GmLinkScheme ? format_CStr("%s://%s", | ||
4962 | cstr_Rangecc(parts.scheme), | ||
4963 | cstr_Rangecc(parts.host)) | ||
4964 | : cstr_Rangecc(parts.host)) | ||
4965 | : "", | ||
4966 | showHost && (showImage || showAudio) ? " \u2014" : "", | ||
4967 | showImage || showAudio | ||
4968 | ? escape_Color(fg) | ||
4969 | : escape_Color(linkColor_GmDocument(doc, run->linkId, domain_GmLinkPart)), | ||
4970 | showImage || showAudio | ||
4971 | ? format_CStr(showImage ? " %s " photo_Icon : " %s \U0001f3b5", | ||
4972 | cstr_Lang(showImage ? "link.hint.image" : "link.hint.audio")) | ||
4973 | : ""); | ||
4974 | } | ||
4975 | if (run->flags & endOfLine_GmRunFlag && flags & visited_GmLinkFlag) { | ||
4976 | iDate date; | ||
4977 | init_Date(&date, linkTime_GmDocument(doc, run->linkId)); | ||
4978 | appendCStr_String(&str, " \u2014 "); | ||
4979 | appendCStr_String( | ||
4980 | &str, escape_Color(linkColor_GmDocument(doc, run->linkId, visited_GmLinkPart))); | ||
4981 | iString *dateStr = format_Date(&date, "%b %d"); | ||
4982 | append_String(&str, dateStr); | ||
4983 | delete_String(dateStr); | ||
4984 | } | ||
4985 | if (!isEmpty_String(&str)) { | ||
4986 | if (run->isRTL) { | ||
4987 | appendCStr_String(&str, " \u2014 "); | ||
4988 | } | ||
4989 | else { | ||
4990 | prependCStr_String(&str, " \u2014 "); | ||
4991 | } | ||
4992 | const iInt2 textSize = measure_Text(metaFont, cstr_String(&str)).bounds.size; | ||
4993 | int tx = topRight_Rect(linkRect).x; | ||
4994 | const char *msg = cstr_String(&str); | ||
4995 | if (run->isRTL) { | ||
4996 | tx = topLeft_Rect(linkRect).x - textSize.x; | ||
4997 | } | ||
4998 | if (tx + textSize.x > right_Rect(d->widgetBounds)) { | ||
4999 | tx = right_Rect(d->widgetBounds) - textSize.x; | ||
5000 | fillRect_Paint(&d->paint, (iRect){ init_I2(tx, top_Rect(linkRect)), textSize }, | ||
5001 | uiBackground_ColorId); | ||
5002 | msg += 4; /* skip the space and dash */ | ||
5003 | tx += measure_Text(metaFont, " \u2014").advance.x / 2; | ||
5004 | } | ||
5005 | drawAlign_Text(metaFont, | ||
5006 | init_I2(tx, top_Rect(linkRect)), | ||
5007 | linkColor_GmDocument(doc, run->linkId, domain_GmLinkPart), | ||
5008 | left_Alignment, | ||
5009 | "%s", | ||
5010 | msg); | ||
5011 | deinit_String(&str); | ||
5012 | } | ||
5013 | } | ||
5014 | #endif | ||
5015 | } | 4965 | } |
5016 | if (0) { | 4966 | if (0) { |
5017 | drawRect_Paint(&d->paint, (iRect){ visPos, run->bounds.size }, green_ColorId); | 4967 | drawRect_Paint(&d->paint, (iRect){ visPos, run->bounds.size }, green_ColorId); |
@@ -5030,16 +4980,16 @@ static int drawSideRect_(iPaint *p, iRect rect) { | |||
5030 | return fg; | 4980 | return fg; |
5031 | } | 4981 | } |
5032 | 4982 | ||
5033 | static int sideElementAvailWidth_DocumentWidget_(const iDocumentWidget *d) { | 4983 | static int sideElementAvailWidth_DocumentView_(const iDocumentView *d) { |
5034 | return left_Rect(documentBounds_DocumentWidget_(d)) - | 4984 | return left_Rect(documentBounds_DocumentView_(d)) - |
5035 | left_Rect(bounds_Widget(constAs_Widget(d))) - 2 * d->pageMargin * gap_UI; | 4985 | left_Rect(bounds_Widget(constAs_Widget(d->owner))) - 2 * d->pageMargin * gap_UI; |
5036 | } | 4986 | } |
5037 | 4987 | ||
5038 | static iBool isSideHeadingVisible_DocumentWidget_(const iDocumentWidget *d) { | 4988 | static iBool isSideHeadingVisible_DocumentView_(const iDocumentView *d) { |
5039 | return sideElementAvailWidth_DocumentWidget_(d) >= lineHeight_Text(banner_FontId) * 4.5f; | 4989 | return sideElementAvailWidth_DocumentView_(d) >= lineHeight_Text(banner_FontId) * 4.5f; |
5040 | } | 4990 | } |
5041 | 4991 | ||
5042 | static void updateSideIconBuf_DocumentWidget_(const iDocumentWidget *d) { | 4992 | static void updateSideIconBuf_DocumentView_(const iDocumentView *d) { |
5043 | if (!isExposed_Window(get_Window())) { | 4993 | if (!isExposed_Window(get_Window())) { |
5044 | return; | 4994 | return; |
5045 | } | 4995 | } |
@@ -5050,20 +5000,20 @@ static void updateSideIconBuf_DocumentWidget_(const iDocumentWidget *d) { | |||
5050 | dbuf->sideIconBuf = NULL; | 5000 | dbuf->sideIconBuf = NULL; |
5051 | } | 5001 | } |
5052 | // const iGmRun *banner = siteBanner_GmDocument(d->doc); | 5002 | // const iGmRun *banner = siteBanner_GmDocument(d->doc); |
5053 | if (isEmpty_Banner(d->banner)) { | 5003 | if (isEmpty_Banner(d->owner->banner)) { |
5054 | return; | 5004 | return; |
5055 | } | 5005 | } |
5056 | const int margin = gap_UI * d->pageMargin; | 5006 | const int margin = gap_UI * d->pageMargin; |
5057 | const int minBannerSize = lineHeight_Text(banner_FontId) * 2; | 5007 | const int minBannerSize = lineHeight_Text(banner_FontId) * 2; |
5058 | const iChar icon = siteIcon_GmDocument(d->doc); | 5008 | const iChar icon = siteIcon_GmDocument(d->doc); |
5059 | const int avail = sideElementAvailWidth_DocumentWidget_(d) - margin; | 5009 | const int avail = sideElementAvailWidth_DocumentView_(d) - margin; |
5060 | iBool isHeadingVisible = isSideHeadingVisible_DocumentWidget_(d); | 5010 | iBool isHeadingVisible = isSideHeadingVisible_DocumentView_(d); |
5061 | /* Determine the required size. */ | 5011 | /* Determine the required size. */ |
5062 | iInt2 bufSize = init1_I2(minBannerSize); | 5012 | iInt2 bufSize = init1_I2(minBannerSize); |
5063 | const int sideHeadingFont = FONT_ID(documentHeading_FontId, regular_FontStyle, contentBig_FontSize); | 5013 | const int sideHeadingFont = FONT_ID(documentHeading_FontId, regular_FontStyle, contentBig_FontSize); |
5064 | if (isHeadingVisible) { | 5014 | if (isHeadingVisible) { |
5065 | const iInt2 headingSize = measureWrapRange_Text(sideHeadingFont, avail, | 5015 | const iInt2 headingSize = measureWrapRange_Text(sideHeadingFont, avail, |
5066 | currentHeading_DocumentWidget_(d)).bounds.size; | 5016 | currentHeading_DocumentView_(d)).bounds.size; |
5067 | if (headingSize.x > 0) { | 5017 | if (headingSize.x > 0) { |
5068 | bufSize.y += gap_Text + headingSize.y; | 5018 | bufSize.y += gap_Text + headingSize.y; |
5069 | bufSize.x = iMax(bufSize.x, headingSize.x); | 5019 | bufSize.x = iMax(bufSize.x, headingSize.x); |
@@ -5090,7 +5040,7 @@ static void updateSideIconBuf_DocumentWidget_(const iDocumentWidget *d) { | |||
5090 | drawCentered_Text(banner_FontId, iconRect, iTrue, fg, "%s", cstr_String(&str)); | 5040 | drawCentered_Text(banner_FontId, iconRect, iTrue, fg, "%s", cstr_String(&str)); |
5091 | deinit_String(&str); | 5041 | deinit_String(&str); |
5092 | if (isHeadingVisible) { | 5042 | if (isHeadingVisible) { |
5093 | iRangecc text = currentHeading_DocumentWidget_(d); | 5043 | iRangecc text = currentHeading_DocumentView_(d); |
5094 | iInt2 pos = addY_I2(bottomLeft_Rect(iconRect), gap_Text); | 5044 | iInt2 pos = addY_I2(bottomLeft_Rect(iconRect), gap_Text); |
5095 | const int font = sideHeadingFont; | 5045 | const int font = sideHeadingFont; |
5096 | drawWrapRange_Text(font, pos, avail, tmBannerSideTitle_ColorId, text); | 5046 | drawWrapRange_Text(font, pos, avail, tmBannerSideTitle_ColorId, text); |
@@ -5099,10 +5049,10 @@ static void updateSideIconBuf_DocumentWidget_(const iDocumentWidget *d) { | |||
5099 | SDL_SetTextureBlendMode(dbuf->sideIconBuf, SDL_BLENDMODE_BLEND); | 5049 | SDL_SetTextureBlendMode(dbuf->sideIconBuf, SDL_BLENDMODE_BLEND); |
5100 | } | 5050 | } |
5101 | 5051 | ||
5102 | static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | 5052 | static void drawSideElements_DocumentView_(const iDocumentView *d) { |
5103 | const iWidget *w = constAs_Widget(d); | 5053 | const iWidget *w = constAs_Widget(d->owner); |
5104 | const iRect bounds = bounds_Widget(w); | 5054 | const iRect bounds = bounds_Widget(w); |
5105 | const iRect docBounds = documentBounds_DocumentWidget_(d); | 5055 | const iRect docBounds = documentBounds_DocumentView_(d); |
5106 | const int margin = gap_UI * d->pageMargin; | 5056 | const int margin = gap_UI * d->pageMargin; |
5107 | float opacity = value_Anim(&d->sideOpacity); | 5057 | float opacity = value_Anim(&d->sideOpacity); |
5108 | const int avail = left_Rect(docBounds) - left_Rect(bounds) - 2 * margin; | 5058 | const int avail = left_Rect(docBounds) - left_Rect(bounds) - 2 * margin; |
@@ -5140,20 +5090,20 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | |||
5140 | unsetClip_Paint(&p); | 5090 | unsetClip_Paint(&p); |
5141 | } | 5091 | } |
5142 | 5092 | ||
5143 | static void drawMedia_DocumentWidget_(const iDocumentWidget *d, iPaint *p) { | 5093 | static void drawMedia_DocumentView_(const iDocumentView *d, iPaint *p) { |
5144 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 5094 | iConstForEach(PtrArray, i, &d->visibleMedia) { |
5145 | const iGmRun * run = i.ptr; | 5095 | const iGmRun * run = i.ptr; |
5146 | if (run->mediaType == audio_MediaType) { | 5096 | if (run->mediaType == audio_MediaType) { |
5147 | iPlayerUI ui; | 5097 | iPlayerUI ui; |
5148 | init_PlayerUI(&ui, | 5098 | init_PlayerUI(&ui, |
5149 | audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)), | 5099 | audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)), |
5150 | runRect_DocumentWidget_(d, run)); | 5100 | runRect_DocumentView_(d, run)); |
5151 | draw_PlayerUI(&ui, p); | 5101 | draw_PlayerUI(&ui, p); |
5152 | } | 5102 | } |
5153 | else if (run->mediaType == download_MediaType) { | 5103 | else if (run->mediaType == download_MediaType) { |
5154 | iDownloadUI ui; | 5104 | iDownloadUI ui; |
5155 | init_DownloadUI(&ui, constMedia_GmDocument(d->doc), run->mediaId, | 5105 | init_DownloadUI(&ui, constMedia_GmDocument(d->doc), run->mediaId, |
5156 | runRect_DocumentWidget_(d, run)); | 5106 | runRect_DocumentView_(d, run)); |
5157 | draw_DownloadUI(&ui, p); | 5107 | draw_DownloadUI(&ui, p); |
5158 | } | 5108 | } |
5159 | } | 5109 | } |
@@ -5166,20 +5116,23 @@ static void extend_GmRunRange_(iGmRunRange *runs) { | |||
5166 | } | 5116 | } |
5167 | } | 5117 | } |
5168 | 5118 | ||
5169 | static iBool render_DocumentWidget_(const iDocumentWidget *d, iDrawContext *ctx, iBool prerenderExtra) { | 5119 | static iBool render_DocumentView_(const iDocumentView *d, iDrawContext *ctx, iBool prerenderExtra) { |
5170 | iBool didDraw = iFalse; | 5120 | iBool didDraw = iFalse; |
5171 | const iRect bounds = bounds_Widget(constAs_Widget(d)); | 5121 | const iRect bounds = bounds_Widget(constAs_Widget(d->owner)); |
5172 | const iRect ctxWidgetBounds = init_Rect( | 5122 | const iRect ctxWidgetBounds = |
5173 | 0, 0, width_Rect(bounds) - constAs_Widget(d->scroll)->rect.size.x, height_Rect(bounds)); | 5123 | init_Rect(0, |
5174 | const iRangei full = { 0, size_GmDocument(d->doc).y }; | 5124 | 0, |
5175 | const iRangei vis = ctx->vis; | 5125 | width_Rect(bounds) - constAs_Widget(d->owner->scroll)->rect.size.x, |
5176 | iVisBuf *visBuf = d->visBuf; /* will be updated now */ | 5126 | height_Rect(bounds)); |
5127 | const iRangei full = { 0, size_GmDocument(d->doc).y }; | ||
5128 | const iRangei vis = ctx->vis; | ||
5129 | iVisBuf *visBuf = d->visBuf; /* will be updated now */ | ||
5177 | d->drawBufs->lastRenderTime = SDL_GetTicks(); | 5130 | d->drawBufs->lastRenderTime = SDL_GetTicks(); |
5178 | /* Swap buffers around to have room available both before and after the visible region. */ | 5131 | /* Swap buffers around to have room available both before and after the visible region. */ |
5179 | allocVisBuffer_DocumentWidget_(d); | 5132 | allocVisBuffer_DocumentView_(d); |
5180 | reposition_VisBuf(visBuf, vis); | 5133 | reposition_VisBuf(visBuf, vis); |
5181 | /* Redraw the invalid ranges. */ | 5134 | /* Redraw the invalid ranges. */ |
5182 | if (~flags_Widget(constAs_Widget(d)) & destroyPending_WidgetFlag) { | 5135 | if (~flags_Widget(constAs_Widget(d->owner)) & destroyPending_WidgetFlag) { |
5183 | iPaint *p = &ctx->paint; | 5136 | iPaint *p = &ctx->paint; |
5184 | init_Paint(p); | 5137 | init_Paint(p); |
5185 | iForIndices(i, visBuf->buffers) { | 5138 | iForIndices(i, visBuf->buffers) { |
@@ -5326,6 +5279,7 @@ static iBool render_DocumentWidget_(const iDocumentWidget *d, iDrawContext *ctx, | |||
5326 | } | 5279 | } |
5327 | 5280 | ||
5328 | static void prerender_DocumentWidget_(iAny *context) { | 5281 | static void prerender_DocumentWidget_(iAny *context) { |
5282 | iAssert(isInstance_Object(context, &Class_DocumentWidget)); | ||
5329 | if (current_Root() == NULL) { | 5283 | if (current_Root() == NULL) { |
5330 | /* The widget has probably been removed from the widget tree, pending destruction. | 5284 | /* The widget has probably been removed from the widget tree, pending destruction. |
5331 | Tickers are not cancelled until the widget is actually destroyed. */ | 5285 | Tickers are not cancelled until the widget is actually destroyed. */ |
@@ -5333,16 +5287,16 @@ static void prerender_DocumentWidget_(iAny *context) { | |||
5333 | } | 5287 | } |
5334 | const iDocumentWidget *d = context; | 5288 | const iDocumentWidget *d = context; |
5335 | iDrawContext ctx = { | 5289 | iDrawContext ctx = { |
5336 | .widget = d, | 5290 | .view = &d->view, |
5337 | .docBounds = documentBounds_DocumentWidget_(d), | 5291 | .docBounds = documentBounds_DocumentView_(&d->view), |
5338 | .vis = visibleRange_DocumentWidget_(d), | 5292 | .vis = visibleRange_DocumentView_(&d->view), |
5339 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0 | 5293 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0 |
5340 | }; | 5294 | }; |
5341 | // printf("%u prerendering\n", SDL_GetTicks()); | 5295 | // printf("%u prerendering\n", SDL_GetTicks()); |
5342 | if (d->visBuf->buffers[0].texture) { | 5296 | if (d->view.visBuf->buffers[0].texture) { |
5343 | makePaletteGlobal_GmDocument(d->doc); | 5297 | makePaletteGlobal_GmDocument(d->view.doc); |
5344 | if (render_DocumentWidget_(d, &ctx, iTrue /* just fill up progressively */)) { | 5298 | if (render_DocumentView_(&d->view, &ctx, iTrue /* just fill up progressively */)) { |
5345 | /* Something was drawn, should check if there is still more to do. */ | 5299 | /* Something was drawn, should check later if there is still more to do. */ |
5346 | addTicker_App(prerender_DocumentWidget_, context); | 5300 | addTicker_App(prerender_DocumentWidget_, context); |
5347 | } | 5301 | } |
5348 | } | 5302 | } |
@@ -5358,47 +5312,44 @@ static void checkPendingInvalidation_DocumentWidget_(const iDocumentWidget *d) { | |||
5358 | } | 5312 | } |
5359 | } | 5313 | } |
5360 | 5314 | ||
5361 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | 5315 | static void draw_DocumentView_(const iDocumentView *d) { |
5362 | const iWidget *w = constAs_Widget(d); | 5316 | const iWidget *w = constAs_Widget(d->owner); |
5363 | const iRect bounds = bounds_Widget(w); | 5317 | const iRect bounds = bounds_Widget(w); |
5364 | const iRect boundsWithoutVisOff = boundsWithoutVisualOffset_Widget(w); | 5318 | const iRect boundsWithoutVisOff = boundsWithoutVisualOffset_Widget(w); |
5365 | const iRect clipBounds = intersect_Rect(bounds, boundsWithoutVisOff); | 5319 | const iRect clipBounds = intersect_Rect(bounds, boundsWithoutVisOff); |
5366 | if (width_Rect(bounds) <= 0) { | ||
5367 | return; | ||
5368 | } | ||
5369 | checkPendingInvalidation_DocumentWidget_(d); | ||
5370 | /* Each document has its own palette, but the drawing routines rely on a global one. | 5320 | /* Each document has its own palette, but the drawing routines rely on a global one. |
5371 | As we're now drawing a document, ensure that the right palette is in effect. | 5321 | As we're now drawing a document, ensure that the right palette is in effect. |
5372 | Document theme colors can be used elsewhere, too, but first a document's palette | 5322 | Document theme colors can be used elsewhere, too, but first a document's palette |
5373 | must be made global. */ | 5323 | must be made global. */ |
5374 | makePaletteGlobal_GmDocument(d->doc); | 5324 | makePaletteGlobal_GmDocument(d->doc); |
5375 | if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) { | 5325 | if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) { |
5376 | updateTimestampBuf_DocumentWidget_(d); | 5326 | updateTimestampBuf_DocumentView_(d); |
5377 | } | 5327 | } |
5378 | if (d->drawBufs->flags & updateSideBuf_DrawBufsFlag) { | 5328 | if (d->drawBufs->flags & updateSideBuf_DrawBufsFlag) { |
5379 | updateSideIconBuf_DocumentWidget_(d); | 5329 | updateSideIconBuf_DocumentView_(d); |
5380 | } | 5330 | } |
5381 | const iRect docBounds = documentBounds_DocumentWidget_(d); | 5331 | const iRect docBounds = documentBounds_DocumentView_(d); |
5382 | const iRangei vis = visibleRange_DocumentWidget_(d); | 5332 | const iRangei vis = visibleRange_DocumentView_(d); |
5383 | iDrawContext ctx = { | 5333 | iDrawContext ctx = { |
5384 | .widget = d, | 5334 | .view = d, |
5385 | .docBounds = docBounds, | 5335 | .docBounds = docBounds, |
5386 | .vis = vis, | 5336 | .vis = vis, |
5387 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0, | 5337 | .showLinkNumbers = (d->owner->flags & showLinkNumbers_DocumentWidgetFlag) != 0, |
5388 | }; | 5338 | }; |
5389 | init_Paint(&ctx.paint); | 5339 | init_Paint(&ctx.paint); |
5390 | render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); | 5340 | render_DocumentView_(d, &ctx, iFalse /* just the mandatory parts */); |
5391 | int yTop = docBounds.pos.y + viewPos_DocumentWidget_(d); | 5341 | iBanner *banner = d->owner->banner; |
5342 | int yTop = docBounds.pos.y + viewPos_DocumentView_(d); | ||
5392 | const iBool isDocEmpty = size_GmDocument(d->doc).y == 0; | 5343 | const iBool isDocEmpty = size_GmDocument(d->doc).y == 0; |
5393 | const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; | 5344 | const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; |
5394 | if (!isDocEmpty || !isEmpty_Banner(d->banner)) { | 5345 | if (!isDocEmpty || !isEmpty_Banner(banner)) { |
5395 | const int docBgColor = isDocEmpty ? tmBannerBackground_ColorId : tmBackground_ColorId; | 5346 | const int docBgColor = isDocEmpty ? tmBannerBackground_ColorId : tmBackground_ColorId; |
5396 | setClip_Paint(&ctx.paint, clipBounds); | 5347 | setClip_Paint(&ctx.paint, clipBounds); |
5397 | if (!isDocEmpty) { | 5348 | if (!isDocEmpty) { |
5398 | draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds)); | 5349 | draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds)); |
5399 | } | 5350 | } |
5400 | /* Text markers. */ | 5351 | /* Text markers. */ |
5401 | if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) { | 5352 | if (!isEmpty_Range(&d->owner->foundMark) || !isEmpty_Range(&d->owner->selectMark)) { |
5402 | SDL_Renderer *render = renderer_Window(get_Window()); | 5353 | SDL_Renderer *render = renderer_Window(get_Window()); |
5403 | ctx.firstMarkRect = zero_Rect(); | 5354 | ctx.firstMarkRect = zero_Rect(); |
5404 | ctx.lastMarkRect = zero_Rect(); | 5355 | ctx.lastMarkRect = zero_Rect(); |
@@ -5408,14 +5359,14 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
5408 | ctx.viewPos = topLeft_Rect(docBounds); | 5359 | ctx.viewPos = topLeft_Rect(docBounds); |
5409 | /* Marker starting outside the visible range? */ | 5360 | /* Marker starting outside the visible range? */ |
5410 | if (d->visibleRuns.start) { | 5361 | if (d->visibleRuns.start) { |
5411 | if (!isEmpty_Range(&d->selectMark) && | 5362 | if (!isEmpty_Range(&d->owner->selectMark) && |
5412 | d->selectMark.start < d->visibleRuns.start->text.start && | 5363 | d->owner->selectMark.start < d->visibleRuns.start->text.start && |
5413 | d->selectMark.end > d->visibleRuns.start->text.start) { | 5364 | d->owner->selectMark.end > d->visibleRuns.start->text.start) { |
5414 | ctx.inSelectMark = iTrue; | 5365 | ctx.inSelectMark = iTrue; |
5415 | } | 5366 | } |
5416 | if (isEmpty_Range(&d->foundMark) && | 5367 | if (isEmpty_Range(&d->owner->foundMark) && |
5417 | d->foundMark.start < d->visibleRuns.start->text.start && | 5368 | d->owner->foundMark.start < d->visibleRuns.start->text.start && |
5418 | d->foundMark.end > d->visibleRuns.start->text.start) { | 5369 | d->owner->foundMark.end > d->visibleRuns.start->text.start) { |
5419 | ctx.inFoundMark = iTrue; | 5370 | ctx.inFoundMark = iTrue; |
5420 | } | 5371 | } |
5421 | } | 5372 | } |
@@ -5427,26 +5378,26 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
5427 | drawPin_Paint(&ctx.paint, ctx.lastMarkRect, 1, tmQuote_ColorId); | 5378 | drawPin_Paint(&ctx.paint, ctx.lastMarkRect, 1, tmQuote_ColorId); |
5428 | } | 5379 | } |
5429 | } | 5380 | } |
5430 | drawMedia_DocumentWidget_(d, &ctx.paint); | 5381 | drawMedia_DocumentView_(d, &ctx.paint); |
5431 | /* Fill the top and bottom, in case the document is short. */ | 5382 | /* Fill the top and bottom, in case the document is short. */ |
5432 | if (yTop > top_Rect(bounds)) { | 5383 | if (yTop > top_Rect(bounds)) { |
5433 | fillRect_Paint(&ctx.paint, | 5384 | fillRect_Paint(&ctx.paint, |
5434 | (iRect){ bounds.pos, init_I2(bounds.size.x, yTop - top_Rect(bounds)) }, | 5385 | (iRect){ bounds.pos, init_I2(bounds.size.x, yTop - top_Rect(bounds)) }, |
5435 | !isEmpty_Banner(d->banner) ? tmBannerBackground_ColorId | 5386 | !isEmpty_Banner(banner) ? tmBannerBackground_ColorId |
5436 | : docBgColor); | 5387 | : docBgColor); |
5437 | } | 5388 | } |
5438 | /* Banner. */ | 5389 | /* Banner. */ |
5439 | if (!isDocEmpty || numItems_Banner(d->banner) > 0) { | 5390 | if (!isDocEmpty || numItems_Banner(banner) > 0) { |
5440 | /* Fill the part between the banner and the top of the document. */ | 5391 | /* Fill the part between the banner and the top of the document. */ |
5441 | fillRect_Paint(&ctx.paint, | 5392 | fillRect_Paint(&ctx.paint, |
5442 | (iRect){ init_I2(left_Rect(bounds), | 5393 | (iRect){ init_I2(left_Rect(bounds), |
5443 | top_Rect(docBounds) + viewPos_DocumentWidget_(d) - | 5394 | top_Rect(docBounds) + viewPos_DocumentView_(d) - |
5444 | documentTopPad_DocumentWidget_(d)), | 5395 | documentTopPad_DocumentView_(d)), |
5445 | init_I2(bounds.size.x, documentTopPad_DocumentWidget_(d)) }, | 5396 | init_I2(bounds.size.x, documentTopPad_DocumentView_(d)) }, |
5446 | docBgColor); | 5397 | docBgColor); |
5447 | setPos_Banner(d->banner, addY_I2(topLeft_Rect(docBounds), | 5398 | setPos_Banner(banner, addY_I2(topLeft_Rect(docBounds), |
5448 | -pos_SmoothScroll(&d->scrollY))); | 5399 | -pos_SmoothScroll(&d->scrollY))); |
5449 | draw_Banner(d->banner); | 5400 | draw_Banner(banner); |
5450 | } | 5401 | } |
5451 | const int yBottom = yTop + size_GmDocument(d->doc).y; | 5402 | const int yBottom = yTop + size_GmDocument(d->doc).y; |
5452 | if (yBottom < bottom_Rect(bounds)) { | 5403 | if (yBottom < bottom_Rect(bounds)) { |
@@ -5455,60 +5406,107 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
5455 | !isDocEmpty ? docBgColor : tmBannerBackground_ColorId); | 5406 | !isDocEmpty ? docBgColor : tmBannerBackground_ColorId); |
5456 | } | 5407 | } |
5457 | unsetClip_Paint(&ctx.paint); | 5408 | unsetClip_Paint(&ctx.paint); |
5458 | drawSideElements_DocumentWidget_(d); | 5409 | drawSideElements_DocumentView_(d); |
5459 | if (deviceType_App() == desktop_AppDeviceType && prefs_App()->hoverLink && d->linkInfo) { | 5410 | /* Alt text. */ |
5460 | const int pad = gap_UI; | 5411 | const float altTextOpacity = value_Anim(&d->altTextOpacity) * 6 - 5; |
5461 | update_LinkInfo(d->linkInfo, | 5412 | if (d->hoverAltPre && altTextOpacity > 0) { |
5462 | d->doc, | 5413 | const iGmPreMeta *meta = preMeta_GmDocument(d->doc, preId_GmRun(d->hoverAltPre)); |
5463 | d->hoverLink ? d->hoverLink->linkId : 0, | 5414 | if (meta->flags & topLeft_GmPreMetaFlag && ~meta->flags & decoration_GmRunFlag && |
5464 | width_Rect(bounds) - 2 * pad); | 5415 | !isEmpty_Range(&meta->altText)) { |
5465 | const iInt2 infoSize = size_LinkInfo(d->linkInfo); | 5416 | const int margin = 3 * gap_UI / 2; |
5466 | iInt2 infoPos = add_I2(bottomLeft_Rect(bounds), init_I2(pad, -infoSize.y - pad)); | 5417 | const int altFont = uiLabel_FontId; |
5467 | if (d->hoverLink) { | 5418 | const int wrap = docBounds.size.x - 2 * margin; |
5468 | const iRect runRect = runRect_DocumentWidget_(d, d->hoverLink); | 5419 | iInt2 pos = addY_I2(add_I2(docBounds.pos, meta->pixelRect.pos), |
5469 | d->linkInfo->isAltPos = | 5420 | viewPos_DocumentView_(d)); |
5470 | (bottom_Rect(runRect) >= infoPos.y - lineHeight_Text(paragraph_FontId)); | 5421 | const iInt2 textSize = measureWrapRange_Text(altFont, wrap, meta->altText).bounds.size; |
5471 | } | 5422 | pos.y -= textSize.y + gap_UI; |
5472 | if (d->linkInfo->isAltPos) { | 5423 | pos.y = iMax(pos.y, top_Rect(bounds)); |
5473 | infoPos.y = top_Rect(bounds) + pad; | 5424 | const iRect altRect = { pos, init_I2(docBounds.size.x, textSize.y) }; |
5425 | ctx.paint.alpha = altTextOpacity * 255; | ||
5426 | if (altTextOpacity < 1) { | ||
5427 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); | ||
5428 | } | ||
5429 | fillRect_Paint(&ctx.paint, altRect, tmBackgroundAltText_ColorId); | ||
5430 | drawRect_Paint(&ctx.paint, altRect, tmFrameAltText_ColorId); | ||
5431 | setOpacity_Text(altTextOpacity); | ||
5432 | drawWrapRange_Text(altFont, addX_I2(pos, margin), wrap, | ||
5433 | tmQuote_ColorId, meta->altText); | ||
5434 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | ||
5435 | setOpacity_Text(1.0f); | ||
5474 | } | 5436 | } |
5475 | draw_LinkInfo(d->linkInfo, infoPos); | ||
5476 | } | 5437 | } |
5477 | #if 0 | 5438 | /* Touch selection indicator. */ |
5478 | if (prefs_App()->hoverLink && d->hoverLink) { | 5439 | if (isTouchSelecting) { |
5479 | const int font = uiLabel_FontId; | 5440 | iRect rect = { topLeft_Rect(bounds), |
5480 | const iRangecc linkUrl = range_String(linkUrl_GmDocument(d->doc, d->hoverLink->linkId)); | 5441 | init_I2(width_Rect(bounds), lineHeight_Text(uiLabelBold_FontId)) }; |
5481 | const iInt2 size = measureRange_Text(font, linkUrl).bounds.size; | 5442 | fillRect_Paint(&ctx.paint, rect, uiTextAction_ColorId); |
5482 | const iRect linkRect = { addY_I2(bottomLeft_Rect(bounds), -size.y), | 5443 | const iRangecc mark = selectMark_DocumentWidget_(d->owner); |
5483 | addX_I2(size, 2 * gap_UI) }; | 5444 | drawCentered_Text(uiLabelBold_FontId, |
5484 | fillRect_Paint(&ctx.paint, linkRect, tmBackground_ColorId); | 5445 | rect, |
5485 | drawRange_Text(font, addX_I2(topLeft_Rect(linkRect), gap_UI), tmParagraph_ColorId, linkUrl); | 5446 | iFalse, |
5447 | uiBackground_ColorId, | ||
5448 | "%zu bytes selected", /* TODO: i18n */ | ||
5449 | size_Range(&mark)); | ||
5486 | } | 5450 | } |
5487 | #endif | ||
5488 | } | 5451 | } |
5452 | } | ||
5453 | |||
5454 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | ||
5455 | const iWidget *w = constAs_Widget(d); | ||
5456 | const iRect bounds = bounds_Widget(w); | ||
5457 | const iRect boundsWithoutVisOff = boundsWithoutVisualOffset_Widget(w); | ||
5458 | const iRect clipBounds = intersect_Rect(bounds, boundsWithoutVisOff); | ||
5459 | if (width_Rect(bounds) <= 0) { | ||
5460 | return; | ||
5461 | } | ||
5462 | checkPendingInvalidation_DocumentWidget_(d); | ||
5463 | draw_DocumentView_(&d->view); | ||
5464 | iPaint p; | ||
5465 | init_Paint(&p); | ||
5489 | if (colorTheme_App() == pureWhite_ColorTheme) { | 5466 | if (colorTheme_App() == pureWhite_ColorTheme) { |
5490 | drawHLine_Paint(&ctx.paint, topLeft_Rect(bounds), width_Rect(bounds), uiSeparator_ColorId); | 5467 | drawHLine_Paint(&p, topLeft_Rect(bounds), width_Rect(bounds), uiSeparator_ColorId); |
5491 | } | 5468 | } |
5492 | /* Pull action indicator. */ | 5469 | /* Pull action indicator. */ |
5493 | if (deviceType_App() != desktop_AppDeviceType) { | 5470 | if (deviceType_App() != desktop_AppDeviceType) { |
5494 | float pullPos = pullActionPos_SmoothScroll(&d->scrollY); | 5471 | float pullPos = pullActionPos_SmoothScroll(&d->view.scrollY); |
5495 | /* Account for the part where the indicator isn't yet visible. */ | 5472 | /* Account for the part where the indicator isn't yet visible. */ |
5496 | pullPos = (pullPos - 0.2f) / 0.8f; | 5473 | pullPos = (pullPos - 0.2f) / 0.8f; |
5497 | iRect indRect = initCentered_Rect(init_I2(mid_Rect(bounds).x, | 5474 | iRect indRect = initCentered_Rect(init_I2(mid_Rect(bounds).x, |
5498 | top_Rect(bounds) - 5 * gap_UI - | 5475 | top_Rect(bounds) - 5 * gap_UI - |
5499 | pos_SmoothScroll(&d->scrollY)), | 5476 | pos_SmoothScroll(&d->view.scrollY)), |
5500 | init_I2(20 * gap_UI, 2 * gap_UI)); | 5477 | init_I2(20 * gap_UI, 2 * gap_UI)); |
5501 | setClip_Paint(&ctx.paint, clipBounds); | 5478 | setClip_Paint(&p, clipBounds); |
5502 | int color = pullPos < 1.0f ? tmBannerItemFrame_ColorId : tmBannerItemText_ColorId; | 5479 | int color = pullPos < 1.0f ? tmBannerItemFrame_ColorId : tmBannerItemText_ColorId; |
5503 | drawRect_Paint(&ctx.paint, indRect, color); | 5480 | drawRect_Paint(&p, indRect, color); |
5504 | if (pullPos > 0) { | 5481 | if (pullPos > 0) { |
5505 | shrink_Rect(&indRect, divi_I2(gap2_UI, 2)); | 5482 | shrink_Rect(&indRect, divi_I2(gap2_UI, 2)); |
5506 | indRect.size.x *= pullPos; | 5483 | indRect.size.x *= pullPos; |
5507 | fillRect_Paint(&ctx.paint, indRect, color); | 5484 | fillRect_Paint(&p, indRect, color); |
5508 | } | 5485 | } |
5509 | unsetClip_Paint(&ctx.paint); | 5486 | unsetClip_Paint(&p); |
5510 | } | 5487 | } |
5488 | /* Scroll bar. */ | ||
5511 | drawChildren_Widget(w); | 5489 | drawChildren_Widget(w); |
5490 | /* Information about the hovered link. */ | ||
5491 | if (deviceType_App() == desktop_AppDeviceType && prefs_App()->hoverLink && d->linkInfo) { | ||
5492 | const int pad = gap_UI; | ||
5493 | update_LinkInfo(d->linkInfo, | ||
5494 | d->view.doc, | ||
5495 | d->view.hoverLink ? d->view.hoverLink->linkId : 0, | ||
5496 | width_Rect(bounds) - 2 * pad); | ||
5497 | const iInt2 infoSize = size_LinkInfo(d->linkInfo); | ||
5498 | iInt2 infoPos = add_I2(bottomLeft_Rect(bounds), init_I2(pad, -infoSize.y - pad)); | ||
5499 | if (d->view.hoverLink) { | ||
5500 | const iRect runRect = runRect_DocumentView_(&d->view, d->view.hoverLink); | ||
5501 | d->linkInfo->isAltPos = | ||
5502 | (bottom_Rect(runRect) >= infoPos.y - lineHeight_Text(paragraph_FontId)); | ||
5503 | } | ||
5504 | if (d->linkInfo->isAltPos) { | ||
5505 | infoPos.y = top_Rect(bounds) + pad; | ||
5506 | } | ||
5507 | draw_LinkInfo(d->linkInfo, infoPos); | ||
5508 | } | ||
5509 | /* Full-sized download indicator. */ | ||
5512 | if (d->flags & drawDownloadCounter_DocumentWidgetFlag && isRequestOngoing_DocumentWidget(d)) { | 5510 | if (d->flags & drawDownloadCounter_DocumentWidgetFlag && isRequestOngoing_DocumentWidget(d)) { |
5513 | const int font = uiLabelLarge_FontId; | 5511 | const int font = uiLabelLarge_FontId; |
5514 | const iInt2 sevenSegWidth = measureRange_Text(font, range_CStr("\U0001fbf0")).bounds.size; | 5512 | const iInt2 sevenSegWidth = measureRange_Text(font, range_CStr("\U0001fbf0")).bounds.size; |
@@ -5518,62 +5516,26 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
5518 | tmQuote_ColorId, tmQuoteIcon_ColorId, | 5516 | tmQuote_ColorId, tmQuoteIcon_ColorId, |
5519 | bodySize_GmRequest(d->request)); | 5517 | bodySize_GmRequest(d->request)); |
5520 | } | 5518 | } |
5521 | /* Alt text. */ | ||
5522 | const float altTextOpacity = value_Anim(&d->altTextOpacity) * 6 - 5; | ||
5523 | if (d->hoverAltPre && altTextOpacity > 0) { | ||
5524 | const iGmPreMeta *meta = preMeta_GmDocument(d->doc, preId_GmRun(d->hoverAltPre)); | ||
5525 | if (meta->flags & topLeft_GmPreMetaFlag && ~meta->flags & decoration_GmRunFlag && | ||
5526 | !isEmpty_Range(&meta->altText)) { | ||
5527 | const int margin = 3 * gap_UI / 2; | ||
5528 | const int altFont = uiLabel_FontId; | ||
5529 | const int wrap = docBounds.size.x - 2 * margin; | ||
5530 | iInt2 pos = addY_I2(add_I2(docBounds.pos, meta->pixelRect.pos), | ||
5531 | viewPos_DocumentWidget_(d)); | ||
5532 | const iInt2 textSize = measureWrapRange_Text(altFont, wrap, meta->altText).bounds.size; | ||
5533 | pos.y -= textSize.y + gap_UI; | ||
5534 | pos.y = iMax(pos.y, top_Rect(bounds)); | ||
5535 | const iRect altRect = { pos, init_I2(docBounds.size.x, textSize.y) }; | ||
5536 | ctx.paint.alpha = altTextOpacity * 255; | ||
5537 | if (altTextOpacity < 1) { | ||
5538 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); | ||
5539 | } | ||
5540 | fillRect_Paint(&ctx.paint, altRect, tmBackgroundAltText_ColorId); | ||
5541 | drawRect_Paint(&ctx.paint, altRect, tmFrameAltText_ColorId); | ||
5542 | setOpacity_Text(altTextOpacity); | ||
5543 | drawWrapRange_Text(altFont, addX_I2(pos, margin), wrap, | ||
5544 | tmQuote_ColorId, meta->altText); | ||
5545 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | ||
5546 | setOpacity_Text(1.0f); | ||
5547 | } | ||
5548 | } | ||
5549 | /* Pinch zoom indicator. */ | 5519 | /* Pinch zoom indicator. */ |
5550 | if (d->flags & pinchZoom_DocumentWidgetFlag) { | 5520 | if (d->flags & pinchZoom_DocumentWidgetFlag) { |
5551 | const int font = uiLabelLargeBold_FontId; | 5521 | const int font = uiLabelLargeBold_FontId; |
5552 | const int height = lineHeight_Text(font) * 2; | 5522 | const int height = lineHeight_Text(font) * 2; |
5553 | const iInt2 size = init_I2(height * 2, height); | 5523 | const iInt2 size = init_I2(height * 2, height); |
5554 | const iRect rect = { sub_I2(mid_Rect(bounds), divi_I2(size, 2)), size }; | 5524 | const iRect rect = { sub_I2(mid_Rect(bounds), divi_I2(size, 2)), size }; |
5555 | fillRect_Paint(&ctx.paint, rect, d->pinchZoomPosted == 100 ? uiTextCaution_ColorId : uiTextAction_ColorId); | 5525 | fillRect_Paint(&p, rect, d->pinchZoomPosted == 100 ? uiTextCaution_ColorId : uiTextAction_ColorId); |
5556 | drawCentered_Text(font, bounds, iFalse, uiBackground_ColorId, "%d %%", | 5526 | drawCentered_Text(font, bounds, iFalse, uiBackground_ColorId, "%d %%", |
5557 | d->pinchZoomPosted); | 5527 | d->pinchZoomPosted); |
5558 | } | 5528 | } |
5559 | /* Touch selection indicator. */ | 5529 | /* Dimming during swipe animation. */ |
5560 | if (isTouchSelecting) { | ||
5561 | iRect rect = { topLeft_Rect(bounds), | ||
5562 | init_I2(width_Rect(bounds), lineHeight_Text(uiLabelBold_FontId)) }; | ||
5563 | fillRect_Paint(&ctx.paint, rect, uiTextAction_ColorId); | ||
5564 | const iRangecc mark = selectMark_DocumentWidget_(d); | ||
5565 | drawCentered_Text(uiLabelBold_FontId, rect, iFalse, uiBackground_ColorId, "%zu bytes selected", | ||
5566 | size_Range(&mark)); | ||
5567 | } | ||
5568 | if (w->offsetRef) { | 5530 | if (w->offsetRef) { |
5569 | const int offX = visualOffsetByReference_Widget(w); | 5531 | const int offX = visualOffsetByReference_Widget(w); |
5570 | if (offX) { | 5532 | if (offX) { |
5571 | setClip_Paint(&ctx.paint, clipBounds); | 5533 | setClip_Paint(&p, clipBounds); |
5572 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); | 5534 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); |
5573 | ctx.paint.alpha = iAbs(offX) / (float) get_Window()->size.x * 300; | 5535 | p.alpha = iAbs(offX) / (float) get_Window()->size.x * 300; |
5574 | fillRect_Paint(&ctx.paint, bounds, backgroundFadeColor_Widget()); | 5536 | fillRect_Paint(&p, bounds, backgroundFadeColor_Widget()); |
5575 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | 5537 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); |
5576 | unsetClip_Paint(&ctx.paint); | 5538 | unsetClip_Paint(&p); |
5577 | } | 5539 | } |
5578 | else { | 5540 | else { |
5579 | /* TODO: Should have a better place to do this; drawing is supposed to be immutable. */ | 5541 | /* TODO: Should have a better place to do this; drawing is supposed to be immutable. */ |
@@ -5582,11 +5544,11 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
5582 | mut->flags &= ~refChildrenOffset_WidgetFlag; | 5544 | mut->flags &= ~refChildrenOffset_WidgetFlag; |
5583 | } | 5545 | } |
5584 | } | 5546 | } |
5585 | // drawRect_Paint(&ctx.paint, docBounds, red_ColorId); | 5547 | // drawRect_Paint(&p, docBounds, red_ColorId); |
5586 | if (deviceType_App() == phone_AppDeviceType) { | 5548 | if (deviceType_App() == phone_AppDeviceType) { |
5587 | /* The phone toolbar uses the palette of the active tab, but there may be other | 5549 | /* The phone toolbar uses the palette of the active tab, but there may be other |
5588 | documents drawn before the toolbar, causing the colors to be incorrect. */ | 5550 | documents drawn before the toolbar, causing the colors to be incorrect. */ |
5589 | makePaletteGlobal_GmDocument(document_App()->doc); | 5551 | makePaletteGlobal_GmDocument(document_App()->view.doc); |
5590 | } | 5552 | } |
5591 | } | 5553 | } |
5592 | 5554 | ||
@@ -5601,7 +5563,7 @@ const iString *url_DocumentWidget(const iDocumentWidget *d) { | |||
5601 | } | 5563 | } |
5602 | 5564 | ||
5603 | const iGmDocument *document_DocumentWidget(const iDocumentWidget *d) { | 5565 | const iGmDocument *document_DocumentWidget(const iDocumentWidget *d) { |
5604 | return d->doc; | 5566 | return d->view.doc; |
5605 | } | 5567 | } |
5606 | 5568 | ||
5607 | const iBlock *sourceContent_DocumentWidget(const iDocumentWidget *d) { | 5569 | const iBlock *sourceContent_DocumentWidget(const iDocumentWidget *d) { |
@@ -5609,20 +5571,20 @@ const iBlock *sourceContent_DocumentWidget(const iDocumentWidget *d) { | |||
5609 | } | 5571 | } |
5610 | 5572 | ||
5611 | int documentWidth_DocumentWidget(const iDocumentWidget *d) { | 5573 | int documentWidth_DocumentWidget(const iDocumentWidget *d) { |
5612 | return documentWidth_DocumentWidget_(d); | 5574 | return documentWidth_DocumentView_(&d->view); |
5613 | } | 5575 | } |
5614 | 5576 | ||
5615 | const iString *feedTitle_DocumentWidget(const iDocumentWidget *d) { | 5577 | const iString *feedTitle_DocumentWidget(const iDocumentWidget *d) { |
5616 | if (!isEmpty_String(title_GmDocument(d->doc))) { | 5578 | if (!isEmpty_String(title_GmDocument(d->view.doc))) { |
5617 | return title_GmDocument(d->doc); | 5579 | return title_GmDocument(d->view.doc); |
5618 | } | 5580 | } |
5619 | return bookmarkTitle_DocumentWidget(d); | 5581 | return bookmarkTitle_DocumentWidget(d); |
5620 | } | 5582 | } |
5621 | 5583 | ||
5622 | const iString *bookmarkTitle_DocumentWidget(const iDocumentWidget *d) { | 5584 | const iString *bookmarkTitle_DocumentWidget(const iDocumentWidget *d) { |
5623 | iStringArray *title = iClob(new_StringArray()); | 5585 | iStringArray *title = iClob(new_StringArray()); |
5624 | if (!isEmpty_String(title_GmDocument(d->doc))) { | 5586 | if (!isEmpty_String(title_GmDocument(d->view.doc))) { |
5625 | pushBack_StringArray(title, title_GmDocument(d->doc)); | 5587 | pushBack_StringArray(title, title_GmDocument(d->view.doc)); |
5626 | } | 5588 | } |
5627 | if (!isEmpty_String(d->titleUser)) { | 5589 | if (!isEmpty_String(d->titleUser)) { |
5628 | pushBack_StringArray(title, d->titleUser); | 5590 | pushBack_StringArray(title, d->titleUser); |
@@ -5682,7 +5644,7 @@ void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, cons | |||
5682 | iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { | 5644 | iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { |
5683 | iDocumentWidget *d = new_DocumentWidget(); | 5645 | iDocumentWidget *d = new_DocumentWidget(); |
5684 | delete_History(d->mod.history); | 5646 | delete_History(d->mod.history); |
5685 | d->initNormScrollY = normScrollPos_DocumentWidget_(d); | 5647 | d->initNormScrollY = normScrollPos_DocumentView_(&d->view); |
5686 | d->mod.history = copy_History(orig->mod.history); | 5648 | d->mod.history = copy_History(orig->mod.history); |
5687 | setUrlFlags_DocumentWidget(d, orig->mod.url, useCachedContentIfAvailable_DocumentWidgetSetUrlFlag); | 5649 | setUrlFlags_DocumentWidget(d, orig->mod.url, useCachedContentIfAvailable_DocumentWidgetSetUrlFlag); |
5688 | return d; | 5650 | return d; |
@@ -5733,11 +5695,12 @@ void takeRequest_DocumentWidget(iDocumentWidget *d, iGmRequest *finishedRequest) | |||
5733 | } | 5695 | } |
5734 | 5696 | ||
5735 | void updateSize_DocumentWidget(iDocumentWidget *d) { | 5697 | void updateSize_DocumentWidget(iDocumentWidget *d) { |
5736 | updateDocumentWidthRetainingScrollPosition_DocumentWidget_(d, iFalse); | 5698 | iDocumentView *view = &d->view; |
5737 | resetWideRuns_DocumentWidget_(d); | 5699 | updateDocumentWidthRetainingScrollPosition_DocumentView_(view, iFalse); |
5738 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; | 5700 | resetWideRuns_DocumentView_(view); |
5739 | updateVisible_DocumentWidget_(d); | 5701 | view->drawBufs->flags |= updateSideBuf_DrawBufsFlag; |
5740 | setWidth_Banner(d->banner, documentWidth_DocumentWidget(d)); | 5702 | updateVisible_DocumentView_(view); |
5703 | setWidth_Banner(d->banner, documentWidth_DocumentView_(view)); | ||
5741 | invalidate_DocumentWidget_(d); | 5704 | invalidate_DocumentWidget_(d); |
5742 | arrange_Widget(d->footerButtons); | 5705 | arrange_Widget(d->footerButtons); |
5743 | } | 5706 | } |