diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-26 22:00:14 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-26 22:00:14 +0300 |
commit | 6d8d188237324fc1379859251ca66f1ba4247589 (patch) | |
tree | 1c7f95edf7ad96bf80c4586271ce0e869c989147 /src/ui/documentwidget.c | |
parent | be6438af11dec5ee10b68fe315b3bc74d2877f8e (diff) |
Finding text in the document
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r-- | src/ui/documentwidget.c | 103 |
1 files changed, 93 insertions, 10 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 5df2b1b9..f7bfa39f 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -32,6 +32,7 @@ struct Impl_DocumentWidget { | |||
32 | iGmRequest *request; | 32 | iGmRequest *request; |
33 | iAtomicInt isSourcePending; /* request has new content, need to parse it */ | 33 | iAtomicInt isSourcePending; /* request has new content, need to parse it */ |
34 | iGmDocument *doc; | 34 | iGmDocument *doc; |
35 | iRangecc foundMark; | ||
35 | int pageMargin; | 36 | int pageMargin; |
36 | int scrollY; | 37 | int scrollY; |
37 | iPtrArray visibleLinks; | 38 | iPtrArray visibleLinks; |
@@ -47,14 +48,15 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
47 | iWidget *w = as_Widget(d); | 48 | iWidget *w = as_Widget(d); |
48 | init_Widget(w); | 49 | init_Widget(w); |
49 | setId_Widget(w, "document"); | 50 | setId_Widget(w, "document"); |
50 | d->state = blank_DocumentState; | 51 | d->state = blank_DocumentState; |
51 | d->url = new_String(); | 52 | d->url = new_String(); |
52 | d->request = NULL; | 53 | d->request = NULL; |
53 | d->isSourcePending = iFalse; | 54 | d->isSourcePending = iFalse; |
54 | d->doc = new_GmDocument(); | 55 | d->doc = new_GmDocument(); |
55 | d->pageMargin = 5; | 56 | d->foundMark = iNullRange; |
56 | d->scrollY = 0; | 57 | d->pageMargin = 5; |
57 | d->hoverLink = NULL; | 58 | d->scrollY = 0; |
59 | d->hoverLink = NULL; | ||
58 | init_PtrArray(&d->visibleLinks); | 60 | init_PtrArray(&d->visibleLinks); |
59 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | 61 | init_Click(&d->click, d, SDL_BUTTON_LEFT); |
60 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); | 62 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); |
@@ -150,10 +152,11 @@ static void updateSource_DocumentWidget_(iDocumentWidget *d) { | |||
150 | /* TODO: Do this in the background. However, that requires a text metrics calculator | 152 | /* TODO: Do this in the background. However, that requires a text metrics calculator |
151 | that does not try to cache the glyph bitmaps. */ | 153 | that does not try to cache the glyph bitmaps. */ |
152 | if (status_GmRequest(d->request) != input_GmStatusCode && | 154 | if (status_GmRequest(d->request) != input_GmStatusCode && |
153 | status_GmRequest(d->request) != sensitiveInput_GmStatusCode) { | 155 | status_GmRequest(d->request) != sensitiveInput_GmStatusCode) { |
154 | iString str; | 156 | iString str; |
155 | initBlock_String(&str, body_GmRequest(d->request)); | 157 | initBlock_String(&str, body_GmRequest(d->request)); |
156 | setSource_GmDocument(d->doc, &str, documentWidth_DocumentWidget_(d)); | 158 | setSource_GmDocument(d->doc, &str, documentWidth_DocumentWidget_(d)); |
159 | d->foundMark = iNullRange; | ||
157 | updateWindowTitle_DocumentWidget_(d); | 160 | updateWindowTitle_DocumentWidget_(d); |
158 | updateVisible_DocumentWidget_(d); | 161 | updateVisible_DocumentWidget_(d); |
159 | refresh_Widget(as_Widget(d)); | 162 | refresh_Widget(as_Widget(d)); |
@@ -211,6 +214,11 @@ static void scroll_DocumentWidget_(iDocumentWidget *d, int offset) { | |||
211 | refresh_Widget(as_Widget(d)); | 214 | refresh_Widget(as_Widget(d)); |
212 | } | 215 | } |
213 | 216 | ||
217 | static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY) { | ||
218 | d->scrollY = documentY - documentBounds_DocumentWidget_(d).size.y / 2; | ||
219 | scroll_DocumentWidget_(d, 0); /* clamp it */ | ||
220 | } | ||
221 | |||
214 | static iRangecc dirPath_(iRangecc path) { | 222 | static iRangecc dirPath_(iRangecc path) { |
215 | const size_t pos = lastIndexOfCStr_Rangecc(&path, "/"); | 223 | const size_t pos = lastIndexOfCStr_Rangecc(&path, "/"); |
216 | if (pos == iInvalidPos) return path; | 224 | if (pos == iInvalidPos) return path; |
@@ -285,6 +293,7 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
285 | default: | 293 | default: |
286 | break; | 294 | break; |
287 | } | 295 | } |
296 | d->foundMark = iNullRange; | ||
288 | setSource_GmDocument(d->doc, src, documentWidth_DocumentWidget_(d)); | 297 | setSource_GmDocument(d->doc, src, documentWidth_DocumentWidget_(d)); |
289 | updateWindowTitle_DocumentWidget_(d); | 298 | updateWindowTitle_DocumentWidget_(d); |
290 | updateVisible_DocumentWidget_(d); | 299 | updateVisible_DocumentWidget_(d); |
@@ -408,6 +417,34 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
408 | d, arg_Command(command_UserEvent(ev)) * height_Rect(documentBounds_DocumentWidget_(d))); | 417 | d, arg_Command(command_UserEvent(ev)) * height_Rect(documentBounds_DocumentWidget_(d))); |
409 | return iTrue; | 418 | return iTrue; |
410 | } | 419 | } |
420 | else if (isCommand_UserEvent(ev, "find.next")) { | ||
421 | iInputWidget *find = findWidget_App("find.input"); | ||
422 | if (isEmpty_String(text_InputWidget(find))) { | ||
423 | d->foundMark = iNullRange; | ||
424 | } | ||
425 | else { | ||
426 | const iBool wrap = d->foundMark.start != NULL; | ||
427 | d->foundMark = findText_GmDocument(d->doc, text_InputWidget(find), d->foundMark.end); | ||
428 | if (!d->foundMark.start && wrap) { | ||
429 | d->foundMark = findText_GmDocument(d->doc, text_InputWidget(find), 0); | ||
430 | } | ||
431 | if (d->foundMark.start) { | ||
432 | iGmRun *run = findRunCStr_GmDocument(d->doc, d->foundMark.start); | ||
433 | if (run) { | ||
434 | scrollTo_DocumentWidget_(d, mid_Rect(run->bounds).y); | ||
435 | } | ||
436 | } | ||
437 | } | ||
438 | refresh_Widget(w); | ||
439 | return iTrue; | ||
440 | } | ||
441 | else if (isCommand_UserEvent(ev, "find.clearmark")) { | ||
442 | if (d->foundMark.start) { | ||
443 | d->foundMark = iNullRange; | ||
444 | refresh_Widget(w); | ||
445 | } | ||
446 | return iTrue; | ||
447 | } | ||
411 | if (ev->type == SDL_KEYDOWN) { | 448 | if (ev->type == SDL_KEYDOWN) { |
412 | const int mods = keyMods_Sym(ev->key.keysym.mod); | 449 | const int mods = keyMods_Sym(ev->key.keysym.mod); |
413 | const int key = ev->key.keysym.sym; | 450 | const int key = ev->key.keysym.sym; |
@@ -493,6 +530,7 @@ struct Impl_DrawContext { | |||
493 | const iDocumentWidget *widget; | 530 | const iDocumentWidget *widget; |
494 | iRect bounds; | 531 | iRect bounds; |
495 | iPaint paint; | 532 | iPaint paint; |
533 | int insideMark; | ||
496 | }; | 534 | }; |
497 | 535 | ||
498 | static void drawRun_DrawContext_(void *context, const iGmRun *run) { | 536 | static void drawRun_DrawContext_(void *context, const iGmRun *run) { |
@@ -501,10 +539,55 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
501 | /* TODO: making a copy is unnecessary; the text routines should accept Rangecc */ | 539 | /* TODO: making a copy is unnecessary; the text routines should accept Rangecc */ |
502 | initRange_String(&text, run->text); | 540 | initRange_String(&text, run->text); |
503 | iInt2 origin = addY_I2(d->bounds.pos, -d->widget->scrollY); | 541 | iInt2 origin = addY_I2(d->bounds.pos, -d->widget->scrollY); |
504 | drawString_Text(run->font, add_I2(run->bounds.pos, origin), run->color, &text); | ||
505 | if (run == d->widget->hoverLink) { | 542 | if (run == d->widget->hoverLink) { |
506 | drawRect_Paint(&d->paint, moved_Rect(run->bounds, origin), orange_ColorId); | 543 | const char *desc = ""; |
544 | const iString *url = linkUrl_GmDocument(d->widget->doc, d->widget->hoverLink->linkId); | ||
545 | if (indexOfCStr_String(url, "://") == iInvalidPos) { | ||
546 | url = d->widget->url; | ||
547 | } | ||
548 | iUrl parts; | ||
549 | init_Url(&parts, url); | ||
550 | desc = cstrFormat_String("\u2192 %s", cstr_String(collect_String(newRange_String(parts.protocol)))); | ||
551 | int descWidth = measure_Text(default_FontId, desc).x + gap_UI; | ||
552 | iRect linkRect = expanded_Rect(moved_Rect(run->bounds, origin), init_I2(gap_UI, 0)); | ||
553 | linkRect.size.x += descWidth; | ||
554 | fillRect_Paint(&d->paint, | ||
555 | linkRect, | ||
556 | teal_ColorId); | ||
557 | drawAlign_Text(default_FontId, | ||
558 | addX_I2(topRight_Rect(linkRect), -gap_UI), | ||
559 | cyan_ColorId, | ||
560 | right_Alignment, | ||
561 | "%s", | ||
562 | desc); | ||
563 | } | ||
564 | const iInt2 visPos = add_I2(run->bounds.pos, origin); | ||
565 | /* Found text marker. */ | ||
566 | if ((!d->insideMark && contains_Range(&run->text, d->widget->foundMark.start)) || | ||
567 | d->insideMark) { | ||
568 | int x = 0; | ||
569 | if (!d->insideMark) { | ||
570 | x = advanceRange_Text(run->font, (iRangecc){ run->text.start, | ||
571 | d->widget->foundMark.start }).x; | ||
572 | } | ||
573 | int w = width_Rect(run->bounds) - x; | ||
574 | if (contains_Range(&run->text, d->widget->foundMark.end) || | ||
575 | run->text.end == d->widget->foundMark.end) { | ||
576 | w = advanceRange_Text(run->font, | ||
577 | !d->insideMark | ||
578 | ? d->widget->foundMark | ||
579 | : (iRangecc){ run->text.start, d->widget->foundMark.end }) | ||
580 | .x; | ||
581 | d->insideMark = iFalse; | ||
582 | } | ||
583 | else { | ||
584 | d->insideMark = iTrue; /* at least until the next run */ | ||
585 | } | ||
586 | fillRect_Paint(&d->paint, | ||
587 | (iRect){ addX_I2(visPos, x), init_I2(w, height_Rect(run->bounds)) }, | ||
588 | teal_ColorId); | ||
507 | } | 589 | } |
590 | drawString_Text(run->font, visPos, run->color, &text); | ||
508 | deinit_String(&text); | 591 | deinit_String(&text); |
509 | } | 592 | } |
510 | 593 | ||