diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-25 16:50:30 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-25 16:50:30 +0300 |
commit | 327e2fd4229bf14ef61b0aeace9c8503b183f7e9 (patch) | |
tree | 616510056dc1c91a21fe8c45204110e59fdc94a6 /src/ui/documentwidget.c | |
parent | 37d93f50a1e4c2c1d95246e2110c7bafb5ff5c89 (diff) |
DocumentWidget: Smooth scrolling for keyboard
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r-- | src/ui/documentwidget.c | 95 |
1 files changed, 89 insertions, 6 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index a36fe761..5292d3f5 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -162,6 +162,8 @@ iDefineTypeConstruction(VisBuffer) | |||
162 | 162 | ||
163 | /*----------------------------------------------------------------------------------------------*/ | 163 | /*----------------------------------------------------------------------------------------------*/ |
164 | 164 | ||
165 | static const int smoothSpeed_DocumentWidget_ = 150; /* unit: gap_Text per second */ | ||
166 | |||
165 | enum iRequestState { | 167 | enum iRequestState { |
166 | blank_RequestState, | 168 | blank_RequestState, |
167 | fetching_RequestState, | 169 | fetching_RequestState, |
@@ -193,6 +195,10 @@ struct Impl_DocumentWidget { | |||
193 | iClick click; | 195 | iClick click; |
194 | float initNormScrollY; | 196 | float initNormScrollY; |
195 | int scrollY; | 197 | int scrollY; |
198 | int smoothScroll; | ||
199 | int smoothSpeed; | ||
200 | int lastSmoothOffset; | ||
201 | iBool keepScrolling; | ||
196 | iScrollWidget *scroll; | 202 | iScrollWidget *scroll; |
197 | iWidget * menu; | 203 | iWidget * menu; |
198 | iVisBuffer * visBuffer; | 204 | iVisBuffer * visBuffer; |
@@ -217,6 +223,10 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
217 | d->doc = new_GmDocument(); | 223 | d->doc = new_GmDocument(); |
218 | d->initNormScrollY = 0; | 224 | d->initNormScrollY = 0; |
219 | d->scrollY = 0; | 225 | d->scrollY = 0; |
226 | d->smoothScroll = 0; | ||
227 | d->smoothSpeed = 0; | ||
228 | d->lastSmoothOffset = 0; | ||
229 | d->keepScrolling = iFalse; | ||
220 | d->selecting = iFalse; | 230 | d->selecting = iFalse; |
221 | d->selectMark = iNullRange; | 231 | d->selectMark = iNullRange; |
222 | d->foundMark = iNullRange; | 232 | d->foundMark = iNullRange; |
@@ -254,6 +264,13 @@ void deinit_DocumentWidget(iDocumentWidget *d) { | |||
254 | deinit_Model(&d->mod); | 264 | deinit_Model(&d->mod); |
255 | } | 265 | } |
256 | 266 | ||
267 | static void resetSmoothScroll_DocumentWidget_(iDocumentWidget *d) { | ||
268 | d->smoothSpeed = 0; | ||
269 | d->smoothScroll = 0; | ||
270 | d->lastSmoothOffset = 0; | ||
271 | d->keepScrolling = iFalse; | ||
272 | } | ||
273 | |||
257 | static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { | 274 | static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { |
258 | const iWidget *w = constAs_Widget(d); | 275 | const iWidget *w = constAs_Widget(d); |
259 | const iRect bounds = bounds_Widget(w); | 276 | const iRect bounds = bounds_Widget(w); |
@@ -287,9 +304,11 @@ iLocalDef int documentToWindowY_DocumentWidget_(const iDocumentWidget *d, int do | |||
287 | return docY - d->scrollY + documentBounds_DocumentWidget_(d).pos.y; | 304 | return docY - d->scrollY + documentBounds_DocumentWidget_(d).pos.y; |
288 | } | 305 | } |
289 | 306 | ||
307 | #if 0 | ||
290 | iLocalDef int windowToDocumentY_DocumentWidget_(const iDocumentWidget *d, int localY) { | 308 | iLocalDef int windowToDocumentY_DocumentWidget_(const iDocumentWidget *d, int localY) { |
291 | return localY + d->scrollY - documentBounds_DocumentWidget_(d).pos.y; | 309 | return localY + d->scrollY - documentBounds_DocumentWidget_(d).pos.y; |
292 | } | 310 | } |
311 | #endif | ||
293 | 312 | ||
294 | static iInt2 documentPos_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { | 313 | static iInt2 documentPos_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { |
295 | return addY_I2(sub_I2(pos, topLeft_Rect(documentBounds_DocumentWidget_(d))), d->scrollY); | 314 | return addY_I2(sub_I2(pos, topLeft_Rect(documentBounds_DocumentWidget_(d))), d->scrollY); |
@@ -500,6 +519,7 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
500 | break; | 519 | break; |
501 | } | 520 | } |
502 | setSource_DocumentWidget_(d, src); | 521 | setSource_DocumentWidget_(d, src); |
522 | resetSmoothScroll_DocumentWidget_(d); | ||
503 | d->scrollY = 0; | 523 | d->scrollY = 0; |
504 | d->state = ready_RequestState; | 524 | d->state = ready_RequestState; |
505 | } | 525 | } |
@@ -743,6 +763,37 @@ static void scroll_DocumentWidget_(iDocumentWidget *d, int offset) { | |||
743 | refresh_Widget(as_Widget(d)); | 763 | refresh_Widget(as_Widget(d)); |
744 | } | 764 | } |
745 | 765 | ||
766 | static void doScroll_DocumentWidget_(iAny *ptr) { | ||
767 | iDocumentWidget *d = ptr; | ||
768 | if (!d->smoothScroll) return; /* was cancelled */ | ||
769 | const double elapsed = (double) elapsedSinceLastTicker_App() / 1000.0; | ||
770 | int delta = d->smoothSpeed * elapsed * iSign(d->smoothScroll); | ||
771 | if (iAbs(d->smoothScroll) <= iAbs(delta)) { | ||
772 | if (d->keepScrolling) { | ||
773 | d->smoothScroll += d->lastSmoothOffset; | ||
774 | } | ||
775 | else { | ||
776 | delta = d->smoothScroll; | ||
777 | } | ||
778 | } | ||
779 | scroll_DocumentWidget_(d, delta); | ||
780 | d->smoothScroll -= delta; | ||
781 | if (d->smoothScroll != 0) { | ||
782 | addTicker_App(doScroll_DocumentWidget_, d); | ||
783 | } | ||
784 | } | ||
785 | |||
786 | static void smoothScroll_DocumentWidget_(iDocumentWidget *d, int offset, int speed) { | ||
787 | if (speed == 0) { | ||
788 | scroll_DocumentWidget_(d, offset); | ||
789 | return; | ||
790 | } | ||
791 | d->smoothSpeed = speed; | ||
792 | d->smoothScroll += offset; | ||
793 | d->lastSmoothOffset = offset; | ||
794 | addTicker_App(doScroll_DocumentWidget_, d); | ||
795 | } | ||
796 | |||
746 | static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY, iBool centered) { | 797 | static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY, iBool centered) { |
747 | d->scrollY = documentY - (centered ? documentBounds_DocumentWidget_(d).size.y / 2 : | 798 | d->scrollY = documentY - (centered ? documentBounds_DocumentWidget_(d).size.y / 2 : |
748 | lineHeight_Text(paragraph_FontId)); | 799 | lineHeight_Text(paragraph_FontId)); |
@@ -780,6 +831,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
780 | } | 831 | } |
781 | case categorySuccess_GmStatusCode: | 832 | case categorySuccess_GmStatusCode: |
782 | d->scrollY = 0; | 833 | d->scrollY = 0; |
834 | resetSmoothScroll_DocumentWidget_(d); | ||
783 | reset_GmDocument(d->doc); /* new content incoming */ | 835 | reset_GmDocument(d->doc); /* new content incoming */ |
784 | updateDocument_DocumentWidget_(d, response_GmRequest(d->request)); | 836 | updateDocument_DocumentWidget_(d, response_GmRequest(d->request)); |
785 | break; | 837 | break; |
@@ -1076,6 +1128,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1076 | else if (equalWidget_Command(cmd, w, "document.request.finished") && | 1128 | else if (equalWidget_Command(cmd, w, "document.request.finished") && |
1077 | pointerLabel_Command(cmd, "request") == d->request) { | 1129 | pointerLabel_Command(cmd, "request") == d->request) { |
1078 | checkResponse_DocumentWidget_(d); | 1130 | checkResponse_DocumentWidget_(d); |
1131 | resetSmoothScroll_DocumentWidget_(d); | ||
1079 | d->scrollY = d->initNormScrollY * size_GmDocument(d->doc).y; | 1132 | d->scrollY = d->initNormScrollY * size_GmDocument(d->doc).y; |
1080 | d->state = ready_RequestState; | 1133 | d->state = ready_RequestState; |
1081 | /* The response may be cached. */ { | 1134 | /* The response may be cached. */ { |
@@ -1123,12 +1176,24 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1123 | } | 1176 | } |
1124 | else if (equalWidget_Command(cmd, w, "scroll.moved")) { | 1177 | else if (equalWidget_Command(cmd, w, "scroll.moved")) { |
1125 | d->scrollY = arg_Command(cmd); | 1178 | d->scrollY = arg_Command(cmd); |
1179 | resetSmoothScroll_DocumentWidget_(d); | ||
1126 | updateVisible_DocumentWidget_(d); | 1180 | updateVisible_DocumentWidget_(d); |
1127 | return iTrue; | 1181 | return iTrue; |
1128 | } | 1182 | } |
1129 | else if (equalWidget_Command(cmd, w, "scroll.page")) { | 1183 | else if (equalWidget_Command(cmd, w, "scroll.page")) { |
1130 | scroll_DocumentWidget_(d, | 1184 | if (argLabel_Command(cmd, "repeat")) { |
1131 | arg_Command(cmd) * height_Rect(documentBounds_DocumentWidget_(d))); | 1185 | if (!d->keepScrolling) { |
1186 | d->keepScrolling = iTrue; | ||
1187 | } | ||
1188 | else { | ||
1189 | return iTrue; | ||
1190 | } | ||
1191 | } | ||
1192 | smoothScroll_DocumentWidget_(d, | ||
1193 | arg_Command(cmd) * | ||
1194 | (0.5f * height_Rect(documentBounds_DocumentWidget_(d)) - | ||
1195 | 0 * lineHeight_Text(paragraph_FontId)), | ||
1196 | 15 * smoothSpeed_DocumentWidget_); | ||
1132 | return iTrue; | 1197 | return iTrue; |
1133 | } | 1198 | } |
1134 | else if (equal_Command(cmd, "document.goto") && document_App() == d) { | 1199 | else if (equal_Command(cmd, "document.goto") && document_App() == d) { |
@@ -1211,6 +1276,13 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1211 | refresh_Widget(w); | 1276 | refresh_Widget(w); |
1212 | } | 1277 | } |
1213 | break; | 1278 | break; |
1279 | case SDLK_PAGEUP: | ||
1280 | case SDLK_PAGEDOWN: | ||
1281 | case SDLK_SPACE: | ||
1282 | case SDLK_UP: | ||
1283 | case SDLK_DOWN: | ||
1284 | d->keepScrolling = iFalse; | ||
1285 | break; | ||
1214 | } | 1286 | } |
1215 | } | 1287 | } |
1216 | if (ev->type == SDL_KEYDOWN) { | 1288 | if (ev->type == SDL_KEYDOWN) { |
@@ -1241,12 +1313,14 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1241 | break; | 1313 | break; |
1242 | case SDLK_HOME: | 1314 | case SDLK_HOME: |
1243 | d->scrollY = 0; | 1315 | d->scrollY = 0; |
1316 | resetSmoothScroll_DocumentWidget_(d); | ||
1244 | scroll_DocumentWidget_(d, 0); | 1317 | scroll_DocumentWidget_(d, 0); |
1245 | updateVisible_DocumentWidget_(d); | 1318 | updateVisible_DocumentWidget_(d); |
1246 | refresh_Widget(w); | 1319 | refresh_Widget(w); |
1247 | return iTrue; | 1320 | return iTrue; |
1248 | case SDLK_END: | 1321 | case SDLK_END: |
1249 | d->scrollY = scrollMax_DocumentWidget_(d); | 1322 | d->scrollY = scrollMax_DocumentWidget_(d); |
1323 | resetSmoothScroll_DocumentWidget_(d); | ||
1250 | scroll_DocumentWidget_(d, 0); | 1324 | scroll_DocumentWidget_(d, 0); |
1251 | updateVisible_DocumentWidget_(d); | 1325 | updateVisible_DocumentWidget_(d); |
1252 | refresh_Widget(w); | 1326 | refresh_Widget(w); |
@@ -1254,8 +1328,16 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1254 | case SDLK_UP: | 1328 | case SDLK_UP: |
1255 | case SDLK_DOWN: | 1329 | case SDLK_DOWN: |
1256 | if (mods == 0) { | 1330 | if (mods == 0) { |
1257 | scroll_DocumentWidget_(d, 2 * lineHeight_Text(default_FontId) * | 1331 | if (ev->key.repeat) { |
1258 | (key == SDLK_UP ? -1 : 1)); | 1332 | if (!d->keepScrolling) { |
1333 | d->keepScrolling = iTrue; | ||
1334 | } | ||
1335 | else return iTrue; | ||
1336 | } | ||
1337 | smoothScroll_DocumentWidget_(d, | ||
1338 | 3 * lineHeight_Text(paragraph_FontId) * | ||
1339 | (key == SDLK_UP ? -1 : 1), | ||
1340 | gap_Text * smoothSpeed_DocumentWidget_); | ||
1259 | return iTrue; | 1341 | return iTrue; |
1260 | } | 1342 | } |
1261 | break; | 1343 | break; |
@@ -1264,8 +1346,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1264 | case SDLK_SPACE: | 1346 | case SDLK_SPACE: |
1265 | postCommand_Widget( | 1347 | postCommand_Widget( |
1266 | w, | 1348 | w, |
1267 | "scroll.page arg:%d", | 1349 | "scroll.page arg:%d repeat:%d", |
1268 | (key == SDLK_SPACE && mods & KMOD_SHIFT) || key == SDLK_PAGEUP ? -1 : +1); | 1350 | (key == SDLK_SPACE && mods & KMOD_SHIFT) || key == SDLK_PAGEUP ? -1 : +1, |
1351 | ev->key.repeat != 0); | ||
1269 | return iTrue; | 1352 | return iTrue; |
1270 | #if 1 | 1353 | #if 1 |
1271 | case SDLK_KP_1: { | 1354 | case SDLK_KP_1: { |