mod.rs 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use async_lock::Mutex as AsyncMutex;
  19. use async_trait::async_trait;
  20. use atomic_float::AtomicF32;
  21. use darkfi::system::msleep;
  22. use darkfi_serial::Decodable;
  23. use futures::FutureExt;
  24. use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
  25. use parking_lot::Mutex as SyncMutex;
  26. use rand::{rngs::OsRng, Rng};
  27. use std::{
  28. io::Cursor,
  29. mem::swap,
  30. ops::{Deref, DerefMut},
  31. sync::{
  32. atomic::{AtomicBool, Ordering},
  33. Arc, Weak,
  34. },
  35. };
  36. #[cfg(target_os = "android")]
  37. use crate::AndroidSuggestEvent;
  38. use crate::{
  39. gfx::{gfxtag, DrawCall, DrawInstruction, DrawMesh, Point, Rectangle, RenderApi, Vertex},
  40. mesh::MeshBuilder,
  41. prop::{
  42. BatchGuardId, BatchGuardPtr, PropertyAtomicGuard, PropertyBool, PropertyColor,
  43. PropertyFloat32, PropertyPtr, PropertyRect, PropertyStr, PropertyUint32, Role,
  44. },
  45. scene::{MethodCallSub, Pimpl, SceneNodePtr, SceneNodeWeak},
  46. text2::{self, Editor},
  47. util::unixtime,
  48. ExecutorPtr,
  49. };
  50. use super::{DrawUpdate, OnModify, UIObject};
  51. mod filter;
  52. use filter::{ALLOWED_KEYCODES, DISALLOWED_CHARS};
  53. mod behave;
  54. pub use behave::BaseEditType;
  55. use behave::{EditorBehavior, MultiLine, ScrollDir, SingleLine};
  56. mod repeat;
  57. use repeat::{PressedKey, PressedKeysSmoothRepeat};
  58. /// The travel threshold on long hold select before activating select.
  59. const HOLD_TRAVEL_THRESHOLD_SQ: f32 = 100.;
  60. /// How long to hold before select is enabled in ms.
  61. const HOLD_ENABLE_TIME: u128 = 500;
  62. /// Minimum dist to update scroll when finger scrolling.
  63. /// Avoid updating too much makes scrolling smoother.
  64. const VERT_SCROLL_UPDATE_INC: f32 = 1.;
  65. /// How often to update the scrolling selection with mouse.
  66. const SELECT_TASK_UPDATE_TIME: u64 = 500;
  67. // Should be a property
  68. const SELECT_SCROLL_TRAVEL_SPEED: f32 = 1.;
  69. macro_rules! d { ($($arg:tt)*) => { debug!(target: "ui::edit", $($arg)*); } }
  70. macro_rules! t { ($($arg:tt)*) => { trace!(target: "ui::edit", $($arg)*); } }
  71. #[derive(Debug, Clone)]
  72. enum TouchStateAction {
  73. Inactive,
  74. Started { pos: Point, instant: std::time::Instant },
  75. StartSelect,
  76. Select,
  77. DragSelectHandle { side: isize },
  78. ScrollVert { start_pos: Point, scroll_start: f32 },
  79. SetCursorPos,
  80. }
  81. struct TouchInfo {
  82. state: TouchStateAction,
  83. scroll: Arc<AtomicF32>,
  84. scroll_ctrl: ScrollDir,
  85. }
  86. impl TouchInfo {
  87. fn new(scroll: Arc<AtomicF32>, scroll_ctrl: ScrollDir) -> Self {
  88. Self { state: TouchStateAction::Inactive, scroll, scroll_ctrl }
  89. }
  90. fn start(&mut self, pos: Point) {
  91. debug!(target: "ui::chatedit::touch", "start touch: Started state");
  92. self.state = TouchStateAction::Started { pos, instant: std::time::Instant::now() };
  93. }
  94. fn stop(&mut self) -> TouchStateAction {
  95. debug!(target: "ui::chatedit::touch", "stop touch: Inactive state");
  96. std::mem::replace(&mut self.state, TouchStateAction::Inactive)
  97. }
  98. fn update(&mut self, pos: &Point) {
  99. match &self.state {
  100. TouchStateAction::Started { pos: start_pos, instant } => {
  101. let travel_dist_sq = pos.dist_sq(*start_pos);
  102. let grad = (pos.y - start_pos.y) / (pos.x - start_pos.x);
  103. let elapsed = instant.elapsed().as_millis();
  104. //debug!(target: "ui::chatedit::touch", "TouchInfo::update() [travel_dist_sq={travel_dist_sq}, grad={grad}]");
  105. if travel_dist_sq < HOLD_TRAVEL_THRESHOLD_SQ {
  106. if elapsed > HOLD_ENABLE_TIME {
  107. debug!(target: "ui::chatedit::touch", "update touch state: Started -> StartSelect");
  108. self.state = TouchStateAction::StartSelect;
  109. }
  110. } else if self.scroll_ctrl.cmp(grad) {
  111. // Vertical movement
  112. debug!(target: "ui::chatedit::touch", "update touch state: Started -> ScrollVert");
  113. let scroll_start = self.scroll.load(Ordering::Relaxed);
  114. self.state =
  115. TouchStateAction::ScrollVert { start_pos: *start_pos, scroll_start };
  116. } else {
  117. // Horizontal movement
  118. debug!(target: "ui::chatedit::touch", "update touch state: Started -> SetCursorPos");
  119. self.state = TouchStateAction::SetCursorPos;
  120. }
  121. }
  122. _ => {}
  123. }
  124. }
  125. }
  126. /*
  127. impl std::fmt::Debug for Editor {
  128. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  129. let mut changes = vec![];
  130. let sel = self.editor.raw_selection();
  131. if sel.is_collapsed() {
  132. let cursor = sel.focus().index();
  133. changes.push((cursor, '|'));
  134. } else {
  135. let sel = sel.text_range();
  136. changes.push((sel.start, '{'));
  137. changes.push((sel.end, '}'));
  138. }
  139. if let Some(compose) = self.editor.compose() {
  140. changes.push((compose.start, '['));
  141. changes.push((compose.end, ']'));
  142. }
  143. changes.sort_by(|a, b| b.0.cmp(&a.0));
  144. write!(f, "'")?;
  145. let mut buffer = self.editor.raw_text();
  146. for (byte_idx, c) in buffer.char_indices() {
  147. while let Some((idx, d)) = changes.last() {
  148. if *idx > byte_idx {
  149. break
  150. }
  151. write!(f, "{}", d)?;
  152. let _ = changes.pop();
  153. }
  154. write!(f, "{}", c)?;
  155. }
  156. write!(f, "'")
  157. }
  158. }
  159. */
  160. struct EditorHandle<'a> {
  161. guard: async_lock::MutexGuard<'a, Option<Editor>>,
  162. }
  163. impl<'a> Deref for EditorHandle<'a> {
  164. type Target = Editor;
  165. fn deref(&self) -> &Self::Target {
  166. self.guard.as_ref().unwrap()
  167. }
  168. }
  169. impl<'a> DerefMut for EditorHandle<'a> {
  170. fn deref_mut(&mut self) -> &mut Self::Target {
  171. self.guard.as_mut().unwrap()
  172. }
  173. }
  174. pub type BaseEditPtr = Arc<BaseEdit>;
  175. pub struct BaseEdit {
  176. node: SceneNodeWeak,
  177. tasks: SyncMutex<Vec<smol::Task<()>>>,
  178. render_api: RenderApi,
  179. key_repeat: SyncMutex<PressedKeysSmoothRepeat>,
  180. // Moves the draw cursor and applies scroll
  181. root_dc_key: u64,
  182. phone_select_handle_dc_key: u64,
  183. // Applies the clipping view
  184. content_dc_key: u64,
  185. select_dc_key: u64,
  186. text_dc_key: u64,
  187. cursor_dc_key: u64,
  188. cursor_mesh: SyncMutex<Option<DrawMesh>>,
  189. is_active: PropertyBool,
  190. is_focused: PropertyBool,
  191. rect: PropertyRect,
  192. baseline: PropertyFloat32,
  193. lineheight: PropertyFloat32,
  194. scroll_speed: PropertyFloat32,
  195. padding: PropertyPtr,
  196. font_size: PropertyFloat32,
  197. text: PropertyStr,
  198. text_color: PropertyColor,
  199. text_hi_color: PropertyColor,
  200. //text_cmd_color: PropertyColor,
  201. cursor_color: PropertyColor,
  202. cursor_width: PropertyFloat32,
  203. cursor_ascent: PropertyFloat32,
  204. cursor_descent: PropertyFloat32,
  205. cursor_blink_time: PropertyUint32,
  206. cursor_idle_time: PropertyUint32,
  207. hi_bg_color: PropertyColor,
  208. //cmd_bg_color: PropertyColor,
  209. select_ascent: PropertyFloat32,
  210. select_descent: PropertyFloat32,
  211. handle_descent: PropertyFloat32,
  212. select_text: PropertyPtr,
  213. z_index: PropertyUint32,
  214. priority: PropertyUint32,
  215. debug: PropertyBool,
  216. mouse_btn_held: AtomicBool,
  217. cursor_is_visible: AtomicBool,
  218. blink_is_paused: AtomicBool,
  219. /// Used to explicitly hide the cursor. Must be manually re-enabled.
  220. hide_cursor: AtomicBool,
  221. /// Used to start select and scroll when mouse moves outside widget rect.
  222. sel_sender: SyncMutex<Option<async_channel::Sender<Option<(Point, Option<isize>)>>>>,
  223. scroll: Arc<AtomicF32>,
  224. touch_info: SyncMutex<TouchInfo>,
  225. is_phone_select: AtomicBool,
  226. window_scale: PropertyFloat32,
  227. parent_rect: Arc<SyncMutex<Option<Rectangle>>>,
  228. is_mouse_hover: AtomicBool,
  229. editor: Arc<AsyncMutex<Option<Editor>>>,
  230. behave: Box<dyn EditorBehavior>,
  231. }
  232. impl BaseEdit {
  233. pub async fn new(
  234. node: SceneNodeWeak,
  235. window_scale: PropertyFloat32,
  236. render_api: RenderApi,
  237. edit_type: BaseEditType,
  238. ) -> Pimpl {
  239. t!("BaseEdit::new()");
  240. let node_ref = &node.upgrade().unwrap();
  241. let is_active = PropertyBool::wrap(node_ref, Role::Internal, "is_active", 0).unwrap();
  242. let is_focused = PropertyBool::wrap(node_ref, Role::Internal, "is_focused", 0).unwrap();
  243. let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap();
  244. let baseline = PropertyFloat32::wrap(node_ref, Role::Internal, "baseline", 0).unwrap();
  245. let lineheight = PropertyFloat32::wrap(node_ref, Role::Internal, "lineheight", 0).unwrap();
  246. let scroll_speed =
  247. PropertyFloat32::wrap(node_ref, Role::Internal, "scroll_speed", 0).unwrap();
  248. let padding = node_ref.get_property("padding").unwrap();
  249. let font_size = PropertyFloat32::wrap(node_ref, Role::Internal, "font_size", 0).unwrap();
  250. let text = PropertyStr::wrap(node_ref, Role::Internal, "text", 0).unwrap();
  251. let text_color = PropertyColor::wrap(node_ref, Role::Internal, "text_color").unwrap();
  252. let text_hi_color = PropertyColor::wrap(node_ref, Role::Internal, "text_hi_color").unwrap();
  253. //let text_cmd_color =
  254. // PropertyColor::wrap(node_ref, Role::Internal, "text_cmd_color").unwrap();
  255. let cursor_color = PropertyColor::wrap(node_ref, Role::Internal, "cursor_color").unwrap();
  256. let cursor_width =
  257. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_width", 0).unwrap();
  258. let cursor_ascent =
  259. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_ascent", 0).unwrap();
  260. let cursor_descent =
  261. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_descent", 0).unwrap();
  262. let hi_bg_color = PropertyColor::wrap(node_ref, Role::Internal, "hi_bg_color").unwrap();
  263. //let cmd_bg_color = PropertyColor::wrap(node_ref, Role::Internal, "cmd_bg_color").unwrap();
  264. let select_ascent =
  265. PropertyFloat32::wrap(node_ref, Role::Internal, "select_ascent", 0).unwrap();
  266. let select_descent =
  267. PropertyFloat32::wrap(node_ref, Role::Internal, "select_descent", 0).unwrap();
  268. let handle_descent =
  269. PropertyFloat32::wrap(node_ref, Role::Internal, "handle_descent", 0).unwrap();
  270. let select_text = node_ref.get_property("select_text").unwrap();
  271. let cursor_blink_time =
  272. PropertyUint32::wrap(node_ref, Role::Internal, "cursor_blink_time", 0).unwrap();
  273. let cursor_idle_time =
  274. PropertyUint32::wrap(node_ref, Role::Internal, "cursor_idle_time", 0).unwrap();
  275. let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
  276. let priority = PropertyUint32::wrap(node_ref, Role::Internal, "priority", 0).unwrap();
  277. let debug = PropertyBool::wrap(node_ref, Role::Internal, "debug", 0).unwrap();
  278. let parent_rect = Arc::new(SyncMutex::new(None));
  279. let editor = Arc::new(AsyncMutex::new(None));
  280. let scroll = Arc::new(AtomicF32::new(0.));
  281. let behave: Box<dyn EditorBehavior> = match edit_type {
  282. BaseEditType::SingleLine => Box::new(SingleLine {
  283. rect: rect.clone(),
  284. padding: padding.clone(),
  285. cursor_width: cursor_width.clone(),
  286. parent_rect: parent_rect.clone(),
  287. editor: editor.clone(),
  288. content_height: AtomicF32::new(0.),
  289. scroll: scroll.clone(),
  290. }),
  291. BaseEditType::MultiLine => {
  292. let min_height =
  293. PropertyFloat32::wrap(node_ref, Role::Internal, "height_range", 0).unwrap();
  294. let max_height =
  295. PropertyFloat32::wrap(node_ref, Role::Internal, "height_range", 1).unwrap();
  296. Box::new(MultiLine {
  297. min_height: min_height.clone(),
  298. max_height: max_height.clone(),
  299. rect: rect.clone(),
  300. baseline: baseline.clone(),
  301. padding: padding.clone(),
  302. cursor_descent: cursor_descent.clone(),
  303. parent_rect: parent_rect.clone(),
  304. editor: editor.clone(),
  305. content_height: AtomicF32::new(0.),
  306. scroll: scroll.clone(),
  307. })
  308. }
  309. };
  310. let self_ = Arc::new(Self {
  311. node,
  312. tasks: SyncMutex::new(vec![]),
  313. render_api,
  314. key_repeat: SyncMutex::new(PressedKeysSmoothRepeat::new(400, 50)),
  315. root_dc_key: OsRng.gen(),
  316. phone_select_handle_dc_key: OsRng.gen(),
  317. content_dc_key: OsRng.gen(),
  318. select_dc_key: OsRng.gen(),
  319. text_dc_key: OsRng.gen(),
  320. cursor_dc_key: OsRng.gen(),
  321. cursor_mesh: SyncMutex::new(None),
  322. is_active,
  323. is_focused,
  324. rect,
  325. baseline,
  326. lineheight: lineheight.clone(),
  327. scroll_speed,
  328. padding,
  329. font_size: font_size.clone(),
  330. text: text.clone(),
  331. text_color: text_color.clone(),
  332. text_hi_color,
  333. //text_cmd_color,
  334. cursor_color,
  335. cursor_width,
  336. cursor_ascent,
  337. cursor_descent,
  338. cursor_blink_time,
  339. cursor_idle_time,
  340. hi_bg_color,
  341. //cmd_bg_color,
  342. select_ascent,
  343. select_descent,
  344. handle_descent,
  345. select_text,
  346. z_index,
  347. priority,
  348. debug,
  349. mouse_btn_held: AtomicBool::new(false),
  350. cursor_is_visible: AtomicBool::new(true),
  351. blink_is_paused: AtomicBool::new(false),
  352. hide_cursor: AtomicBool::new(false),
  353. sel_sender: SyncMutex::new(None),
  354. scroll: scroll.clone(),
  355. touch_info: SyncMutex::new(TouchInfo::new(scroll, behave.scroll_ctrl())),
  356. is_phone_select: AtomicBool::new(false),
  357. window_scale: window_scale.clone(),
  358. parent_rect,
  359. is_mouse_hover: AtomicBool::new(false),
  360. editor,
  361. behave,
  362. });
  363. Pimpl::Edit(self_)
  364. }
  365. fn node(&self) -> SceneNodePtr {
  366. self.node.upgrade().unwrap()
  367. }
  368. fn abs_to_local(&self, point: &mut Point) {
  369. let rect = self.rect.get();
  370. *point -= rect.pos();
  371. *point -= self.behave.inner_pos();
  372. *point -= self.behave.scroll();
  373. }
  374. /// Gets the real cursor pos within the rect.
  375. async fn get_cursor_pos(&self) -> Point {
  376. // This is the position within the content.
  377. let cursor_pos = self.lock_editor().await.get_cursor_pos();
  378. // Apply the inner padding
  379. cursor_pos + self.behave.inner_pos()
  380. }
  381. /// Lazy-initializes the editor and returns a handle to it
  382. async fn lock_editor<'a>(&'a self) -> EditorHandle<'a> {
  383. EditorHandle { guard: self.editor.lock().await }
  384. }
  385. fn regen_cursor_mesh(&self) -> DrawMesh {
  386. let cursor_width = self.cursor_width.get();
  387. let cursor_ascent = self.cursor_ascent.get();
  388. let cursor_descent = self.cursor_descent.get();
  389. let baseline = self.baseline.get();
  390. let cursor_rect = Rectangle {
  391. x: 0.,
  392. y: baseline - cursor_ascent,
  393. w: cursor_width,
  394. h: cursor_ascent + cursor_descent,
  395. };
  396. let cursor_color = self.cursor_color.get();
  397. let mut mesh = MeshBuilder::new(gfxtag!("chatedit_cursor"));
  398. mesh.draw_filled_box(&cursor_rect, cursor_color);
  399. mesh.alloc(&self.render_api).draw_untextured()
  400. }
  401. fn draw_phone_select_handle(&self, mesh: &mut MeshBuilder, mut pos: Point, side: f32) {
  402. let baseline = self.baseline.get();
  403. let select_ascent = self.select_ascent.get();
  404. let handle_descent = self.handle_descent.get();
  405. let color = self.text_hi_color.get();
  406. // Transparent for fade
  407. let mut color_trans = color.clone();
  408. color_trans[3] = 0.;
  409. pos += self.behave.inner_pos();
  410. let x = pos.x;
  411. let mut y = pos.y + baseline;
  412. // We can cache this
  413. // Vertical line downwards. We use this instead of draw_box() so we have a fade.
  414. let verts = vec![
  415. Vertex { pos: [x - side * 1., y - select_ascent], color: color_trans, uv: [0., 0.] },
  416. Vertex { pos: [x + side * 4., y - select_ascent], color: color_trans, uv: [0., 0.] },
  417. Vertex { pos: [x - side * 1., y + handle_descent + 5.], color, uv: [0., 0.] },
  418. Vertex { pos: [x + side * 4., y + handle_descent + 5.], color, uv: [0., 0.] },
  419. ];
  420. let indices = vec![0, 2, 1, 1, 2, 3];
  421. mesh.append(verts, indices);
  422. y += handle_descent;
  423. // The arrow itself.
  424. // Go anti-clockwise
  425. let verts = vec![
  426. Vertex { pos: [x, y], color, uv: [0., 0.] },
  427. Vertex { pos: [x, y + 50.], color, uv: [0., 0.] },
  428. Vertex { pos: [x + side * 30., y + 50.], color, uv: [0., 0.] },
  429. Vertex { pos: [x + side * 50., y + 25.], color, uv: [0., 0.] },
  430. ];
  431. let indices = vec![0, 1, 2, 0, 2, 3];
  432. mesh.append(verts, indices);
  433. }
  434. async fn change_focus(self: Arc<Self>, batch: BatchGuardPtr) {
  435. if !self.is_active.get() {
  436. return
  437. }
  438. t!("Focus changed");
  439. let atom = &mut batch.spawn();
  440. // Cursor visibility will change so just redraw everything lol
  441. self.redraw(atom).await;
  442. }
  443. async fn handle_shortcut(
  444. &self,
  445. key: char,
  446. mods: &KeyMods,
  447. atom: &mut PropertyAtomicGuard,
  448. ) -> bool {
  449. t!("handle_shortcut({:?}, {:?})", key, mods);
  450. #[cfg(not(target_os = "macos"))]
  451. let action_mod = mods.ctrl;
  452. #[cfg(target_os = "macos")]
  453. let action_mod = mods.logo;
  454. match key {
  455. 'a' => {
  456. if action_mod {
  457. let mut txt_ctx = text2::TEXT_CTX.get().await;
  458. let mut editor = self.lock_editor().await;
  459. let mut drv = editor.driver(&mut txt_ctx).unwrap();
  460. drv.select_all();
  461. if let Some(seltext) = editor.selected_text() {
  462. self.select_text.clone().set_str(atom, Role::Internal, 0, seltext).unwrap();
  463. }
  464. }
  465. }
  466. 'c' => {
  467. if action_mod {
  468. let editor = self.lock_editor().await;
  469. if let Some(txt) = editor.selected_text() {
  470. miniquad::window::clipboard_set(&txt);
  471. }
  472. }
  473. }
  474. 'v' => {
  475. if action_mod {
  476. if let Some(txt) = miniquad::window::clipboard_get() {
  477. self.insert(&txt, atom).await;
  478. // Maybe insert should call this?
  479. self.behave.apply_cursor_scroll().await;
  480. }
  481. }
  482. }
  483. _ => return false,
  484. }
  485. self.eval_rect().await;
  486. self.redraw(atom).await;
  487. true
  488. }
  489. async fn handle_key(
  490. &self,
  491. key: &KeyCode,
  492. mods: &KeyMods,
  493. atom: &mut PropertyAtomicGuard,
  494. ) -> bool {
  495. #[cfg(not(target_os = "macos"))]
  496. let action_mod = mods.ctrl;
  497. #[cfg(target_os = "macos")]
  498. let action_mod = mods.logo;
  499. t!("handle_key({:?}, {:?}) action_mod={action_mod}", key, mods);
  500. let mut txt_ctx = text2::TEXT_CTX.get().await;
  501. let mut editor = self.lock_editor().await;
  502. let mut drv = editor.driver(&mut txt_ctx).unwrap();
  503. match key {
  504. KeyCode::Left => {
  505. if action_mod {
  506. if mods.shift {
  507. drv.select_word_left();
  508. } else {
  509. drv.move_word_left();
  510. }
  511. } else if mods.shift {
  512. drv.select_left();
  513. } else {
  514. drv.move_left();
  515. }
  516. }
  517. KeyCode::Right => {
  518. if action_mod {
  519. if mods.shift {
  520. drv.select_word_right();
  521. } else {
  522. drv.move_word_right();
  523. }
  524. } else if mods.shift {
  525. drv.select_right();
  526. } else {
  527. drv.move_right();
  528. }
  529. }
  530. KeyCode::Up => {
  531. if mods.shift {
  532. drv.select_up();
  533. } else {
  534. drv.move_up();
  535. }
  536. }
  537. KeyCode::Down => {
  538. if mods.shift {
  539. drv.select_down();
  540. } else {
  541. drv.move_down();
  542. }
  543. }
  544. KeyCode::Enter | KeyCode::KpEnter => {
  545. if mods.shift {
  546. if self.behave.allow_endl() {
  547. drv.insert_or_replace_selection("\n");
  548. editor.on_buffer_changed(atom).await;
  549. }
  550. } else {
  551. //let node = self.node.upgrade().unwrap();
  552. //node.trigger("enter_pressed", vec![]).await.unwrap();
  553. return false;
  554. }
  555. }
  556. KeyCode::Delete => {
  557. if action_mod {
  558. drv.delete_word();
  559. } else {
  560. drv.delete();
  561. }
  562. editor.on_buffer_changed(atom).await;
  563. }
  564. KeyCode::Backspace => {
  565. if action_mod {
  566. drv.backdelete_word();
  567. } else {
  568. drv.backdelete();
  569. }
  570. editor.on_buffer_changed(atom).await;
  571. }
  572. KeyCode::Home => {
  573. if action_mod {
  574. if mods.shift {
  575. drv.select_to_text_start();
  576. } else {
  577. drv.move_to_text_start();
  578. }
  579. } else if mods.shift {
  580. drv.select_to_line_start();
  581. } else {
  582. drv.move_to_line_start();
  583. }
  584. }
  585. KeyCode::End => {
  586. if action_mod {
  587. if mods.shift {
  588. drv.select_to_text_end();
  589. } else {
  590. drv.move_to_text_end();
  591. }
  592. } else if mods.shift {
  593. drv.select_to_line_end();
  594. } else {
  595. drv.move_to_line_end();
  596. }
  597. }
  598. _ => return false,
  599. }
  600. if let Some(seltext) = editor.selected_text() {
  601. self.select_text.clone().set_str(atom, Role::Internal, 0, seltext).unwrap();
  602. } else {
  603. self.select_text.clone().set_null(atom, Role::Internal, 0).unwrap();
  604. }
  605. drop(editor);
  606. drop(txt_ctx);
  607. self.eval_rect().await;
  608. self.behave.apply_cursor_scroll().await;
  609. self.pause_blinking();
  610. self.redraw(atom).await;
  611. return true
  612. }
  613. /// This will select the entire word rather than move the cursor to that location
  614. async fn start_touch_select(&self, touch_pos: Point, atom: &mut PropertyAtomicGuard) {
  615. t!("start_touch_select({touch_pos:?})");
  616. let mut editor = self.lock_editor().await;
  617. editor.select_word_at_point(touch_pos);
  618. editor.refresh().await;
  619. let seltext = editor.selected_text().unwrap();
  620. d!("Selected {seltext:?} from {touch_pos:?}");
  621. self.select_text.clone().set_str(atom, Role::Internal, 0, seltext).unwrap();
  622. drop(editor);
  623. // if start != end {
  624. t!("is_phone_select = true");
  625. self.is_phone_select.store(true, Ordering::Relaxed);
  626. self.hide_cursor.store(true, Ordering::Relaxed);
  627. // }
  628. }
  629. async fn handle_touch_start(&self, touch_pos: Point) -> bool {
  630. t!("handle_touch_start({touch_pos:?})");
  631. let rect = self.rect.get();
  632. if !rect.contains(touch_pos) {
  633. t!("rect!cont rect={rect:?}, touch_pos={touch_pos:?}");
  634. return false
  635. }
  636. if self.try_handle_drag(touch_pos).await {
  637. return true
  638. }
  639. let mut touch_info = self.touch_info.lock();
  640. touch_info.start(touch_pos);
  641. true
  642. }
  643. async fn get_select_handles(&self, editor: &Editor) -> Option<(Point, Point)> {
  644. let layout = editor.layout();
  645. let sel = editor.selection(1);
  646. if sel.is_collapsed() {
  647. assert!(!self.is_phone_select.load(Ordering::Relaxed));
  648. return None
  649. }
  650. let first = Rectangle::from(sel.anchor().geometry(layout, 0.)).pos();
  651. let last = Rectangle::from(sel.focus().geometry(layout, 0.)).pos();
  652. Some((first, last))
  653. }
  654. async fn try_handle_drag(&self, mut touch_pos: Point) -> bool {
  655. let editor = self.lock_editor().await;
  656. let Some((mut first, mut last)) = self.get_select_handles(&editor).await else {
  657. return false
  658. };
  659. self.abs_to_local(&mut touch_pos);
  660. t!("localize touch_pos = {touch_pos:?}");
  661. let baseline = self.baseline.get();
  662. let handle_off_y = self.handle_descent.get();
  663. first.y += baseline + handle_off_y;
  664. last.y += baseline + handle_off_y;
  665. // Are we within range of either one?
  666. t!("handle center points = ({first:?}, {last:?})");
  667. const TOUCH_RADIUS_SQ: f32 = 10_000.;
  668. let first_dist_sq = first.dist_sq(touch_pos);
  669. let last_dist_sq = last.dist_sq(touch_pos);
  670. let is_first = first_dist_sq <= TOUCH_RADIUS_SQ;
  671. let is_last = last_dist_sq <= TOUCH_RADIUS_SQ;
  672. let mut side = 0;
  673. if is_first && is_last {
  674. // Are we closer to the first or last?
  675. // Break the tie
  676. if first_dist_sq < last_dist_sq {
  677. side = -1;
  678. } else {
  679. side = 1;
  680. }
  681. } else if is_first {
  682. side = -1;
  683. } else if is_last {
  684. side = 1;
  685. }
  686. if side != 0 {
  687. d!("start touch: DragSelectHandle state [side={side}]");
  688. // Set touch_state status to enable begin dragging them
  689. let mut touch_info = self.touch_info.lock();
  690. touch_info.state = TouchStateAction::DragSelectHandle { side };
  691. return true
  692. }
  693. false
  694. }
  695. async fn handle_touch_move(&self, mut touch_pos: Point) -> bool {
  696. if !self.is_active.get() {
  697. return false
  698. }
  699. // We must update with non relative touch_pos bcos when doing vertical scrolling
  700. // we will modify the scroll, which is used by abs_to_local(), which is used
  701. // to then calculate the max scroll. So it ends up jumping around.
  702. // We use the abs touch_pos without scroll adjust applied for vert scrolling.
  703. let touch_state = {
  704. let mut touch_info = self.touch_info.lock();
  705. touch_info.update(&touch_pos);
  706. touch_info.state.clone()
  707. };
  708. //t!("handle_touch_move({touch_pos:?}) touch_state={touch_state:?}");
  709. match &touch_state {
  710. TouchStateAction::Inactive => return false,
  711. TouchStateAction::StartSelect => {
  712. if self.text.get().is_empty() {
  713. self.node().trigger("paste_request", vec![]).await.unwrap();
  714. } else {
  715. self.abs_to_local(&mut touch_pos);
  716. let atom =
  717. &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_touch_move"));
  718. self.start_touch_select(touch_pos, atom).await;
  719. self.redraw_select(atom.batch_id).await;
  720. }
  721. d!("touch state: StartSelect -> Select");
  722. self.touch_info.lock().state = TouchStateAction::Select;
  723. }
  724. TouchStateAction::DragSelectHandle { side } => {
  725. let rect = self.rect.get();
  726. let is_touch_hover = rect.contains(touch_pos);
  727. let sel_sender = self.sel_sender.lock().clone().unwrap();
  728. // Mouse is outside rect?
  729. // If so we gotta scroll it while selecting.
  730. if !is_touch_hover {
  731. // This process will begin selecting text and applying scroll too.
  732. sel_sender.send(Some((touch_pos, Some(*side)))).await.unwrap();
  733. } else {
  734. // Stop any existing select/scroll process
  735. sel_sender.send(None).await.unwrap();
  736. // Mouse is inside so just select the text once and be done.
  737. self.handle_select(touch_pos, Some(*side)).await;
  738. }
  739. }
  740. TouchStateAction::ScrollVert { start_pos, scroll_start } => {
  741. let travel_dist = self.behave.scroll_ctrl().travel(*start_pos, touch_pos);
  742. let mut scroll = scroll_start + travel_dist;
  743. scroll = scroll.clamp(0., self.behave.max_scroll().await);
  744. if (self.scroll.load(Ordering::Relaxed) - scroll).abs() < VERT_SCROLL_UPDATE_INC {
  745. return true
  746. }
  747. self.scroll.store(scroll, Ordering::Release);
  748. let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_touch_move"));
  749. self.redraw_scroll(atom.batch_id).await;
  750. }
  751. TouchStateAction::SetCursorPos => {
  752. // TBH I can't even see the cursor under my thumb so I'll just
  753. // comment this for now.
  754. }
  755. _ => {}
  756. }
  757. true
  758. }
  759. async fn handle_touch_end(&self, atom: &mut PropertyAtomicGuard, mut touch_pos: Point) -> bool {
  760. //t!("handle_touch_end({touch_pos:?})");
  761. self.abs_to_local(&mut touch_pos);
  762. let state = self.touch_info.lock().stop();
  763. match state {
  764. TouchStateAction::Inactive => return false,
  765. TouchStateAction::Started { pos: _, instant: _ } | TouchStateAction::SetCursorPos => {
  766. self.touch_set_cursor_pos(atom, touch_pos).await;
  767. self.redraw(atom).await;
  768. }
  769. _ => {}
  770. }
  771. // Stop any selection scrolling
  772. let scroll_sender = self.sel_sender.lock().clone().unwrap();
  773. scroll_sender.send(None).await.unwrap();
  774. self.node().trigger("focus_request", vec![]).await.unwrap();
  775. true
  776. }
  777. async fn touch_set_cursor_pos(&self, atom: &mut PropertyAtomicGuard, touch_pos: Point) {
  778. t!("touch_set_cursor_pos({touch_pos:?})");
  779. let mut editor = self.lock_editor().await;
  780. editor.move_to_pos(touch_pos);
  781. editor.refresh().await;
  782. drop(editor);
  783. self.pause_blinking();
  784. self.finish_select(atom);
  785. }
  786. fn finish_select(&self, atom: &mut PropertyAtomicGuard) {
  787. self.is_phone_select.store(false, Ordering::Release);
  788. self.hide_cursor.store(false, Ordering::Release);
  789. self.select_text.clone().set_null(atom, Role::Internal, 0).unwrap();
  790. }
  791. async fn handle_select(&self, mouse_pos: Point, side: Option<isize>) {
  792. //t!("handle_select({mouse_pos:?}, {side:?})");
  793. let rect = self.rect.get();
  794. let is_mouse_hover = rect.contains(mouse_pos);
  795. let mut clip_mouse_pos = rect.clip_point(mouse_pos);
  796. let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_mouse_move"));
  797. // Handle scrolling
  798. if !is_mouse_hover {
  799. let scroll_ctrl = self.behave.scroll_ctrl();
  800. // How far is the cursor outside the widget rect
  801. let travel = scroll_ctrl.travel(mouse_pos, clip_mouse_pos);
  802. //t!("select autoscroll travel={travel}");
  803. let max_scroll = self.behave.max_scroll().await;
  804. let delta = travel * SELECT_SCROLL_TRAVEL_SPEED;
  805. let scroll = (self.scroll.load(Ordering::Relaxed) + delta).clamp(0., max_scroll);
  806. self.scroll.store(scroll, Ordering::Release);
  807. self.redraw_scroll(atom.batch_id).await;
  808. }
  809. // Move mouse pos within this widget
  810. self.abs_to_local(&mut clip_mouse_pos);
  811. let mut sel_side = 1;
  812. if let Some(side) = side {
  813. sel_side = side;
  814. }
  815. let seltext = {
  816. let mut editor = self.lock_editor().await;
  817. // The below lines rely on parley driver which android does not use
  818. //let mut txt_ctx = text2::TEXT_CTX.get().await;
  819. //let mut drv = editor.driver(&mut txt_ctx).unwrap();
  820. //drv.extend_selection_to_point(clip_mouse_pos.x, clip_mouse_pos.y);
  821. let layout = editor.layout();
  822. let prev_sel = editor.selection(sel_side);
  823. let sel = prev_sel.extend_to_point(layout, clip_mouse_pos.x, clip_mouse_pos.y);
  824. let (mut prev_start, mut prev_end) =
  825. (prev_sel.anchor().index(), prev_sel.focus().index());
  826. let (mut start, mut end) = (sel.anchor().index(), sel.focus().index());
  827. //t!("handle_select() setting ({start}, {end})");
  828. // When dragging phone handles we prevent the selection crossing over itself.
  829. // Mouse select does not have this limitation.
  830. if let Some(side) = side {
  831. assert!(side.abs() == 1);
  832. assert!(self.is_phone_select.load(Ordering::Relaxed));
  833. // Prevent selection crossing itself
  834. if side == -1 {
  835. let max_end = sel.anchor().previous_visual(layout).index();
  836. end = std::cmp::min(end, max_end);
  837. //t!("handle_select(): LHS set focus {end} before {max_end}");
  838. // When selecting from the LHS these will be swapped so swap them back
  839. swap(&mut start, &mut end);
  840. swap(&mut prev_start, &mut prev_end);
  841. } else {
  842. let min_end = sel.anchor().next_visual(layout).index();
  843. end = std::cmp::max(end, min_end);
  844. //t!("handle_select(): RHS set focus {end} after {min_end}");
  845. }
  846. }
  847. if start == prev_start && end == prev_end {
  848. // No change so just return
  849. //t!("handle_select(): no change early exit");
  850. return
  851. }
  852. #[cfg(target_os = "android")]
  853. assert!(start <= end);
  854. //t!("handle_select(): set_selection({start}, {end})");
  855. editor.set_selection(start, end);
  856. editor.refresh().await;
  857. editor.selected_text()
  858. };
  859. //d!("Select {seltext:?} from {clip_mouse_pos:?} (unclipped: {mouse_pos:?}) to ({sel_start}, {sel_end})");
  860. // Android editor impl detail: selection disappears when anchor == index
  861. #[cfg(target_os = "android")]
  862. {
  863. assert!(sel_start != sel_end);
  864. //if sel_start == sel_end {
  865. // self.finish_select(atom);
  866. //}
  867. }
  868. // Will be None when drag select just started
  869. if let Some(seltext) = seltext {
  870. self.select_text.clone().set_str(atom, Role::Internal, 0, seltext).unwrap();
  871. }
  872. self.pause_blinking();
  873. //self.behave.apply_cursor_scroll().await;
  874. self.redraw_cursor(atom.batch_id).await;
  875. self.redraw_select(atom.batch_id).await;
  876. }
  877. fn pause_blinking(&self) {
  878. self.blink_is_paused.store(true, Ordering::Relaxed);
  879. self.cursor_is_visible.store(true, Ordering::Relaxed);
  880. }
  881. async fn redraw(&self, atom: &mut PropertyAtomicGuard) {
  882. let trace_id = rand::random();
  883. let timest = unixtime();
  884. let draw_update = self.make_draw_calls(trace_id, atom).await;
  885. self.render_api.replace_draw_calls(atom.batch_id, timest, draw_update.draw_calls);
  886. }
  887. /// Called when scroll changes. Moves content up or down. Nothing more.
  888. async fn redraw_scroll(&self, batch_id: BatchGuardId) {
  889. let timest = unixtime();
  890. let rect = self.rect.get();
  891. let mut content_instrs = vec![DrawInstruction::ApplyView(rect.with_zero_pos())];
  892. let mut bg_instrs = self.regen_bg_mesh();
  893. content_instrs.append(&mut bg_instrs);
  894. content_instrs.push(DrawInstruction::Move(self.behave.scroll()));
  895. let draw_main = vec![(
  896. self.content_dc_key,
  897. DrawCall::new(
  898. content_instrs,
  899. vec![
  900. self.text_dc_key,
  901. self.phone_select_handle_dc_key,
  902. self.cursor_dc_key,
  903. self.select_dc_key,
  904. ],
  905. 0,
  906. "chatedit_content",
  907. ),
  908. )];
  909. self.render_api.replace_draw_calls(batch_id, timest, draw_main);
  910. }
  911. async fn redraw_cursor(&self, batch_id: BatchGuardId) {
  912. let timest = unixtime();
  913. let instrs = self.get_cursor_instrs().await;
  914. let draw_calls = vec![(self.cursor_dc_key, DrawCall::new(instrs, vec![], 2, "curs_redr"))];
  915. self.render_api.replace_draw_calls(batch_id, timest, draw_calls);
  916. }
  917. async fn redraw_select(&self, batch_id: BatchGuardId) {
  918. let timest = unixtime();
  919. let sel_instrs = self.regen_select_mesh().await;
  920. let phone_sel_instrs = self.regen_phone_select_handle_mesh().await;
  921. let draw_calls = vec![
  922. (self.select_dc_key, DrawCall::new(sel_instrs, vec![], 0, "chatedit_sel")),
  923. (
  924. self.phone_select_handle_dc_key,
  925. DrawCall::new(phone_sel_instrs, vec![], 1, "chatedit_phone_sel_redraw_sel"),
  926. ),
  927. ];
  928. self.render_api.replace_draw_calls(batch_id, timest, draw_calls);
  929. }
  930. async fn get_cursor_instrs(&self) -> Vec<DrawInstruction> {
  931. if !self.is_focused.get() ||
  932. !self.cursor_is_visible.load(Ordering::Relaxed) ||
  933. self.hide_cursor.load(Ordering::Relaxed)
  934. {
  935. return vec![]
  936. }
  937. let cursor_mesh =
  938. self.cursor_mesh.lock().get_or_insert_with(|| self.regen_cursor_mesh()).clone();
  939. vec![DrawInstruction::Move(self.get_cursor_pos().await), DrawInstruction::Draw(cursor_mesh)]
  940. }
  941. fn regen_bg_mesh(&self) -> Vec<DrawInstruction> {
  942. if !self.debug.get() {
  943. return vec![]
  944. }
  945. let mut rect = self.rect.get().with_zero_pos();
  946. let mut mesh = MeshBuilder::new(gfxtag!("chatedit_bg"));
  947. mesh.draw_outline(&rect, [0., 1., 0., 1.], 1.);
  948. let pad_top = self.padding.get_f32(0).unwrap();
  949. let pad_right = self.padding.get_f32(1).unwrap();
  950. let pad_bottom = self.padding.get_f32(2).unwrap();
  951. let pad_left = self.padding.get_f32(3).unwrap();
  952. rect.x = pad_left;
  953. rect.y = pad_top;
  954. rect.w -= pad_left + pad_right;
  955. rect.h -= pad_top + pad_bottom;
  956. mesh.draw_outline(&rect, [0., 1., 0., 0.5], 1.);
  957. vec![DrawInstruction::Draw(mesh.alloc(&self.render_api).draw_untextured())]
  958. }
  959. async fn regen_txt_mesh(&self) -> Vec<DrawInstruction> {
  960. let mut instrs = vec![DrawInstruction::Move(self.behave.inner_pos())];
  961. let editor = self.lock_editor().await;
  962. let layout = editor.layout();
  963. let mut render_instrs =
  964. text2::render_layout(layout, &self.render_api, gfxtag!("chatedit_txt_mesh"));
  965. instrs.append(&mut render_instrs);
  966. instrs
  967. }
  968. async fn regen_select_mesh(&self) -> Vec<DrawInstruction> {
  969. let mut instrs = vec![DrawInstruction::Move(self.behave.inner_pos())];
  970. let editor = self.lock_editor().await;
  971. let layout = editor.layout();
  972. let sel = editor.selection(1);
  973. let sel_color = self.hi_bg_color.get();
  974. if !sel.is_collapsed() {
  975. let mut mesh = MeshBuilder::new(gfxtag!("chatedit_select_mesh"));
  976. sel.geometry_with(layout, |rect: parley::BoundingBox, _| {
  977. mesh.draw_filled_box(&rect.into(), sel_color);
  978. });
  979. instrs.push(DrawInstruction::Draw(mesh.alloc(&self.render_api).draw_untextured()));
  980. }
  981. instrs
  982. }
  983. async fn regen_phone_select_handle_mesh(&self) -> Vec<DrawInstruction> {
  984. if !self.is_phone_select.load(Ordering::Acquire) {
  985. //t!("regen_phone_select_handle_mesh() [DISABLED]");
  986. return vec![]
  987. }
  988. let editor = self.lock_editor().await;
  989. let (first, last) = self.get_select_handles(&editor).await.unwrap();
  990. let sel = editor.selection(1);
  991. assert!(!sel.is_collapsed());
  992. // We could cache this and use Move instead but why bother?
  993. let mut mesh = MeshBuilder::new(gfxtag!("chatedit_phone_select_handle"));
  994. self.draw_phone_select_handle(&mut mesh, first, -1.);
  995. self.draw_phone_select_handle(&mut mesh, last, 1.);
  996. vec![DrawInstruction::Draw(mesh.alloc(&self.render_api).draw_untextured())]
  997. }
  998. /// This does not make use of an atom since we want to de-atomize updating this widget from
  999. /// its dependencies so theres zero latency when typing.
  1000. /// Should be called when text contents changes.
  1001. async fn eval_rect(&self) {
  1002. let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::make_draw_calls"));
  1003. self.behave.eval_rect(atom).await;
  1004. }
  1005. async fn make_draw_calls(&self, _trace_id: u32, atom: &mut PropertyAtomicGuard) -> DrawUpdate {
  1006. let rect = self.rect.get();
  1007. let cursor_instrs = self.get_cursor_instrs().await;
  1008. let txt_instrs = self.regen_txt_mesh().await;
  1009. let sel_instrs = self.regen_select_mesh().await;
  1010. let phone_sel_instrs = self.regen_phone_select_handle_mesh().await;
  1011. let mut content_instrs = vec![DrawInstruction::ApplyView(rect.with_zero_pos())];
  1012. let mut bg_instrs = self.regen_bg_mesh();
  1013. content_instrs.append(&mut bg_instrs);
  1014. content_instrs.push(DrawInstruction::Move(self.behave.scroll()));
  1015. // + root (move)
  1016. // -+ content (apply view)
  1017. // └╴select
  1018. // └╴text
  1019. // └╴phone_handle
  1020. // └╴cursor
  1021. // Why do we have such a complicated layout?
  1022. // When adjusting selection, it's slow to redraw everything, so the selection
  1023. // must be drawn separately.
  1024. // Lastly the cursor is blinking and that's on top but with clipping.
  1025. DrawUpdate {
  1026. key: self.root_dc_key,
  1027. draw_calls: vec![
  1028. (
  1029. self.root_dc_key,
  1030. DrawCall::new(
  1031. vec![DrawInstruction::Move(rect.pos())],
  1032. vec![self.content_dc_key],
  1033. self.z_index.get(),
  1034. "chatedit_root",
  1035. ),
  1036. ),
  1037. (
  1038. self.content_dc_key,
  1039. DrawCall::new(
  1040. content_instrs,
  1041. vec![
  1042. self.text_dc_key,
  1043. self.phone_select_handle_dc_key,
  1044. self.cursor_dc_key,
  1045. self.select_dc_key,
  1046. ],
  1047. 0,
  1048. "chatedit_content",
  1049. ),
  1050. ),
  1051. (self.select_dc_key, DrawCall::new(sel_instrs, vec![], 0, "chatedit_sel")),
  1052. (self.text_dc_key, DrawCall::new(txt_instrs, vec![], 1, "chatedit_text")),
  1053. (self.cursor_dc_key, DrawCall::new(cursor_instrs, vec![], 2, "chatedit_curs")),
  1054. (
  1055. self.phone_select_handle_dc_key,
  1056. DrawCall::new(phone_sel_instrs, vec![], 1, "chatedit_phone_sel"),
  1057. ),
  1058. ],
  1059. }
  1060. }
  1061. async fn insert(&self, txt: &str, atom: &mut PropertyAtomicGuard) {
  1062. let mut editor = self.lock_editor().await;
  1063. editor.insert(txt, atom).await;
  1064. }
  1065. async fn process_insert_text_method(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  1066. let Ok(method_call) = sub.receive().await else {
  1067. debug!(target: "ui::chatedit", "Event relayer closed");
  1068. return false
  1069. };
  1070. t!("method called: insert_line({method_call:?})");
  1071. assert!(method_call.send_res.is_none());
  1072. fn decode_data(data: &[u8]) -> std::io::Result<String> {
  1073. let mut cur = Cursor::new(&data);
  1074. let text = String::decode(&mut cur)?;
  1075. Ok(text)
  1076. }
  1077. let Ok(text) = decode_data(&method_call.data) else {
  1078. error!(target: "ui::chatedit", "insert_text() method invalid arg data");
  1079. return true
  1080. };
  1081. let Some(self_) = me.upgrade() else {
  1082. // Should not happen
  1083. panic!("self destroyed before insert_text_method_task was stopped!");
  1084. };
  1085. let atom =
  1086. &mut self_.render_api.make_guard(gfxtag!("BaseEdit::process_insert_text_method"));
  1087. self_.insert(&text, atom).await;
  1088. self_.eval_rect().await;
  1089. self_.redraw(atom).await;
  1090. true
  1091. }
  1092. async fn process_focus_method(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  1093. let Ok(method_call) = sub.receive().await else {
  1094. debug!(target: "ui::chatedit", "Event relayer closed");
  1095. return false
  1096. };
  1097. t!("method called: focus({method_call:?})");
  1098. assert!(method_call.send_res.is_none());
  1099. assert!(method_call.data.is_empty());
  1100. let Some(self_) = me.upgrade() else {
  1101. // Should not happen
  1102. panic!("self destroyed before insert_text_method_task was stopped!");
  1103. };
  1104. let editor = self_.lock_editor().await;
  1105. editor.focus();
  1106. true
  1107. }
  1108. async fn process_unfocus_method(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  1109. let Ok(method_call) = sub.receive().await else {
  1110. debug!(target: "ui::chatedit", "Event relayer closed");
  1111. return false
  1112. };
  1113. t!("method called: focus({method_call:?})");
  1114. assert!(method_call.send_res.is_none());
  1115. assert!(method_call.data.is_empty());
  1116. let Some(self_) = me.upgrade() else {
  1117. // Should not happen
  1118. panic!("self destroyed before insert_text_method_task was stopped!");
  1119. };
  1120. let editor = self_.lock_editor().await;
  1121. editor.unfocus();
  1122. true
  1123. }
  1124. #[cfg(target_os = "android")]
  1125. async fn handle_android_event(&self, ev: AndroidSuggestEvent) {
  1126. if !self.is_active.get() {
  1127. return
  1128. }
  1129. t!("handle_android_event({ev:?})");
  1130. let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_android_event"));
  1131. match ev {
  1132. AndroidSuggestEvent::Init => {
  1133. let mut editor = self.lock_editor().await;
  1134. editor.init();
  1135. // For debugging select, enable these and set a selection in the editor.
  1136. //self.is_phone_select.store(true, Ordering::Relaxed);
  1137. //self.hide_cursor.store(true, Ordering::Relaxed);
  1138. // Debug code if we set text in editor.init()
  1139. //editor.on_buffer_changed(&mut PropertyAtomicGuard::none()).await;
  1140. return
  1141. }
  1142. AndroidSuggestEvent::CreateInputConnect => {
  1143. let mut editor = self.lock_editor().await;
  1144. editor.setup();
  1145. }
  1146. // Destructive text edits
  1147. AndroidSuggestEvent::ComposeRegion { .. } |
  1148. AndroidSuggestEvent::Compose { .. } |
  1149. AndroidSuggestEvent::DeleteSurroundingText { .. } => {
  1150. // Any editing will collapse selections
  1151. self.finish_select(atom);
  1152. let mut editor = self.lock_editor().await;
  1153. editor.on_buffer_changed(atom).await;
  1154. drop(editor);
  1155. self.eval_rect().await;
  1156. self.behave.apply_cursor_scroll().await;
  1157. }
  1158. AndroidSuggestEvent::FinishCompose => {
  1159. let mut editor = self.lock_editor().await;
  1160. editor.on_buffer_changed(atom).await;
  1161. }
  1162. }
  1163. // Only redraw once we have the parent_rect
  1164. // Can happen when we receive an Android event before the canvas is ready
  1165. if self.parent_rect.lock().is_some() {
  1166. self.redraw(atom).await;
  1167. }
  1168. }
  1169. }
  1170. impl Drop for BaseEdit {
  1171. fn drop(&mut self) {
  1172. let atom = self.render_api.make_guard(gfxtag!("BaseEdit::drop"));
  1173. self.render_api.replace_draw_calls(
  1174. atom.batch_id,
  1175. unixtime(),
  1176. vec![(self.text_dc_key, Default::default())],
  1177. );
  1178. }
  1179. }
  1180. #[async_trait]
  1181. impl UIObject for BaseEdit {
  1182. fn priority(&self) -> u32 {
  1183. self.priority.get()
  1184. }
  1185. fn init(&self) {
  1186. let mut guard = self.editor.lock_blocking();
  1187. assert!(guard.is_none());
  1188. *guard = Some(Editor::new(
  1189. self.text.clone(),
  1190. self.font_size.clone(),
  1191. self.text_color.clone(),
  1192. self.window_scale.clone(),
  1193. self.lineheight.clone(),
  1194. ));
  1195. }
  1196. async fn start(self: Arc<Self>, ex: ExecutorPtr) {
  1197. let me = Arc::downgrade(&self);
  1198. let node_ref = &self.node.upgrade().unwrap();
  1199. let method_sub = node_ref.subscribe_method_call("insert_text").unwrap();
  1200. let me2 = me.clone();
  1201. let insert_text_task =
  1202. ex.spawn(
  1203. async move { while Self::process_insert_text_method(&me2, &method_sub).await {} },
  1204. );
  1205. let method_sub = node_ref.subscribe_method_call("focus").unwrap();
  1206. let me2 = me.clone();
  1207. let focus_task =
  1208. ex.spawn(async move { while Self::process_focus_method(&me2, &method_sub).await {} });
  1209. let method_sub = node_ref.subscribe_method_call("unfocus").unwrap();
  1210. let me2 = me.clone();
  1211. let unfocus_task =
  1212. ex.spawn(async move { while Self::process_unfocus_method(&me2, &method_sub).await {} });
  1213. let mut on_modify = OnModify::new(ex.clone(), self.node.clone(), me.clone());
  1214. on_modify.when_change(self.is_focused.prop(), Self::change_focus);
  1215. // When text has been changed.
  1216. // Cursor and selection might be invalidated.
  1217. async fn reset(self_: Arc<BaseEdit>, batch: BatchGuardPtr) {
  1218. let atom = &mut batch.spawn();
  1219. //self_.select_text.set_null(Role::Internal, 0).unwrap();
  1220. self_.scroll.store(0., Ordering::Release);
  1221. self_.eval_rect().await;
  1222. self_.redraw(atom).await;
  1223. }
  1224. async fn redraw(self_: Arc<BaseEdit>, batch: BatchGuardPtr) {
  1225. let atom = &mut batch.spawn();
  1226. self_.eval_rect().await;
  1227. self_.redraw(atom).await;
  1228. }
  1229. async fn set_text(self_: Arc<BaseEdit>, batch: BatchGuardPtr) {
  1230. self_.lock_editor().await.on_text_prop_changed().await;
  1231. let atom = &mut batch.spawn();
  1232. self_.eval_rect().await;
  1233. self_.redraw(atom).await;
  1234. }
  1235. on_modify.when_change(self.rect.prop(), redraw);
  1236. on_modify.when_change(self.baseline.prop(), redraw);
  1237. on_modify.when_change(self.lineheight.prop(), redraw);
  1238. on_modify.when_change(self.select_ascent.prop(), redraw);
  1239. on_modify.when_change(self.select_descent.prop(), redraw);
  1240. on_modify.when_change(self.handle_descent.prop(), redraw);
  1241. on_modify.when_change(self.padding.clone(), redraw);
  1242. on_modify.when_change(self.text.prop(), set_text);
  1243. // The commented properties are modified on input events
  1244. // So then redraw() will get repeatedly triggered when these properties
  1245. // are changed. We should find a solution. For now the hooks are disabled.
  1246. //on_modify.when_change(scroll.prop(), redraw);
  1247. //on_modify.when_change(cursor_pos.prop(), redraw);
  1248. on_modify.when_change(self.font_size.prop(), redraw);
  1249. on_modify.when_change(self.text.prop(), reset);
  1250. on_modify.when_change(self.text_color.prop(), redraw);
  1251. on_modify.when_change(self.hi_bg_color.prop(), redraw);
  1252. //on_modify.when_change(selected.clone(), redraw);
  1253. on_modify.when_change(self.z_index.prop(), redraw);
  1254. on_modify.when_change(self.debug.prop(), redraw);
  1255. async fn regen_cursor(self_: Arc<BaseEdit>, _batch: BatchGuardPtr) {
  1256. // Free the cache
  1257. *self_.cursor_mesh.lock() = None;
  1258. }
  1259. on_modify.when_change(self.cursor_color.prop(), regen_cursor);
  1260. on_modify.when_change(self.cursor_ascent.prop(), regen_cursor);
  1261. on_modify.when_change(self.cursor_descent.prop(), regen_cursor);
  1262. on_modify.when_change(self.cursor_width.prop(), regen_cursor);
  1263. let me2 = me.clone();
  1264. let cursor_blink_time = self.cursor_blink_time.clone();
  1265. let cursor_idle_time = self.cursor_idle_time.clone();
  1266. let blinking_cursor_task = ex.spawn(async move {
  1267. loop {
  1268. msleep(cursor_blink_time.get() as u64).await;
  1269. let self_ = me2.upgrade().unwrap();
  1270. if self_.blink_is_paused.swap(false, Ordering::Relaxed) {
  1271. msleep(cursor_idle_time.get() as u64).await;
  1272. continue
  1273. }
  1274. if !self_.rect.has_cached() {
  1275. continue
  1276. }
  1277. // Invert the bool
  1278. self_.cursor_is_visible.fetch_not(Ordering::Relaxed);
  1279. let atom = &mut self_.render_api.make_guard(gfxtag!("BaseEdit::start"));
  1280. self_.redraw_cursor(atom.batch_id).await;
  1281. }
  1282. });
  1283. let (sel_sender, sel_recvr) = async_channel::unbounded();
  1284. *self.sel_sender.lock() = Some(sel_sender);
  1285. let me2 = me.clone();
  1286. // We don't get continuous mouse move events. Instead this task is used to smoothly
  1287. // scroll when selecting text.
  1288. let sel_task = ex.spawn(async move {
  1289. let mut scroll_stat = None;
  1290. // Too much code duplication here but I didn't find a solution that looks any cleaner.
  1291. loop {
  1292. if scroll_stat.is_some() {
  1293. futures::select! {
  1294. rcv = sel_recvr.recv().fuse() => {
  1295. scroll_stat = rcv.unwrap();
  1296. if let Some((mouse_pos, side)) = scroll_stat {
  1297. let self_ = me2.upgrade().unwrap();
  1298. //t!("select task interrupt: {mouse_pos:?} (side={side:?})");
  1299. self_.handle_select(mouse_pos, side).await;
  1300. };
  1301. }
  1302. _ = msleep(SELECT_TASK_UPDATE_TIME).fuse() => {
  1303. if let Some((mouse_pos, side)) = scroll_stat {
  1304. let self_ = me2.upgrade().unwrap();
  1305. //t!("select task update: {mouse_pos:?} (side={side:?})");
  1306. self_.handle_select(mouse_pos, side).await;
  1307. };
  1308. }
  1309. }
  1310. } else {
  1311. scroll_stat = sel_recvr.recv().await.unwrap();
  1312. if let Some((mouse_pos, side)) = scroll_stat {
  1313. let self_ = me2.upgrade().unwrap();
  1314. //t!("select task wake up: {mouse_pos:?} (side={side:?})");
  1315. self_.handle_select(mouse_pos, side).await;
  1316. };
  1317. }
  1318. }
  1319. });
  1320. let mut tasks =
  1321. vec![insert_text_task, focus_task, unfocus_task, blinking_cursor_task, sel_task];
  1322. tasks.append(&mut on_modify.tasks);
  1323. #[cfg(target_os = "android")]
  1324. {
  1325. let recvr = self.lock_editor().await.recvr.clone();
  1326. let me2 = me.clone();
  1327. let autosuggest_task = ex.spawn(async move {
  1328. loop {
  1329. let Ok(ev) = recvr.recv().await else {
  1330. t!("Event relayer closed");
  1331. break
  1332. };
  1333. let Some(self_) = me2.upgrade() else {
  1334. // Should not happen
  1335. panic!("self destroyed before autosuggest_task was stopped!");
  1336. };
  1337. self_.handle_android_event(ev).await;
  1338. }
  1339. });
  1340. tasks.push(autosuggest_task);
  1341. }
  1342. *self.tasks.lock() = tasks;
  1343. }
  1344. fn stop(&self) {
  1345. self.tasks.lock().clear();
  1346. *self.parent_rect.lock() = None;
  1347. self.key_repeat.lock().clear();
  1348. *self.cursor_mesh.lock() = None;
  1349. *self.editor.lock_blocking() = None;
  1350. }
  1351. async fn draw(
  1352. &self,
  1353. parent_rect: Rectangle,
  1354. trace_id: u32,
  1355. atom: &mut PropertyAtomicGuard,
  1356. ) -> Option<DrawUpdate> {
  1357. t!("BaseEdit::draw({:?}, {trace_id})", self.node());
  1358. *self.parent_rect.lock() = Some(parent_rect);
  1359. self.eval_rect().await;
  1360. Some(self.make_draw_calls(trace_id, atom).await)
  1361. }
  1362. async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) -> bool {
  1363. t!("handle_char({key}, {mods:?}, {repeat})");
  1364. // First filter for only single digit keys
  1365. if DISALLOWED_CHARS.contains(&key) {
  1366. return false
  1367. }
  1368. if !self.is_focused.get() {
  1369. return false
  1370. }
  1371. // Must be updated before checking the mods. You can press ctrl+a, then release ctrl
  1372. // before a is released. Then the repeater never gets reset, and uses any old value
  1373. // it has from before for a.
  1374. let actions = {
  1375. let mut repeater = self.key_repeat.lock();
  1376. repeater.key_down(PressedKey::Char(key), repeat)
  1377. };
  1378. let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_char"));
  1379. if mods.ctrl || mods.alt || mods.logo {
  1380. if repeat {
  1381. return false
  1382. }
  1383. return self.handle_shortcut(key, &mods, atom).await
  1384. }
  1385. // Do nothing
  1386. if actions == 0 {
  1387. return true
  1388. }
  1389. t!("Key {:?} has {} actions", key, actions);
  1390. let key_str = key.to_string().repeat(actions as usize);
  1391. self.insert(&key_str, atom).await;
  1392. self.eval_rect().await;
  1393. self.behave.apply_cursor_scroll().await;
  1394. self.redraw(atom).await;
  1395. true
  1396. }
  1397. async fn handle_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
  1398. t!("handle_key_down({key:?}, {mods:?}, {repeat})");
  1399. // First filter for only single digit keys
  1400. // Avoid processing events handled by handle_char()
  1401. if !ALLOWED_KEYCODES.contains(&key) {
  1402. return false
  1403. }
  1404. if !self.is_focused.get() {
  1405. return false
  1406. }
  1407. let actions = {
  1408. let mut repeater = self.key_repeat.lock();
  1409. repeater.key_down(PressedKey::Key(key), repeat)
  1410. };
  1411. // Suppress noisy message
  1412. if actions > 0 {
  1413. t!("Key {:?} has {} actions", key, actions);
  1414. }
  1415. let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_key_down"));
  1416. let mut is_handled = false;
  1417. for _ in 0..actions {
  1418. if self.handle_key(&key, &mods, atom).await {
  1419. is_handled = true;
  1420. }
  1421. }
  1422. is_handled
  1423. }
  1424. async fn handle_mouse_btn_down(&self, btn: MouseButton, mut mouse_pos: Point) -> bool {
  1425. if !self.is_active.get() {
  1426. return false
  1427. }
  1428. let rect = self.rect.get();
  1429. if btn != MouseButton::Left {
  1430. if btn == MouseButton::Right && rect.contains(mouse_pos) {
  1431. if self.text.get().is_empty() {
  1432. self.node().trigger("paste_request", vec![]).await.unwrap();
  1433. }
  1434. return true
  1435. }
  1436. return false
  1437. }
  1438. if !rect.contains(mouse_pos) {
  1439. return false
  1440. }
  1441. let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_mouse_btn_down"));
  1442. // clicking inside box will:
  1443. // 1. make it active
  1444. // 2. begin selection
  1445. if self.is_focused.get() {
  1446. d!("BaseEdit clicked");
  1447. } else {
  1448. d!("BaseEdit focused");
  1449. self.is_focused.set(atom, true);
  1450. }
  1451. // Move mouse pos within this widget
  1452. self.abs_to_local(&mut mouse_pos);
  1453. {
  1454. let mut txt_ctx = text2::TEXT_CTX.get().await;
  1455. let mut editor = self.lock_editor().await;
  1456. let mut drv = editor.driver(&mut txt_ctx).unwrap();
  1457. drv.move_to_point(mouse_pos.x, mouse_pos.y);
  1458. }
  1459. if !self.select_text.is_null(0).unwrap() {
  1460. self.select_text.clone().set_null(atom, Role::Internal, 0).unwrap();
  1461. }
  1462. self.mouse_btn_held.store(true, Ordering::Relaxed);
  1463. self.pause_blinking();
  1464. self.eval_rect().await;
  1465. self.redraw(atom).await;
  1466. true
  1467. }
  1468. async fn handle_mouse_btn_up(&self, _btn: MouseButton, _mouse_pos: Point) -> bool {
  1469. if !self.is_active.get() {
  1470. return false
  1471. }
  1472. // Stop any selection scrolling
  1473. let scroll_sender = self.sel_sender.lock().clone().unwrap();
  1474. scroll_sender.send(None).await.unwrap();
  1475. // releasing mouse button will end selection
  1476. self.mouse_btn_held.store(false, Ordering::Relaxed);
  1477. false
  1478. }
  1479. async fn handle_mouse_move(&self, mouse_pos: Point) -> bool {
  1480. if !self.is_active.get() {
  1481. return false
  1482. }
  1483. let rect = self.rect.get();
  1484. let is_mouse_hover = rect.contains(mouse_pos);
  1485. self.is_mouse_hover.store(is_mouse_hover, Ordering::Relaxed);
  1486. if !self.mouse_btn_held.load(Ordering::Relaxed) {
  1487. return false
  1488. }
  1489. let sel_sender = self.sel_sender.lock().clone().unwrap();
  1490. // Mouse is outside rect?
  1491. // If so we gotta scroll it while selecting.
  1492. if !is_mouse_hover {
  1493. // This process will begin selecting text and applying scroll too.
  1494. sel_sender.send(Some((mouse_pos, None))).await.unwrap();
  1495. } else {
  1496. // Stop any existing select/scroll process
  1497. sel_sender.send(None).await.unwrap();
  1498. // Mouse is inside so just select the text once and be done.
  1499. self.handle_select(mouse_pos, None).await;
  1500. }
  1501. true
  1502. }
  1503. async fn handle_mouse_wheel(&self, wheel_pos: Point) -> bool {
  1504. if !self.is_mouse_hover.load(Ordering::Relaxed) {
  1505. return false
  1506. }
  1507. let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_mouse_wheel"));
  1508. let mut scroll =
  1509. self.scroll.load(Ordering::Relaxed) - wheel_pos.y * self.scroll_speed.get();
  1510. scroll = scroll.clamp(0., self.behave.max_scroll().await);
  1511. t!("handle_mouse_wheel({wheel_pos:?}) [scroll={scroll}]");
  1512. self.scroll.store(scroll, Ordering::Release);
  1513. self.redraw_scroll(atom.batch_id).await;
  1514. true
  1515. }
  1516. async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_pos: Point) -> bool {
  1517. if !self.is_active.get() {
  1518. return false
  1519. }
  1520. // Ignore multi-touch
  1521. if id != 0 {
  1522. return false
  1523. }
  1524. let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_touch"));
  1525. match phase {
  1526. TouchPhase::Started => self.handle_touch_start(touch_pos).await,
  1527. TouchPhase::Moved => self.handle_touch_move(touch_pos).await,
  1528. TouchPhase::Ended => self.handle_touch_end(atom, touch_pos).await,
  1529. TouchPhase::Cancelled => false,
  1530. }
  1531. }
  1532. }