Touch.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************
  5. On some platforms we need to monitor released touches in order to detect double clicks.
  6. /******************************************************************************/
  7. #define TouchSelectDist2 Sqr(0.025f)
  8. #define TouchSelectBigDist2 Sqr(0.065f)
  9. #define TouchDoubleClickRange2 Sqr(0.080f)
  10. #define StylusSelectTime 0.070f
  11. #define DETECT_DOUBLE_TAP (!IOS) // iOS already provides the number of taps
  12. #if DETECT_DOUBLE_TAP
  13. struct ReleasedTouch
  14. {
  15. Dbl app_time;
  16. Vec2 pos;
  17. void set(Dbl app_time, C Vec2 &pos) {T.app_time=app_time; T.pos=pos;}
  18. };
  19. static MemtN<ReleasedTouch, 2> ReleasedTouches;
  20. #endif
  21. MemtN<Touch, 10> Touches;
  22. MouseTouch MT;
  23. Bool TouchesSupported;
  24. static Bool TouchesSimulated;
  25. static UInt TouchesID;
  26. /******************************************************************************/
  27. Touch::Touch()
  28. {
  29. user_type=0;
  30. user_ptr =null;
  31. _selecting=_dragging=_scrolling=_remove=false;
  32. _first=true;
  33. _state=0;
  34. _axis_moved=0;
  35. _id=0;
  36. _force=0;
  37. _start_time=0;
  38. _start_pos=_prev_pos=_pos=_sm_pos=_delta=_abs_delta=_vel=0; _posi=_deltai=0;
  39. _handle=null;
  40. _gui_obj=null;
  41. }
  42. Touch& Touch::init(C VecI2 &posi, C Vec2 &pos, CPtr handle, Bool stylus)
  43. {
  44. if(TouchesID==0)TouchesID=1; _id=TouchesID++; // don't select zero for the ID
  45. _start_time=Time.appTime();
  46. _start_pos=_prev_pos=_pos=_sm_pos=pos; _sv_pos.init(pos);
  47. _posi=posi;
  48. _handle=handle;
  49. _gui_obj=Gui.objAtPos(pos);
  50. _stylus=stylus;
  51. return T;
  52. }
  53. Touch& Touch::reinit(C VecI2 &posi, C Vec2 &pos)
  54. {
  55. _start_time=Time.appTime();
  56. _start_pos =_pos=pos;
  57. _posi =posi;
  58. _gui_obj =Gui.objAtPos(_pos);
  59. _axis_moved=0;
  60. user_type =0;
  61. user_ptr =null;
  62. return T;
  63. }
  64. void Touch::eat()
  65. {
  66. FlagDisable(_state, BS_NOT_ON);
  67. }
  68. /******************************************************************************/
  69. void MouseTouch::guiObj(Int i, GuiObj *obj) {if(InRange(i, Touches))Touches[i].guiObj(obj);else Gui._ms=Gui._ms_src=obj;}
  70. /******************************************************************************/
  71. Touch* FindTouch(UInt id) // 0 is reserved for mouse
  72. {
  73. if(id)REPA(Touches)if(Touches[i].id()==id)return &Touches[i];
  74. return null;
  75. }
  76. Touch* FindTouchByHandle(CPtr handle)
  77. {
  78. REPA(Touches)if(Touches[i]._handle==handle)return &Touches[i];
  79. return null;
  80. }
  81. /******************************************************************************/
  82. void SimulateTouches(Bool on)
  83. {
  84. #if DESKTOP
  85. if(TouchesSimulated!=on)
  86. {
  87. Touches.del();
  88. TouchesSimulated=on;
  89. }
  90. #endif
  91. }
  92. /******************************************************************************/
  93. Bool SupportedTouches()
  94. {
  95. #if WINDOWS
  96. return TouchesSupported;
  97. #elif MOBILE
  98. return true;
  99. #else
  100. return false;
  101. #endif
  102. }
  103. /******************************************************************************/
  104. void TouchesUpdate()
  105. {
  106. // simulate touches using mouse
  107. if(TouchesSimulated)
  108. {
  109. CPtr handle=&Ms ; // touch handle
  110. Vec2 pos = Ms.pos(); // touch position
  111. VecI2 posi = Ms.desktopPos();
  112. if(Ms.bp(0)) // simulate 'touchesBegan'
  113. {
  114. Touch *t=FindTouchByHandle(handle); // find existing one
  115. if( !t)t=&Touches.New().init(posi, pos, handle, false);else // create new one
  116. {
  117. t->_posi=posi;
  118. t->_pos =pos ;
  119. }
  120. t->_state=BS_ON|BS_PUSHED;
  121. t->_first=!Ms.bd(0);
  122. if(Ms.bd(0))t->_state|=BS_DOUBLE;
  123. }else
  124. if(Ms.b(0)) // simulate 'touchesMoved'
  125. {
  126. Touch *t=FindTouchByHandle(handle); // find existing one
  127. if( !t) // create new one
  128. {
  129. t=&Touches.New().init(posi, pos, handle, false);
  130. t->_state=BS_ON|BS_PUSHED;
  131. }else
  132. {
  133. t->_posi=posi;
  134. t->_pos =pos ;
  135. }
  136. t->_deltai=Ms.pixelDelta();
  137. }
  138. if(Ms.br(0) || !Ms.b(0))if(Touch *t=FindTouchByHandle(handle)) // simulate 'touchesReleased'
  139. {
  140. t->_posi =posi;
  141. t->_pos =pos ;
  142. t->_deltai=Ms.pixelDelta();
  143. t->_remove=true;
  144. if(t->_state&BS_ON) // check for state in case it was manually eaten
  145. {
  146. t->_state|= BS_RELEASED;
  147. t->_state&=~BS_ON;
  148. if(Ms.tapped(0))t->_state|=BS_TAPPED;
  149. }
  150. }
  151. }
  152. // update, calculate delta, detect selecting/dragging/tapping and detect double click
  153. REPA(Touches)
  154. {
  155. Touch &t=Touches[i];
  156. t._delta =t._pos-t._prev_pos; t._prev_pos=t._pos;
  157. t._abs_delta=t._delta*D.scale();
  158. t._sm_pos =t._sv_pos.update(t._pos);
  159. // update velocity
  160. if(!(t.state()&(BS_PUSHED|BS_RELEASED)) // don't update on push/release because on those events position may not be updated
  161. || t.ad().any())
  162. if(Time.ad())t._vel=t._sv_vel.update(t._abs_delta/Time.ad(), Time.ad());
  163. // dragging
  164. if(t.on())
  165. {
  166. if(!t.selecting())
  167. {
  168. Flt dist2=Dist2(t.pos(), t.startPos())*Sqr(D.scale()*(D.smallSize() ? 0.5f : 1.0f));
  169. if(t.stylus() ? (dist2>=TouchSelectDist2 && t.life()>=StylusSelectTime+Time.ad()) || dist2>=TouchSelectBigDist2 // stylus can be slippery, because of that process it differently (for short distance require time, or allow big distance in case user made long swipe)
  170. : dist2>=TouchSelectDist2 )t._selecting=true;
  171. }
  172. if(!t.dragging() && t.selecting() && t.life()>=DragTime+Time.ad())t._dragging=true;
  173. // scroll regions
  174. if(t.id()!=Gui._drag_touch_id) // only if not dragging something
  175. {
  176. if(t.vel().any())
  177. {
  178. if(Abs(t.vel().x)>Abs(t.vel().y))t._axis_moved|=1;else t._axis_moved|=2;
  179. }
  180. if(Region *region=t.guiObj()->firstScrollableRegion())
  181. if(!region->slidebar[0].contains(t.guiObj()) && !region->slidebar[1].contains(t.guiObj()) && t.guiObj()!=&region->view)
  182. t._scrolling=true;
  183. }
  184. }else
  185. if(!t.rs())t._selecting=t._dragging=t._scrolling=false;
  186. if(t._scrolling) // process for 'on' and 'rs'
  187. if(Region *region=t.guiObj()->firstScrollableRegion())
  188. {
  189. if(t._axis_moved&1)region->scrollX(-t.d().x, true);
  190. if(t._axis_moved&2)region->scrollY( t.d().y, true);
  191. if(t.selecting())switch(t.guiObj()->type()) // if enough movement occurred, then switch focus to the region itself, in case it belonged to a child of the region that is clickable, to avoid clicking that object on scroll end, however allow other objects in case we want to drag them
  192. {
  193. case GO_BUTTON :
  194. case GO_CHECKBOX:
  195. case GO_COMBOBOX:
  196. case GO_SLIDEBAR:
  197. case GO_SLIDER :
  198. case GO_TABS :
  199. case GO_TEXTBOX :
  200. case GO_TEXTLINE:
  201. t.guiObj(region);
  202. break;
  203. }
  204. }
  205. if(t.rs())
  206. {
  207. t._force=0;
  208. if(!t.selecting() && t.life()<=0.25f+Time.ad())t._state|=BS_TAPPED;
  209. // scroll regions (don't check for 't.scrolling' here because we want to process dragging region slidebars too and for those the scrolling is not enabled)
  210. if(t._axis_moved && t.id()!=Gui._drag_touch_id) // only if not dragging something
  211. if(Region *region=t.guiObj()->firstScrollableRegion())
  212. if(t.guiObj()!=&region->view)
  213. {
  214. Vec2 vel=t.vel()*Vec2((t._axis_moved&1) ? -0.1f : 0.0f, (t._axis_moved&2) ? 0.1f : 0.0f);
  215. if(region->slidebar[0].contains(t.guiObj()))vel.set(-vel.x, 0);else
  216. if(region->slidebar[1].contains(t.guiObj()))vel.set(0, -vel.y);
  217. if(vel.length2()>=Sqr(0.15f))region->scrollX(vel.x).scrollY(vel.y);
  218. }
  219. }
  220. #if DETECT_DOUBLE_TAP
  221. if(t.pd())REPA(ReleasedTouches) // if pushed then check for possibility of double click
  222. {
  223. ReleasedTouch &rt=ReleasedTouches[i];
  224. if(Time.appTime()-rt.app_time>TouchDoubleClickTime+Time.ad())ReleasedTouches.remove(i);else // too long ago
  225. if(Dist2(rt.pos, t.pos())*Sqr(D.scale()*(D.smallSize() ? 0.5f : 1.0f))<=TouchDoubleClickRange2) // within range
  226. {
  227. t._state|=BS_DOUBLE;
  228. t._first =false; // disable further double clicks for this touch to prevent 2xDbl from 3xTap (2nd release would be stored, and 3rd tap could trigger additional double)
  229. ReleasedTouches.remove(i);
  230. break;
  231. }
  232. }
  233. #endif
  234. }
  235. }
  236. /******************************************************************************/
  237. void TouchesClear()
  238. {
  239. // remove or disable "pushed|released|double|tapped" state
  240. REPA(Touches)
  241. {
  242. Touch &t=Touches[i];
  243. #if DETECT_DOUBLE_TAP
  244. if(t.rs() && t._first)ReleasedTouches.New().set(t.startTime(), t.startPos()); // detect based on releases (and not '_remove' because hovers may not be removed), only for first clicks
  245. #endif
  246. if(t._remove)
  247. {
  248. Touches.remove(i, true);
  249. }else
  250. {
  251. t._deltai.zero();
  252. FlagDisable(t._state, BS_NOT_ON);
  253. }
  254. }
  255. }
  256. /******************************************************************************/
  257. }
  258. /******************************************************************************/