Input.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. #if !IS_POW_2(INPUT_COMBO_NUM)
  6. #error INPUT_COMBO_NUM must be a power of 2
  7. #endif
  8. /******************************************************************************/
  9. #if ANDROID
  10. ASensorEventQueue *SensorEventQueue;
  11. #elif IOS
  12. CLLocationManager *LocationManager[2];
  13. CMMotionManager *CoreMotionMgr;
  14. #endif
  15. Vec AccelerometerValue, GyroscopeValue, MagnetometerValue;
  16. Bool LocationBackground[2];
  17. Dbl LocationLat[2], LocationLon[2];
  18. Flt LocationAlt[2], LocationAcc[2], LocationSpd[2], LocationInterval[2]={-1, -1}, MagnetometerInterval=-1;
  19. DateTime LocationTim[2];
  20. InputComboClass InputCombo;
  21. InputDevicesClass InputDevices;
  22. /******************************************************************************/
  23. // ACCELEROMETER, GYROSCOPE, MAGNETOMETER, LOCATION
  24. /******************************************************************************/
  25. C Vec& Accelerometer() {return AccelerometerValue;}
  26. C Vec& Gyroscope () {return GyroscopeValue;}
  27. C Vec& Magnetometer () {return MagnetometerValue;}
  28. Dbl LocationLatitude (Bool gps) {return LocationLat[gps];}
  29. Dbl LocationLongitude(Bool gps) {return LocationLon[gps];}
  30. Flt LocationAltitude (Bool gps) {return LocationAlt[gps];}
  31. Flt LocationAccuracy (Bool gps) {return LocationAcc[gps];}
  32. Flt LocationSpeed (Bool gps) {return LocationSpd[gps];}
  33. C DateTime& LocationTimeUTC (Bool gps) {return LocationTim[gps];}
  34. /******************************************************************************/
  35. #if !WINDOWS_NEW
  36. void SetMagnetometerRefresh(Flt interval)
  37. {
  38. #if ANDROID
  39. if(SensorEventQueue)
  40. if(ASensorManager *sensor_manager=ASensorManager_getInstance())
  41. if(C ASensor *magnetometer=ASensorManager_getDefaultSensor(sensor_manager, ASENSOR_TYPE_MAGNETIC_FIELD))
  42. {
  43. if(interval>=0) // enable
  44. {
  45. ASensorEventQueue_setEventRate (SensorEventQueue, magnetometer, RoundU(interval*1000000));
  46. ASensorEventQueue_enableSensor (SensorEventQueue, magnetometer);
  47. }else ASensorEventQueue_disableSensor(SensorEventQueue, magnetometer); // disable
  48. }
  49. #elif IOS
  50. const Bool gps=true;
  51. if(CLLocationManager *lm=LocationManager[gps])
  52. if(interval>=0 && [CLLocationManager headingAvailable])
  53. {
  54. [lm startUpdatingHeading];
  55. }else
  56. {
  57. [lm stopUpdatingHeading];
  58. }
  59. #endif
  60. }
  61. #endif
  62. /******************************************************************************/
  63. void SetLocationRefresh(Flt interval, Bool gps)
  64. {
  65. #if ANDROID
  66. JNI jni; if(jni && EsenthelLocationListener[gps])
  67. {
  68. if(interval>=0) // enable
  69. {
  70. RequirePermission(PERMISSION_LOCATION);
  71. ULong min_time=RoundU(interval*1000);
  72. jni->CallVoidMethod(LocationManager, requestLocationUpdates, gps ? GPS_PROVIDER() : NETWORK_PROVIDER(), min_time, 0.0f, EsenthelLocationListener[gps]());
  73. }else // disable
  74. {
  75. jni->CallVoidMethod(LocationManager, removeUpdates, EsenthelLocationListener[gps]());
  76. }
  77. if(jni->ExceptionCheck())
  78. {
  79. #if DEBUG
  80. jni->ExceptionDescribe();
  81. #endif
  82. jni->ExceptionClear();
  83. }
  84. }
  85. #elif IOS
  86. if(CLLocationManager *lm=LocationManager[gps])
  87. if(interval>=0 && [CLLocationManager locationServicesEnabled])
  88. {
  89. if(LocationBackground[gps]){if([lm respondsToSelector:@selector(requestAlwaysAuthorization )])[lm requestAlwaysAuthorization ];}
  90. else {if([lm respondsToSelector:@selector(requestWhenInUseAuthorization)])[lm requestWhenInUseAuthorization];}
  91. if(gps)[lm startUpdatingLocation];
  92. else [lm startMonitoringSignificantLocationChanges];
  93. }else
  94. {
  95. if(gps)[lm stopUpdatingLocation];
  96. else [lm stopMonitoringSignificantLocationChanges];
  97. }
  98. #endif
  99. }
  100. /******************************************************************************/
  101. void MagnetometerRefresh(Flt interval) {MAX(interval, 0); if(!Equal(MagnetometerInterval, interval))SetMagnetometerRefresh(MagnetometerInterval=interval);}
  102. void MagnetometerDisable( ) { if(!Equal(MagnetometerInterval, -1))SetMagnetometerRefresh(MagnetometerInterval= -1);}
  103. void LocationRefresh(Flt interval, Bool gps, Bool background) {MAX(interval, 0); if(!Equal(LocationInterval[gps], interval) || LocationBackground[gps]!=background){LocationBackground[gps]=background; SetLocationRefresh(LocationInterval[gps]=interval, gps);}}
  104. void LocationDisable( Bool gps ) { if(!Equal(LocationInterval[gps], -1) ) SetLocationRefresh(LocationInterval[gps]= -1, gps); }
  105. LOCATION_AUTHORIZATION_STATUS LocationAuthorizationStatus()
  106. {
  107. #if ANDROID
  108. return HasPermission(PERMISSION_LOCATION) ? LAS_BACKGROUND : LAS_DENIED;
  109. #elif IOS
  110. switch([CLLocationManager authorizationStatus])
  111. {
  112. case kCLAuthorizationStatusRestricted : return LAS_RESTRICTED;
  113. case kCLAuthorizationStatusDenied : return LAS_DENIED;
  114. case kCLAuthorizationStatusAuthorizedWhenInUse: return LAS_FOREGROUND;
  115. case kCLAuthorizationStatusAuthorizedAlways : return LAS_BACKGROUND;
  116. }
  117. #endif
  118. return LAS_UNKNOWN;
  119. }
  120. /******************************************************************************/
  121. #if ANDROID
  122. static ULong LocationTime[2];
  123. void UpdateLocation(jobject location, Bool gps, JNI &jni)
  124. {
  125. if(location && jni && getTime)
  126. {
  127. ULong time=jni->CallLongMethod(location, getTime); // milliseconds since 1st Jan 1970
  128. if(time!=LocationTime[gps])
  129. {
  130. LocationTim[gps].from1970ms(LocationTime[gps]=time);
  131. LocationLat[gps]=jni->CallDoubleMethod(location, getLatitude );
  132. LocationLon[gps]=jni->CallDoubleMethod(location, getLongitude);
  133. LocationAlt[gps]=jni->CallDoubleMethod(location, getAltitude );
  134. LocationAcc[gps]=jni->CallFloatMethod (location, getAccuracy );
  135. LocationSpd[gps]=jni->CallFloatMethod (location, getSpeed );
  136. }
  137. }
  138. }
  139. void UpdateLocation(Bool gps, JNI &jni)
  140. {
  141. if(jni && getLastKnownLocation)
  142. {
  143. JObject location=JObject(jni, jni->CallObjectMethod(LocationManager, getLastKnownLocation, gps ? GPS_PROVIDER() : NETWORK_PROVIDER()));
  144. if(jni->ExceptionCheck())
  145. {
  146. #if DEBUG
  147. jni->ExceptionDescribe();
  148. #endif
  149. location.clear(); jni->ExceptionClear();
  150. }
  151. UpdateLocation(location, gps, jni);
  152. }
  153. }
  154. void UpdateLocation(JNI &jni)
  155. {
  156. REP(2)UpdateLocation(i!=0, jni);
  157. }
  158. #endif
  159. /******************************************************************************/
  160. // INPUT BUTTON
  161. /******************************************************************************/
  162. Bool InputButton::on()C
  163. {
  164. switch(type)
  165. {
  166. case INPUT_KEYBOARD: return Kb .b(KB_KEY(button));
  167. case INPUT_MOUSE : return Ms .b( button );
  168. case INPUT_JOYPAD : return InRange(device, Joypads) ? Joypads[device].b( button ) : false;
  169. }
  170. return false;
  171. }
  172. Bool InputButton::pd()C
  173. {
  174. switch(type)
  175. {
  176. case INPUT_KEYBOARD: return Kb .bp(KB_KEY(button));
  177. case INPUT_MOUSE : return Ms .bp( button );
  178. case INPUT_JOYPAD : return InRange(device, Joypads) ? Joypads[device].bp( button ) : false;
  179. }
  180. return false;
  181. }
  182. Bool InputButton::rs()C
  183. {
  184. switch(type)
  185. {
  186. case INPUT_KEYBOARD: return Kb .br(KB_KEY(button));
  187. case INPUT_MOUSE : return Ms .br( button );
  188. case INPUT_JOYPAD : return InRange(device, Joypads) ? Joypads[device].br( button ) : false;
  189. }
  190. return false;
  191. }
  192. Bool InputButton::db()C
  193. {
  194. switch(type)
  195. {
  196. case INPUT_KEYBOARD: return Kb .bd(KB_KEY(button));
  197. case INPUT_MOUSE : return Ms .bd( button );
  198. case INPUT_JOYPAD : return InRange(device, Joypads) ? Joypads[device].bd( button ) : false;
  199. }
  200. return false;
  201. }
  202. Str InputButton::name()C
  203. {
  204. switch(type)
  205. {
  206. case INPUT_KEYBOARD: return Kb . keyName(KB_KEY(button));
  207. case INPUT_MOUSE : return Ms .buttonName( button );
  208. case INPUT_JOYPAD : return InRange(device, Joypads) ? Joypads[device].buttonName( button ) : S;
  209. default : return S;
  210. }
  211. }
  212. Bool InputButton::operator==(C InputButton &b)C
  213. {
  214. return T.button==b.button
  215. && T.type ==b.type
  216. && T.device==b.device;
  217. }
  218. Bool InputButton::operator!=(C InputButton &b)C
  219. {
  220. return T.button!=b.button
  221. || T.type !=b.type
  222. || T.device!=b.device;
  223. }
  224. /******************************************************************************/
  225. // INPUT
  226. /******************************************************************************/
  227. Bool Input::on()C {return b[0].on() || b[1].on() || b[2].on();}
  228. Bool Input::pd()C {return b[0].pd() || b[1].pd() || b[2].pd();}
  229. Bool Input::rs()C {return b[0].rs() || b[1].rs() || b[2].rs();}
  230. Bool Input::db()C {return b[0].db() || b[1].db() || b[2].db();}
  231. void Input::set(C Str &name, UInt group)
  232. {
  233. T.name =name ;
  234. T.group=group;
  235. }
  236. void Input::set(C Str &name, UInt group, C InputButton &b0, C InputButton *b1, C InputButton *b2)
  237. {
  238. T.name =name ;
  239. T.group=group;
  240. b[0]= b0;
  241. if(b1)b[1]=*b1;else Zero(b[1]);
  242. if(b2)b[2]=*b2;else Zero(b[2]);
  243. }
  244. Bool Input::operator()(C InputButton &b)C
  245. {
  246. return T.b[0]==b
  247. || T.b[1]==b
  248. || T.b[2]==b;
  249. }
  250. /******************************************************************************/
  251. // INPUT COMBO
  252. /******************************************************************************/
  253. #define INPUT_COMBO_AND (INPUT_COMBO_NUM-1)
  254. InputComboClass::InputComboClass()
  255. {
  256. dt=0.22f;
  257. }
  258. void InputComboClass::clear()
  259. {
  260. pos=length=0;
  261. }
  262. void InputComboClass::add(C InputButton &inp_btn)
  263. {
  264. t[pos]=Time.appTime();
  265. b[pos]=inp_btn;
  266. pos =((pos+1)&INPUT_COMBO_AND);
  267. length=Min(length+1, Elms(b));
  268. }
  269. Bool InputComboClass::operator()(C Input &i0, C Input &i1)C
  270. {
  271. if(length>=2)
  272. {
  273. Byte p1=((pos-1)&INPUT_COMBO_AND),
  274. p0=((pos-2)&INPUT_COMBO_AND);
  275. return i1(b[p1]) && (Time.appTime()-t[p1]<dt)
  276. && i0(b[p0]) && (t[p1] -t[p0]<dt);
  277. }
  278. return false;
  279. }
  280. Bool InputComboClass::operator()(C Input &i0, C Input &i1, C Input &i2)C
  281. {
  282. if(length>=3)
  283. {
  284. Byte p2=((pos-1)&INPUT_COMBO_AND),
  285. p1=((pos-2)&INPUT_COMBO_AND),
  286. p0=((pos-3)&INPUT_COMBO_AND);
  287. return i2(b[p2]) && (Time.appTime()-t[p2]<dt)
  288. && i1(b[p1]) && (t[p2] -t[p1]<dt)
  289. && i0(b[p0]) && (t[p1] -t[p0]<dt);
  290. }
  291. return false;
  292. }
  293. Bool InputComboClass::operator()(C Input &i0, C Input &i1, C Input &i2, C Input &i3)C
  294. {
  295. if(length>=4)
  296. {
  297. Byte p3=((pos-1)&INPUT_COMBO_AND),
  298. p2=((pos-2)&INPUT_COMBO_AND),
  299. p1=((pos-3)&INPUT_COMBO_AND),
  300. p0=((pos-4)&INPUT_COMBO_AND);
  301. return i3(b[p3]) && (Time.appTime()-t[p3]<dt)
  302. && i2(b[p2]) && (t[p3] -t[p2]<dt)
  303. && i1(b[p1]) && (t[p2] -t[p1]<dt)
  304. && i0(b[p0]) && (t[p1] -t[p0]<dt);
  305. }
  306. return false;
  307. }
  308. Bool InputComboClass::operator()(C Input &i0, C Input &i1, C Input &i2, C Input &i3, C Input &i4)C
  309. {
  310. if(length>=5)
  311. {
  312. Byte p4=((pos-1)&INPUT_COMBO_AND),
  313. p3=((pos-2)&INPUT_COMBO_AND),
  314. p2=((pos-3)&INPUT_COMBO_AND),
  315. p1=((pos-4)&INPUT_COMBO_AND),
  316. p0=((pos-5)&INPUT_COMBO_AND);
  317. return i4(b[p4]) && (Time.appTime()-t[p4]<dt)
  318. && i3(b[p3]) && (t[p4] -t[p3]<dt)
  319. && i2(b[p2]) && (t[p3] -t[p2]<dt)
  320. && i1(b[p1]) && (t[p2] -t[p1]<dt)
  321. && i0(b[p0]) && (t[p1] -t[p0]<dt);
  322. }
  323. return false;
  324. }
  325. #undef INPUT_COMBO_AND
  326. /******************************************************************************/
  327. // INPUT
  328. /******************************************************************************/
  329. void InputDevicesClass::del()
  330. {
  331. ShutJoypads();
  332. Ms.del();
  333. Kb.del();
  334. #if WINDOWS_OLD
  335. RELEASE(DI);
  336. #elif IOS
  337. [CoreMotionMgr release]; CoreMotionMgr=null;
  338. #endif
  339. VR.shut(); // !! delete as last, after the mouse, because it may try to reset the mouse cursor, so we need to make sure that mouse cursor was already deleted !!
  340. }
  341. /******************************************************************************/
  342. Bool InputDevicesClass::create()
  343. {
  344. if(LogInit)LogN("InputDevicesClass.create");
  345. #if WINDOWS_OLD
  346. if(LogInit)LogN("DirectInput8Create");
  347. if(OK(DirectInput8Create(App._hinstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (Ptr*)&DI, null)))
  348. #endif
  349. {
  350. Kb .create();
  351. Ms .create();
  352. InitJoypads();
  353. //VR .init (); this is now to be called manually in 'InitPre'
  354. #if IOS
  355. CoreMotionMgr=[[CMMotionManager alloc] init];
  356. CoreMotionMgr.accelerometerUpdateInterval=1.0f/60; // 60 Hz
  357. CoreMotionMgr. gyroUpdateInterval=1.0f/60; // 60 Hz
  358. #endif
  359. if(App.active())acquire(true);
  360. return true;
  361. }
  362. return false;
  363. }
  364. /******************************************************************************/
  365. void InputDevicesClass::update()
  366. {
  367. Kb .update();
  368. Ms .update();
  369. REPAO(Joypads).update();
  370. TouchesUpdate();
  371. VR.update();
  372. #if ANDROID
  373. if(!(Time.frame()%60))UpdateLocation(Jni); // update once per 60-frames, because it was reported that this can generate plenty of messages in the log
  374. #elif IOS
  375. if(CoreMotionMgr)
  376. {
  377. if(CMAccelerometerData *acceleration=CoreMotionMgr.accelerometerData)
  378. AccelerometerValue.set(acceleration.acceleration.x, acceleration.acceleration.y, -acceleration.acceleration.z)*=9.80665f;
  379. if(CMGyroData *gyroscope=CoreMotionMgr.gyroData)
  380. GyroscopeValue.set(gyroscope.rotationRate.x, gyroscope.rotationRate.y, -gyroscope.rotationRate.z);
  381. }
  382. #endif
  383. }
  384. void InputDevicesClass::clear()
  385. {
  386. Kb .clear();
  387. Ms .clear();
  388. REPAO(Joypads).clear();
  389. TouchesClear();
  390. }
  391. /******************************************************************************/
  392. void InputDevicesClass::acquire(Bool on)
  393. {
  394. #if WINDOWS && !(_WIN32_WINNT>=_WIN32_WINNT_WIN10) // this is now deprecated
  395. XInputEnable(on);
  396. #endif
  397. Kb .acquire(on);
  398. Ms .acquire(on);
  399. REPAO(Joypads).acquire(on);
  400. #if ANDROID
  401. if(SensorEventQueue)
  402. if(ASensorManager *sensor_manager=ASensorManager_getInstance())
  403. {
  404. if(C ASensor *accelerometer=ASensorManager_getDefaultSensor(sensor_manager, ASENSOR_TYPE_ACCELEROMETER))
  405. {
  406. if(on) // start monitoring the accelerometer
  407. {
  408. ASensorEventQueue_setEventRate (SensorEventQueue, accelerometer, 1000*1000/60); // 60 events per second
  409. ASensorEventQueue_enableSensor (SensorEventQueue, accelerometer);
  410. }else ASensorEventQueue_disableSensor(SensorEventQueue, accelerometer); // stop monitoring accelerometer
  411. }
  412. if(C ASensor *gyroscope=ASensorManager_getDefaultSensor(sensor_manager, ASENSOR_TYPE_GYROSCOPE))
  413. {
  414. if(on) // start monitoring the gyroscope
  415. {
  416. ASensorEventQueue_setEventRate (SensorEventQueue, gyroscope, 1000*1000/60); // 60 events per second
  417. ASensorEventQueue_enableSensor (SensorEventQueue, gyroscope);
  418. }else ASensorEventQueue_disableSensor(SensorEventQueue, gyroscope); // stop monitoring gyroscope
  419. }
  420. }
  421. if(on)UpdateLocation(Jni);
  422. #elif IOS
  423. if(CoreMotionMgr)
  424. {
  425. if(on)
  426. {
  427. [CoreMotionMgr startAccelerometerUpdates];
  428. [CoreMotionMgr startGyroUpdates ];
  429. }else
  430. {
  431. [CoreMotionMgr stopAccelerometerUpdates];
  432. [CoreMotionMgr stopGyroUpdates ];
  433. }
  434. }
  435. #endif
  436. REPA(LocationInterval)SetLocationRefresh(on ? LocationInterval[i] : -1, i!=0);
  437. SetMagnetometerRefresh(on ? MagnetometerInterval : -1);
  438. }
  439. /******************************************************************************/
  440. // TEXT EDIT
  441. /******************************************************************************/
  442. static void TextSelCopy(C Str &str, TextEdit &edit) // assumes that 'sel' and 'cur' are in 'str' range (or sel<0)
  443. {
  444. if(edit.sel<0)ClipSet(str);else // copy all
  445. if(Int length=Abs(edit.sel-edit.cur)) // copy part
  446. {
  447. Memt<Char> temp; temp.setNum(length+1);
  448. CopyFastN(temp.data(), str()+Min(edit.sel, edit.cur), length);
  449. temp[length]=0;
  450. ClipSet(temp.data());
  451. }else ClipSet(S); // copy nothing
  452. }
  453. static Bool TextSelRem(Str &str, TextEdit &edit) // assumes that 'sel' and 'cur' are in 'str' range (or sel<0)
  454. {
  455. if(edit.sel<0) // remove all
  456. {
  457. str .clear();
  458. edit.cur= 0;
  459. edit.sel=-1;
  460. }else // remove part
  461. {
  462. Int pos=Min(edit.sel, edit.cur),
  463. cut=Abs(edit.sel- edit.cur);
  464. str .remove(pos, cut);
  465. edit.cur=pos;
  466. edit.sel=-1;
  467. }
  468. return true;
  469. }
  470. static void SkipCombiningLeft (C Str &str, TextEdit &edit) {for(; CharFlagFast(str[edit.cur])&CHARF_COMBINING && edit.cur>0; )edit.cur--;}
  471. static void SkipCombiningRight(C Str &str, TextEdit &edit) {for(; CharFlagFast(str[edit.cur])&CHARF_COMBINING ; )edit.cur++;}
  472. static Bool Processed ( Str &str, TextEdit &edit, Bool multi_line, C Keyboard::Key &key, Bool &changed)
  473. {
  474. if(!key.winCtrl())
  475. {
  476. switch(key.k)
  477. {
  478. case KB_HOME:
  479. {
  480. if(!key.shift())edit.sel=-1;else if(edit.sel<0)edit.sel=edit.cur;
  481. if(!multi_line || key.ctrlCmd())edit.cur=0;else for(; edit.cur>0 && str[edit.cur-1]!='\n'; )edit.cur--;
  482. return true;
  483. }break;
  484. case KB_END:
  485. {
  486. if(!key.shift())edit.sel=-1;else if(edit.sel<0)edit.sel=edit.cur;
  487. if(!multi_line || key.ctrlCmd())edit.cur=str.length();else for(; edit.cur<str.length() && str[edit.cur]!='\n'; )edit.cur++;
  488. return true;
  489. }break;
  490. case KB_LEFT:
  491. {
  492. if(edit.sel>=0 && !key.shift()){edit.cur=Min(edit.cur, edit.sel); edit.sel=-1;}else // cancel selection
  493. if(edit.cur)
  494. {
  495. if(key.shift() && edit.sel<0)edit.sel=edit.cur; // start selection from cursor
  496. edit.cur--; SkipCombiningLeft(str, edit);
  497. if(key.ctrlCmd())for(CHAR_TYPE ct=CharType(str[edit.cur]); edit.cur; )
  498. {
  499. CHAR_TYPE nt=CharType(str[edit.cur-1]);
  500. if(ct==CHART_SPACE)ct=nt;
  501. if(ct!=nt)break;
  502. edit.cur--; SkipCombiningLeft(str, edit);
  503. }
  504. }
  505. return true;
  506. }break;
  507. case KB_RIGHT:
  508. {
  509. if(edit.sel>=0 && !key.shift()){edit.cur=Max(edit.cur, edit.sel); edit.sel=-1;}else // cancel selection
  510. if(edit.cur<str.length())
  511. {
  512. if(key.shift() && edit.sel<0)edit.sel=edit.cur; // start selection from cursor
  513. edit.cur++; SkipCombiningRight(str, edit);
  514. if(key.ctrlCmd())for(CHAR_TYPE ct=CharType(str[edit.cur-1]); edit.cur<str.length(); )
  515. {
  516. CHAR_TYPE nt=CharType(str[edit.cur]);
  517. if(ct!=nt){for(; edit.cur<str.length() && str[edit.cur]==' '; )edit.cur++; break;}
  518. edit.cur++; SkipCombiningRight(str, edit);
  519. }
  520. }
  521. return true;
  522. }break;
  523. case KB_BACK:
  524. {
  525. if(edit.sel>=0)changed|=TextSelRem(str, edit);else
  526. if(edit.cur)
  527. {
  528. if(key.ctrlCmd())
  529. {
  530. edit.sel=edit.cur;
  531. edit.cur--;
  532. for(CHAR_TYPE ct=CharType(str[edit.cur]); edit.cur; edit.cur--)
  533. {
  534. CHAR_TYPE nt=CharType(str[edit.cur-1]);
  535. if(ct!=nt)break;
  536. }
  537. TextSelRem(str, edit);
  538. }else
  539. {
  540. str.remove(--edit.cur);
  541. }
  542. changed=true;
  543. }
  544. return true;
  545. }break;
  546. case KB_TAB: if(multi_line)
  547. {
  548. if(edit.sel>=0)changed|=TextSelRem(str, edit);
  549. /*if(edit.overwrite && edit.cur<str.length())str. _d[edit.cur]='\t';
  550. else*/ str.insert(edit.cur, '\t');
  551. edit.cur++;
  552. changed=true;
  553. return true;
  554. }break;
  555. case KB_ENTER: if(multi_line)
  556. {
  557. if(edit.sel>=0)changed|=TextSelRem(str, edit);
  558. /*if(edit.overwrite && edit.cur<str.length())str. _d[edit.cur]='\n';
  559. else*/ str.insert(edit.cur, '\n');
  560. edit.cur++;
  561. changed=true;
  562. return true;
  563. }break;
  564. case KB_INS:
  565. {
  566. if(key.ctrlCmd()) // copy
  567. {
  568. copy:
  569. if(!edit.password)TextSelCopy(str, edit);
  570. }else
  571. if(key.shift()) // paste
  572. {
  573. paste:
  574. if(edit.sel>=0)changed|=TextSelRem(str, edit);
  575. Str clip=ClipGet();
  576. if( clip.is())
  577. {
  578. str.insert(edit.cur, clip);
  579. edit.cur+=clip.length();
  580. changed=true;
  581. }
  582. }else
  583. edit.overwrite^=1;
  584. return true;
  585. }break;
  586. case KB_DEL:
  587. {
  588. if(key.shift()) // cut
  589. {
  590. cut:
  591. if(!edit.password)
  592. {
  593. TextSelCopy(str, edit);
  594. changed|=TextSelRem (str, edit);
  595. }
  596. }else
  597. {
  598. if(edit.sel>=0)changed|=TextSelRem(str, edit);else
  599. if(edit.cur<str.length())
  600. {
  601. if(key.ctrlCmd())
  602. {
  603. edit.sel=edit.cur;
  604. edit.cur++;
  605. for(CHAR_TYPE ct=CharType(str[edit.cur-1]); edit.cur<str.length(); edit.cur++)
  606. {
  607. CHAR_TYPE nt=CharType(str[edit.cur]);
  608. if(ct==CHART_SPACE)ct=nt;
  609. if(ct!=nt){for(; edit.cur<str.length() && str[edit.cur]==' '; )edit.cur++; break;}
  610. }
  611. TextSelRem(str, edit);
  612. }else
  613. {
  614. Int num=1; for(; CharFlagFast(str[edit.cur+num])&CHARF_COMBINING; )num++; // del all combining characters after deleted one
  615. str.remove(edit.cur, num);
  616. }
  617. changed=true;
  618. }
  619. }
  620. return true;
  621. }break;
  622. case KB_C: if(key.ctrlCmd())goto copy ; break;
  623. case KB_V: if(key.ctrlCmd())goto paste; break;
  624. case KB_X: if(key.ctrlCmd())goto cut ; break;
  625. case KB_A: if(key.ctrlCmd())
  626. {
  627. if(str.length())
  628. {
  629. edit.sel=0;
  630. edit.cur=str.length();
  631. }
  632. return true;
  633. }break;
  634. }
  635. if(!key.ctrlCmd() && !key.lalt())if(key.c)
  636. {
  637. if(edit.sel>=0)changed|=TextSelRem(str, edit);
  638. if(edit.overwrite && edit.cur<str.length())
  639. {
  640. Int num=0; for(; CharFlagFast(str[edit.cur+1+num])&CHARF_COMBINING; )num++; str.remove(edit.cur+1, num); // del all combining characters after replaced one
  641. str._d[edit.cur]=key.c;
  642. }else str.insert(edit.cur, key.c);
  643. edit.cur++;
  644. changed=true;
  645. return true;
  646. }
  647. }
  648. return false;
  649. }
  650. Bool EditText(Str &str, TextEdit &edit, Bool multi_line)
  651. {
  652. Bool changed=false;
  653. if(edit.cur>str.length() || edit.cur<0)edit.cur=str.length();
  654. if(edit.sel>str.length() )edit.sel=str.length();
  655. if(Ms.bp(4)) // make 4-th mouse button as Backspace
  656. {
  657. Keyboard::Key key; key.clear();
  658. key.k=KB_BACK;
  659. Processed(str, edit, multi_line, key, changed);
  660. Ms.eat(4);
  661. }
  662. if(Processed(str, edit, multi_line, Kb.k, changed))
  663. {
  664. again:
  665. Kb.eatKey(); // eat only if processed
  666. if(C Keyboard::Key *key=Kb.nextKeyPtr()) // get next key without moving it
  667. if(Processed(str, edit, multi_line, *key, changed)) // if can process this key, then eat it, otherwise, leave it to appear in the next frame so other codes before 'EditText' can try to process it
  668. {
  669. Kb.nextInQueue(); // move 'key' to 'Kb.k'
  670. goto again;
  671. }
  672. }
  673. if(edit.sel==edit.cur)edit.sel=-1;
  674. return changed;
  675. }
  676. /******************************************************************************/
  677. }
  678. /******************************************************************************/