From 7a69d53ba72f9e5581c781dd4c54dd3a3e6b89b4 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 4 Aug 2020 11:24:13 +0300 Subject: Remember scroll positions on visited pages --- src/app.c | 6 ++++-- src/app.h | 4 ++-- src/gmutil.h | 20 +++++++++++++++++--- src/history.c | 15 ++++++++++----- src/history.h | 1 + src/ui/documentwidget.c | 42 ++++++++++++++++++++++++++++++++---------- src/ui/documentwidget.h | 1 + src/ui/text.c | 8 +++++++- 8 files changed, 74 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/app.c b/src/app.c index 21028edc..5ac211f9 100644 --- a/src/app.c +++ b/src/app.c @@ -318,7 +318,7 @@ iGmCerts *certs_App(void) { return app_.certs; } -const iHistory *history_App(void) { +iHistory *history_App(void) { return app_.history; } @@ -346,7 +346,9 @@ iBool handleCommand_App(const char *cmd) { } } print_History(d->history); - setUrl_DocumentWidget(findChild_Widget(root, "document"), url); + iDocumentWidget *doc = findChild_Widget(root, "document"); + setUrl_DocumentWidget(doc, url); + setInitialScroll_DocumentWidget(doc, argLabel_Command(cmd, "scroll") * gap_UI); } else if (equal_Command(cmd, "document.request.cancelled")) { /* TODO: How should cancelled requests be treated in the history? */ diff --git a/src/app.h b/src/app.h index 23e30ea5..5cb68920 100644 --- a/src/app.h +++ b/src/app.h @@ -26,8 +26,8 @@ void processEvents_App (enum iAppEventMode mode); iBool handleCommand_App (const char *cmd); void refresh_App (void); -iGmCerts * certs_App (void); -const iHistory *history_App (void); +iGmCerts * certs_App (void); +iHistory * history_App (void); iAny * findWidget_App (const char *id); void addTicker_App (void (*ticker)(iAny *), iAny *context); diff --git a/src/gmutil.h b/src/gmutil.h index 6c1b01ad..d0ed4581 100644 --- a/src/gmutil.h +++ b/src/gmutil.h @@ -3,18 +3,26 @@ #include #include -iDeclareType(GmError) -iDeclareType(Url) +iDeclareType(GmError) iDeclareType(Url) /* Response status codes. */ enum iGmStatusCode { - clientSide_GmStatusCode = -100, /* clientside status codes */ + /* clientside status codes */ + clientSide_GmStatusCode = -100, invalidRedirect_GmStatusCode, invalidHeader_GmStatusCode, unsupportedMimeType_GmStatusCode, failedToOpenFile_GmStatusCode, unknownStatusCode_GmStatusCode, none_GmStatusCode = 0, + /* general status code categories */ + categoryInput_GmStatusCode = 1, + categorySuccess_GmStatusCode = 2, + categoryRedirect_GmStatusCode = 3, + categoryTemporaryFailure_GmStatusCode = 4, + categoryPermanentFailure_GmStatusCode = 5, + categoryClientCertificate_GmStatus = 6, + /* detailed status codes */ input_GmStatusCode = 10, sensitiveInput_GmStatusCode = 11, success_GmStatusCode = 20, @@ -35,6 +43,12 @@ enum iGmStatusCode { certificateNotValid_GmStatusCode = 62, }; +iLocalDef enum iGmStatusCode category_GmStatusCode(enum iGmStatusCode code) { + if (code < 0) return 0; + if (code < 10) return code; + return code / 10; +} + struct Impl_GmError { iChar icon; const char *title; diff --git a/src/history.c b/src/history.c index edffd485..ca80ad3f 100644 --- a/src/history.c +++ b/src/history.c @@ -11,6 +11,7 @@ static const size_t maxAgeVisited_History_ = 3600 * 24 * 30; /* one month */ void init_HistoryItem(iHistoryItem *d) { initCurrent_Time(&d->when); init_String(&d->url); + d->scrollY = 0; } void deinit_HistoryItem(iHistoryItem *d) { @@ -52,13 +53,14 @@ static void writeItems_(const iArray *items, iFile *f) { iDate date; init_Date(&date, &item->when); format_String(line, - "%04d-%02d-%02dT%02d:%02d:%02d %s\n", + "%04d-%02d-%02dT%02d:%02d:%02d %04x %s\n", date.year, date.month, date.day, date.hour, date.minute, date.second, + item->scrollY, cstr_String(&item->url)); writeData_File(f, cstr_String(line), size_String(line)); } @@ -84,18 +86,19 @@ static void loadItems_(iArray *items, iFile *f, double maxAge) { iTime now; initCurrent_Time(&now); while (nextSplit_Rangecc(&src, "\n", &line)) { - int y, m, D, H, M, S; - sscanf(line.start, "%04d-%02d-%02dT%02d:%02d:%02d", &y, &m, &D, &H, &M, &S); + int y, m, D, H, M, S, scroll = 0; + sscanf(line.start, "%04d-%02d-%02dT%02d:%02d:%02d %04x", &y, &m, &D, &H, &M, &S, &scroll); if (!y) break; iHistoryItem item; init_HistoryItem(&item); + item.scrollY = scroll; init_Time( &item.when, &(iDate){ .year = y, .month = m, .day = D, .hour = H, .minute = M, .second = S }); if (maxAge > 0.0 && secondsSince_Time(&now, &item.when) > maxAge) { continue; /* Too old. */ } - initRange_String(&item.url, (iRangecc){ line.start + 20, line.end }); + initRange_String(&item.url, (iRangecc){ line.start + 25, line.end }); pushBack_Array(items, &item); } } @@ -194,7 +197,9 @@ void addUrl_History(iHistory *d, const iString *url ){ iBool goBack_History(iHistory *d) { if (d->stackPos < size_Array(&d->stack) - 1) { d->stackPos++; - postCommandf_App("open history:1 url:%s", cstr_String(url_History(d, d->stackPos))); + postCommandf_App("open history:1 scroll:%d url:%s", + item_History(d)->scrollY, + cstr_String(url_History(d, d->stackPos))); return iTrue; } return iFalse; diff --git a/src/history.h b/src/history.h index 4074b1aa..149e6b82 100644 --- a/src/history.h +++ b/src/history.h @@ -10,6 +10,7 @@ iDeclareTypeConstruction(HistoryItem) struct Impl_HistoryItem { iTime when; iString url; + int scrollY; /* unit is gap_UI */ }; iDeclareType(History) diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 7450ba12..dd119e3e 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -5,6 +5,7 @@ #include "paint.h" #include "command.h" #include "util.h" +#include "history.h" #include "app.h" #include "../gmdocument.h" #include "../gmrequest.h" @@ -91,6 +92,7 @@ struct Impl_DocumentWidget { iPtrArray visibleLinks; const iGmRun *hoverLink; iClick click; + int initialScrollY; iScrollWidget *scroll; iWidget *menu; SDL_Cursor *arrowCursor; @@ -242,6 +244,12 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { clear_PtrArray(&d->visibleLinks); render_GmDocument(d->doc, visRange, addVisibleLink_DocumentWidget_, d); updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window())); + /* Remember scroll positions of recently visited pages. */ { + iHistoryItem *it = item_History(history_App()); + if (it) { + it->scrollY = d->scrollY / gap_UI; + } + } } static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { @@ -300,9 +308,14 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode break; } setSource_DocumentWidget_(d, src); + d->scrollY = 0; + d->state = ready_DocumentState; } static void updateSource_DocumentWidget_(iDocumentWidget *d) { + if (d->state == ready_DocumentState) { + return; + } /* TODO: Do this in the background. However, that requires a text metrics calculator that does not try to cache the glyph bitmaps. */ const enum iGmStatusCode statusCode = status_GmRequest(d->request); @@ -384,6 +397,10 @@ void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) { } } +void setInitialScroll_DocumentWidget (iDocumentWidget *d, int scrollY) { + d->initialScrollY = scrollY; +} + iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { return d->state == fetching_DocumentState || d->state == receivedPartialResponse_DocumentState; } @@ -437,11 +454,14 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { return; } enum iGmStatusCode statusCode = status_GmRequest(d->request); + if (statusCode == none_GmStatusCode) { + return; + } if (d->state == fetching_DocumentState) { d->state = receivedPartialResponse_DocumentState; updateTrust_DocumentWidget_(d); - switch (statusCode / 10) { - case 1: /* input required */ { + switch (category_GmStatusCode(statusCode)) { + case categoryInput_GmStatusCode: { iUrl parts; init_Url(&parts, d->url); printf("%s\n", cstr_String(meta_GmRequest(d->request))); @@ -460,16 +480,17 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { statusCode == sensitiveInput_GmStatusCode); break; } - case 2: /* success */ - d->scrollY = 0; + case categorySuccess_GmStatusCode: + d->scrollY = d->initialScrollY; reset_GmDocument(d->doc); /* new content incoming */ updateSource_DocumentWidget_(d); break; - case 3: /* redirect */ + case categoryRedirect_GmStatusCode: if (isEmpty_String(meta_GmRequest(d->request))) { showErrorPage_DocumentWidget_(d, invalidRedirect_GmStatusCode); } else { + /* TODO: only accept redirects that use gemini protocol */ postCommandf_App( "open redirect:1 url:%s", cstr_String(absoluteUrl_String(d->url, meta_GmRequest(d->request)))); @@ -480,18 +501,20 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { if (isDefined_GmError(statusCode)) { showErrorPage_DocumentWidget_(d, statusCode); } - else if (statusCode / 10 == 4) { + else if (category_GmStatusCode(statusCode) == + categoryTemporaryFailure_GmStatusCode) { showErrorPage_DocumentWidget_(d, temporaryFailure_GmStatusCode); } - else if (statusCode / 10 == 5) { + else if (category_GmStatusCode(statusCode) == + categoryPermanentFailure_GmStatusCode) { showErrorPage_DocumentWidget_(d, permanentFailure_GmStatusCode); } break; } } else if (d->state == receivedPartialResponse_DocumentState) { - switch (statusCode) { - case success_GmStatusCode: + switch (category_GmStatusCode(statusCode)) { + case categorySuccess_GmStatusCode: /* More content available. */ updateSource_DocumentWidget_(d); break; @@ -649,7 +672,6 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e } else if (isCommand_Widget(w, ev, "document.request.finished") && pointerLabel_Command(command_UserEvent(ev), "request") == d->request) { - iAssert(d->state == receivedPartialResponse_DocumentState); /* must already have been updated at least once */ checkResponse_DocumentWidget_(d); d->state = ready_DocumentState; iReleasePtr(&d->request); diff --git a/src/ui/documentwidget.h b/src/ui/documentwidget.h index dfd342af..6b0ea607 100644 --- a/src/ui/documentwidget.h +++ b/src/ui/documentwidget.h @@ -6,4 +6,5 @@ iDeclareWidgetClass(DocumentWidget) iDeclareObjectConstruction(DocumentWidget) void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); +void setInitialScroll_DocumentWidget (iDocumentWidget *, int scrollY); /* set after content received */ iBool isRequestOngoing_DocumentWidget (const iDocumentWidget *); diff --git a/src/ui/text.c b/src/ui/text.c index 50b3bbab..f562e642 100644 --- a/src/ui/text.c +++ b/src/ui/text.c @@ -356,6 +356,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe iChar prevCh = 0; for (const char *chPos = text.start; chPos != text.end; ) { iAssert(chPos < text.end); + const char *currentPos = chPos; if (*chPos == 0x1b) { /* ANSI escape. */ chPos++; @@ -394,7 +395,12 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe int x2 = x1 + glyph->rect[hoff].size.x; /* Out of the allotted space? */ if (xposLimit > 0 && x2 > xposLimit) { - *continueFrom_out = lastWordEnd; + if (lastWordEnd != text.start) { + *continueFrom_out = lastWordEnd; + } + else { + *continueFrom_out = currentPos; /* forced break */ + } break; } const SDL_Rect dst = { x1 + glyph->d[hoff].x, -- cgit v1.2.3