summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ui/documentwidget.c7
-rw-r--r--src/ui/touch.c120
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)
32iDeclareType(TouchState) 32iDeclareType(TouchState)
33iDeclareType(Momentum) 33iDeclareType(Momentum)
34 34
35#define numHistory_Touch_ 3
36#define lastIndex_Touch_ (numHistory_Touch_ - 1)
37
38enum iTouchEdge {
39 none_TouchEdge,
40 left_TouchEdge,
41 right_TouchEdge,
42};
43
35struct Impl_Touch { 44struct 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
46iLocalDef void pushPos_Touch_(iTouch *d, const iFloat3 pos, uint32_t time) { 56iLocalDef 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
87static uint32_t longPressSpanMs_ = 500; 97static const uint32_t longPressSpanMs_ = 500;
88static int tapRadiusPt_ = 15; 98static const int tapRadiusPt_ = 15;
89 99
90static iBool isStationary_Touch_(const iTouch *d) { 100static 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
95static void dispatchClick_Touch_(const iTouch *d, int button) { 105static 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 }