diff options
-rw-r--r-- | src/ui/documentwidget.c | 7 | ||||
-rw-r--r-- | src/ui/touch.c | 120 |
2 files changed, 88 insertions, 39 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 0a282f1b..9619c56e 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -2352,6 +2352,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
2352 | if (ev->wheel.which == 0) { /* Trackpad with precise scrolling w/inertia. */ | 2352 | if (ev->wheel.which == 0) { /* Trackpad with precise scrolling w/inertia. */ |
2353 | stop_Anim(&d->scrollY); | 2353 | stop_Anim(&d->scrollY); |
2354 | iInt2 wheel = mulf_I2(init_I2(ev->wheel.x, ev->wheel.y), get_Window()->pixelRatio); | 2354 | iInt2 wheel = mulf_I2(init_I2(ev->wheel.x, ev->wheel.y), get_Window()->pixelRatio); |
2355 | #if defined (iPlatformAppleMobile) | ||
2356 | wheel.x = -wheel.x; | ||
2357 | #else | ||
2355 | /* Only scroll on one axis at a time. */ | 2358 | /* Only scroll on one axis at a time. */ |
2356 | if (iAbs(wheel.x) > iAbs(wheel.y)) { | 2359 | if (iAbs(wheel.x) > iAbs(wheel.y)) { |
2357 | wheel.y = 0; | 2360 | wheel.y = 0; |
@@ -2359,10 +2362,8 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
2359 | else { | 2362 | else { |
2360 | wheel.x = 0; | 2363 | wheel.x = 0; |
2361 | } | 2364 | } |
2362 | scroll_DocumentWidget_(d, -wheel.y); | ||
2363 | #if defined (iPlatformAppleMobile) | ||
2364 | wheel.x = -wheel.x; | ||
2365 | #endif | 2365 | #endif |
2366 | scroll_DocumentWidget_(d, -wheel.y); | ||
2366 | scrollWideBlock_DocumentWidget_(d, mouseCoord, wheel.x, 0); | 2367 | scrollWideBlock_DocumentWidget_(d, mouseCoord, wheel.x, 0); |
2367 | } | 2368 | } |
2368 | else | 2369 | else |
diff --git a/src/ui/touch.c b/src/ui/touch.c index 12cd1745..dbb16bd5 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c | |||
@@ -32,21 +32,31 @@ iDeclareType(Touch) | |||
32 | iDeclareType(TouchState) | 32 | iDeclareType(TouchState) |
33 | iDeclareType(Momentum) | 33 | iDeclareType(Momentum) |
34 | 34 | ||
35 | #define numHistory_Touch_ 3 | ||
36 | #define lastIndex_Touch_ (numHistory_Touch_ - 1) | ||
37 | |||
38 | enum iTouchEdge { | ||
39 | none_TouchEdge, | ||
40 | left_TouchEdge, | ||
41 | right_TouchEdge, | ||
42 | }; | ||
43 | |||
35 | struct Impl_Touch { | 44 | struct Impl_Touch { |
36 | SDL_FingerID id; | 45 | SDL_FingerID id; |
37 | iWidget *affinity; /* widget on which the touch started */ | 46 | iWidget *affinity; /* widget on which the touch started */ |
38 | iBool hasMoved; | 47 | iBool hasMoved; |
48 | enum iTouchEdge edge; | ||
39 | uint32_t startTime; | 49 | uint32_t startTime; |
40 | iFloat3 startPos; | 50 | iFloat3 startPos; |
41 | uint32_t posTime[2]; | 51 | uint32_t posTime[numHistory_Touch_]; |
42 | iFloat3 pos[2]; | 52 | iFloat3 pos[numHistory_Touch_]; |
43 | iFloat3 remainder; | 53 | iFloat3 remainder; |
44 | }; | 54 | }; |
45 | 55 | ||
46 | iLocalDef void pushPos_Touch_(iTouch *d, const iFloat3 pos, uint32_t time) { | 56 | iLocalDef void pushPos_Touch_(iTouch *d, const iFloat3 pos, uint32_t time) { |
47 | d->posTime[1] = d->posTime[0]; | 57 | memmove(d->posTime + 1, d->posTime, (numHistory_Touch_ - 1) * sizeof(d->posTime[0])); |
58 | memmove(d->pos + 1, d->pos, (numHistory_Touch_ - 1) * sizeof(d->pos[0])); | ||
48 | d->posTime[0] = time; | 59 | d->posTime[0] = time; |
49 | d->pos[1] = d->pos[0]; | ||
50 | d->pos[0] = pos; | 60 | d->pos[0] = pos; |
51 | } | 61 | } |
52 | 62 | ||
@@ -84,8 +94,8 @@ static iTouch *find_TouchState_(iTouchState *d, SDL_FingerID id) { | |||
84 | return NULL; | 94 | return NULL; |
85 | } | 95 | } |
86 | 96 | ||
87 | static uint32_t longPressSpanMs_ = 500; | 97 | static const uint32_t longPressSpanMs_ = 500; |
88 | static int tapRadiusPt_ = 15; | 98 | static const int tapRadiusPt_ = 15; |
89 | 99 | ||
90 | static iBool isStationary_Touch_(const iTouch *d) { | 100 | static iBool isStationary_Touch_(const iTouch *d) { |
91 | return !d->hasMoved && | 101 | return !d->hasMoved && |
@@ -93,7 +103,7 @@ static iBool isStationary_Touch_(const iTouch *d) { | |||
93 | } | 103 | } |
94 | 104 | ||
95 | static void dispatchClick_Touch_(const iTouch *d, int button) { | 105 | static void dispatchClick_Touch_(const iTouch *d, int button) { |
96 | const iFloat3 tapPos = divf_F3(add_F3(d->pos[0], d->startPos), 2); | 106 | const iFloat3 tapPos = d->pos[0]; //divf_F3(add_F3(d->pos[0], d->startPos), 2); |
97 | SDL_MouseButtonEvent btn = { | 107 | SDL_MouseButtonEvent btn = { |
98 | .type = SDL_MOUSEBUTTONDOWN, | 108 | .type = SDL_MOUSEBUTTONDOWN, |
99 | .button = button, | 109 | .button = button, |
@@ -140,12 +150,13 @@ static void update_TouchState_(void *ptr) { | |||
140 | } | 150 | } |
141 | } | 151 | } |
142 | /* Update/cancel momentum scrolling. */ { | 152 | /* Update/cancel momentum scrolling. */ { |
143 | const float minSpeed = 10.0f; | 153 | const float minSpeed = 15.0f; |
144 | const float momFriction = 0.975f; | 154 | const float momFriction = 0.98f; /* per step */ |
145 | const float stepDurationMs = 1000.0f / 120.0f; | 155 | const float stepDurationMs = 1000.0f / 120.0f; |
146 | double momAvailMs = nowTime - d->lastMomTime; | 156 | double momAvailMs = nowTime - d->lastMomTime; |
147 | int numSteps = iMin((int) (momAvailMs / stepDurationMs), 10); | 157 | int numSteps = (int) (momAvailMs / stepDurationMs); |
148 | d->lastMomTime += numSteps * stepDurationMs; | 158 | d->lastMomTime += numSteps * stepDurationMs; |
159 | numSteps = iMin(numSteps, 10); /* don't spend too much time here */ | ||
149 | // printf("mom steps:%d\n", numSteps); | 160 | // printf("mom steps:%d\n", numSteps); |
150 | iForEach(Array, m, d->moms) { | 161 | iForEach(Array, m, d->moms) { |
151 | if (numSteps == 0) break; | 162 | if (numSteps == 0) break; |
@@ -159,13 +170,12 @@ static void update_TouchState_(void *ptr) { | |||
159 | subv_F3(&mom->accum, initI2_F3(pixels)); | 170 | subv_F3(&mom->accum, initI2_F3(pixels)); |
160 | dispatchEvent_Widget(mom->affinity, (SDL_Event *) &(SDL_MouseWheelEvent){ | 171 | dispatchEvent_Widget(mom->affinity, (SDL_Event *) &(SDL_MouseWheelEvent){ |
161 | .type = SDL_MOUSEWHEEL, | 172 | .type = SDL_MOUSEWHEEL, |
162 | .timestamp = SDL_GetTicks(), | 173 | .timestamp = nowTime, |
163 | .which = 0, /* means "precise scrolling" in DocumentWidget */ | 174 | .which = 0, /* means "precise scrolling" in DocumentWidget */ |
164 | .x = pixels.x, | 175 | .x = pixels.x, |
165 | .y = pixels.y | 176 | .y = pixels.y |
166 | }); | 177 | }); |
167 | } | 178 | } |
168 | //printf("mom vel:%f\n", length_F3(mom->velocity)); | ||
169 | if (length_F3(mom->velocity) < minSpeed) { | 179 | if (length_F3(mom->velocity) < minSpeed) { |
170 | remove_ArrayIterator(&m); | 180 | remove_ArrayIterator(&m); |
171 | } | 181 | } |
@@ -192,10 +202,20 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
192 | const uint32_t nowTime = SDL_GetTicks(); | 202 | const uint32_t nowTime = SDL_GetTicks(); |
193 | if (ev->type == SDL_FINGERDOWN) { | 203 | if (ev->type == SDL_FINGERDOWN) { |
194 | /* Register the new touch. */ | 204 | /* Register the new touch. */ |
195 | iWidget *aff = hitChild_Widget(window->root, init_I2(iRound(x_F3(pos)), iRound(y_F3(pos)))); | 205 | const float x = x_F3(pos); |
206 | enum iTouchEdge edge = none_TouchEdge; | ||
207 | const int edgeWidth = 30 * window->pixelRatio; | ||
208 | if (x < edgeWidth) { | ||
209 | edge = left_TouchEdge; | ||
210 | } | ||
211 | else if (x > rootSize.x - edgeWidth) { | ||
212 | edge = right_TouchEdge; | ||
213 | } | ||
214 | iWidget *aff = hitChild_Widget(window->root, init_I2(iRound(x), iRound(y_F3(pos)))); | ||
196 | pushBack_Array(d->touches, &(iTouch){ | 215 | pushBack_Array(d->touches, &(iTouch){ |
197 | .id = fing->fingerId, | 216 | .id = fing->fingerId, |
198 | .affinity = aff, | 217 | .affinity = aff, |
218 | .edge = edge, | ||
199 | .startTime = nowTime, | 219 | .startTime = nowTime, |
200 | .startPos = pos, | 220 | .startPos = pos, |
201 | .pos = pos | 221 | .pos = pos |
@@ -213,18 +233,29 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
213 | else if (ev->type == SDL_FINGERMOTION) { | 233 | else if (ev->type == SDL_FINGERMOTION) { |
214 | iTouch *touch = find_TouchState_(d, fing->fingerId); | 234 | iTouch *touch = find_TouchState_(d, fing->fingerId); |
215 | if (touch && touch->affinity) { | 235 | if (touch && touch->affinity) { |
216 | /* TODO: Update touch position. */ | 236 | /* Update touch position. */ |
237 | pushPos_Touch_(touch, pos, nowTime); | ||
217 | const iFloat3 amount = add_F3(touch->remainder, | 238 | const iFloat3 amount = add_F3(touch->remainder, |
218 | divf_F3(mul_F3(init_F3(fing->dx, fing->dy, 0), | 239 | divf_F3(mul_F3(init_F3(fing->dx, fing->dy, 0), |
219 | init_F3(rootSize.x, rootSize.y, 0)), | 240 | init_F3(rootSize.x, rootSize.y, 0)), |
220 | window->pixelRatio)); | 241 | window->pixelRatio)); |
221 | const iInt2 pixels = init_I2(iRound(x_F3(amount)), iRound(y_F3(amount))); | 242 | iInt2 pixels = init_I2(iRound(x_F3(amount)), iRound(y_F3(amount))); |
243 | /* We're reporting scrolling as full points, so keep track of the precise distance. */ | ||
222 | iFloat3 remainder = sub_F3(amount, initI2_F3(pixels)); | 244 | iFloat3 remainder = sub_F3(amount, initI2_F3(pixels)); |
223 | touch->remainder = remainder; | 245 | touch->remainder = remainder; |
224 | pushPos_Touch_(touch, pos, nowTime); | ||
225 | if (!touch->hasMoved && !isStationary_Touch_(touch)) { | 246 | if (!touch->hasMoved && !isStationary_Touch_(touch)) { |
226 | touch->hasMoved = iTrue; | 247 | touch->hasMoved = iTrue; |
227 | } | 248 | } |
249 | /* Edge swipe aborted? */ | ||
250 | if (touch->edge == left_TouchEdge && fing->dx < 0) { | ||
251 | touch->edge = none_TouchEdge; | ||
252 | } | ||
253 | if (touch->edge == right_TouchEdge && fing->dx > 0) { | ||
254 | touch->edge = none_TouchEdge; | ||
255 | } | ||
256 | if (touch->edge) { | ||
257 | pixels.y = 0; | ||
258 | } | ||
228 | if (pixels.x || pixels.y) { | 259 | if (pixels.x || pixels.y) { |
229 | // printf("%p (%s) wy: %f\n", touch->affinity, class_Widget(touch->affinity)->name, | 260 | // printf("%p (%s) wy: %f\n", touch->affinity, class_Widget(touch->affinity)->name, |
230 | // fing->dy * rootSize.y / window->pixelRatio); | 261 | // fing->dy * rootSize.y / window->pixelRatio); |
@@ -247,29 +278,46 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
247 | if (touch->id != fing->fingerId) { | 278 | if (touch->id != fing->fingerId) { |
248 | continue; | 279 | continue; |
249 | } | 280 | } |
250 | const uint32_t elapsed = nowTime - touch->posTime[1]; | 281 | /* Edge swipes do not generate momentum. */ |
251 | iFloat3 velocity = zero_F3(); | ||
252 | if (elapsed < 50) { | ||
253 | velocity = divf_F3(sub_F3(pos, touch->pos[1]), (float) elapsed / 1000.0f); | ||
254 | } | ||
255 | pushPos_Touch_(touch, pos, nowTime); | ||
256 | iBool wasUsed = iFalse; | ||
257 | const uint32_t duration = nowTime - touch->startTime; | 282 | const uint32_t duration = nowTime - touch->startTime; |
258 | /* If short and didn't move far, do a tap (left click). */ | 283 | const iFloat3 gestureVector = sub_F3(pos, touch->startPos); |
259 | if (duration < longPressSpanMs_ && isStationary_Touch_(touch)) { | 284 | iFloat3 velocity = zero_F3(); |
260 | dispatchClick_Touch_(touch, SDL_BUTTON_LEFT); | 285 | if (touch->edge && fabsf(2 * x_F3(gestureVector)) > fabsf(y_F3(gestureVector)) && |
286 | !isStationary_Touch_(touch)) { | ||
287 | dispatchClick_Touch_(touch, touch->edge == left_TouchEdge ? SDL_BUTTON_X1 | ||
288 | : SDL_BUTTON_X2); | ||
261 | } | 289 | } |
262 | else if (length_F3(velocity) > 10.0f) { | 290 | else { |
263 | clearWidgetMomentum_TouchState_(d, touch->affinity); | 291 | const uint32_t elapsed = fing->timestamp - touch->posTime[lastIndex_Touch_]; |
264 | iMomentum mom = { | 292 | const float minVelocity = 400.0f; |
265 | .affinity = touch->affinity, | 293 | if (elapsed < 40) { |
266 | .releaseTime = nowTime, | 294 | velocity = divf_F3(sub_F3(pos, touch->pos[lastIndex_Touch_]), |
267 | .velocity = velocity | 295 | (float) elapsed / 1000.0f); |
268 | }; | 296 | if (fabsf(x_F3(velocity)) < minVelocity) { |
269 | if (isEmpty_Array(d->moms)) { | 297 | setX_F3(&velocity, 0.0f); |
270 | d->lastMomTime = nowTime; | 298 | } |
299 | if (fabsf(y_F3(velocity)) < minVelocity) { | ||
300 | setY_F3(&velocity, 0.0f); | ||
301 | } | ||
302 | } | ||
303 | pushPos_Touch_(touch, pos, nowTime); | ||
304 | /* If short and didn't move far, do a tap (left click). */ | ||
305 | if (duration < longPressSpanMs_ && isStationary_Touch_(touch)) { | ||
306 | dispatchClick_Touch_(touch, SDL_BUTTON_LEFT); | ||
307 | } | ||
308 | else if (length_F3(velocity) > 0.0f) { | ||
309 | // printf("vel:%f\n", length_F3(velocity)); | ||
310 | clearWidgetMomentum_TouchState_(d, touch->affinity); | ||
311 | iMomentum mom = { | ||
312 | .affinity = touch->affinity, | ||
313 | .releaseTime = nowTime, | ||
314 | .velocity = velocity | ||
315 | }; | ||
316 | if (isEmpty_Array(d->moms)) { | ||
317 | d->lastMomTime = nowTime; | ||
318 | } | ||
319 | pushBack_Array(d->moms, &mom); | ||
271 | } | 320 | } |
272 | pushBack_Array(d->moms, &mom); | ||
273 | } | 321 | } |
274 | remove_ArrayIterator(&i); | 322 | remove_ArrayIterator(&i); |
275 | } | 323 | } |