chatedit.rs 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664
  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::{deserialize, Decodable, Encodable, SerialDecodable, SerialEncodable};
  23. use miniquad::{window, KeyCode, KeyMods, MouseButton, TouchPhase};
  24. use parking_lot::Mutex as SyncMutex;
  25. use rand::{rngs::OsRng, Rng};
  26. use std::{
  27. collections::HashMap,
  28. io::Cursor,
  29. sync::{
  30. atomic::{AtomicBool, Ordering},
  31. Arc, OnceLock, Weak,
  32. },
  33. time::Instant,
  34. };
  35. use crate::{
  36. error::Result,
  37. gfx::{
  38. GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, GraphicsEventPublisherPtr,
  39. Point, Rectangle, RenderApi, Vertex,
  40. },
  41. mesh::{Color, MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_RED, COLOR_WHITE},
  42. prop::{
  43. PropertyAtomicGuard, PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr,
  44. PropertyRect, PropertyStr, PropertyUint32, Role,
  45. },
  46. pubsub::Subscription,
  47. scene::{MethodCallSub, Pimpl, SceneNodePtr, SceneNodeWeak},
  48. text::{self, Glyph, GlyphPositionIter, TextShaperPtr},
  49. text2::{self, Editor},
  50. util::{enumerate_ref, is_whitespace, min_f32, unixtime, zip4},
  51. AndroidSuggestEvent, ExecutorPtr,
  52. };
  53. use super::{
  54. editbox::{
  55. editable::{Editable, RenderedEditable, Selection, TextIdx, TextPos},
  56. eol_nudge,
  57. repeat::{PressedKey, PressedKeysSmoothRepeat},
  58. ALLOWED_KEYCODES, DISALLOWED_CHARS,
  59. },
  60. DrawUpdate, OnModify, UIObject,
  61. };
  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. macro_rules! d { ($($arg:tt)*) => { debug!(target: "ui::chatedit", $($arg)*); } }
  66. macro_rules! t { ($($arg:tt)*) => { trace!(target: "ui::chatedit", $($arg)*); } }
  67. // You must be careful working with string indexes in Java. They are UTF16 string indexs, not UTF8
  68. fn char16_to_byte_index(s: &str, char_idx: usize) -> Option<usize> {
  69. let utf16_data: Vec<_> = s.encode_utf16().take(char_idx).collect();
  70. let prestr = String::from_utf16(&utf16_data).ok()?;
  71. Some(prestr.len())
  72. }
  73. fn byte_to_char16_index(s: &str, byte_idx: usize) -> Option<usize> {
  74. if byte_idx > s.len() || !s.is_char_boundary(byte_idx) {
  75. return None
  76. }
  77. Some(s[..byte_idx].encode_utf16().count())
  78. }
  79. #[derive(Clone)]
  80. struct TouchStartInfo {
  81. pos: Point,
  82. instant: std::time::Instant,
  83. }
  84. impl TouchStartInfo {
  85. fn new(pos: Point) -> Self {
  86. Self { pos, instant: std::time::Instant::now() }
  87. }
  88. }
  89. #[derive(Clone)]
  90. enum TouchStateAction {
  91. Inactive,
  92. Started { pos: Point, instant: std::time::Instant },
  93. StartSelect,
  94. Select,
  95. DragSelectHandle { side: isize },
  96. ScrollVert { start_pos: Point, scroll_start: f32 },
  97. SetCursorPos,
  98. }
  99. struct TouchInfo {
  100. state: TouchStateAction,
  101. start: Option<TouchStartInfo>,
  102. scroll: PropertyFloat32,
  103. }
  104. impl TouchInfo {
  105. fn new(scroll: PropertyFloat32) -> Self {
  106. Self { state: TouchStateAction::Inactive, start: None, scroll }
  107. }
  108. fn start(&mut self, pos: Point) {
  109. debug!(target: "ui::chatedit::touch", "start touch: Started state");
  110. self.state = TouchStateAction::Started { pos, instant: std::time::Instant::now() };
  111. }
  112. fn stop(&mut self) -> TouchStateAction {
  113. debug!(target: "ui::chatedit::touch", "stop touch: Inactive state");
  114. std::mem::replace(&mut self.state, TouchStateAction::Inactive)
  115. }
  116. fn update(&mut self, pos: &Point) {
  117. match &self.state {
  118. TouchStateAction::Started { pos: start_pos, instant } => {
  119. let travel_dist = pos.dist_sq(*start_pos);
  120. let grad = (pos.y - start_pos.y) / (pos.x - start_pos.x);
  121. let elapsed = instant.elapsed().as_millis();
  122. //debug!(target: "ui::chatedit::touch", "TouchInfo::update() [travel_dist={travel_dist}, grad={grad}]");
  123. if travel_dist < 5. {
  124. if elapsed > 1000 {
  125. debug!(target: "ui::chatedit::touch", "update touch state: Started -> StartSelect");
  126. self.state = TouchStateAction::StartSelect;
  127. }
  128. } else if grad.abs() > 0.5 {
  129. // Vertical movement
  130. debug!(target: "ui::chatedit::touch", "update touch state: Started -> ScrollVert");
  131. let scroll_start = self.scroll.get();
  132. self.state =
  133. TouchStateAction::ScrollVert { start_pos: *start_pos, scroll_start };
  134. } else {
  135. // Horizontal movement
  136. debug!(target: "ui::chatedit::touch", "update touch state: Started -> SetCursorPos");
  137. self.state = TouchStateAction::SetCursorPos;
  138. }
  139. }
  140. _ => {}
  141. }
  142. }
  143. }
  144. /*
  145. impl std::fmt::Debug for Editor {
  146. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  147. let mut changes = vec![];
  148. let sel = self.editor.raw_selection();
  149. if sel.is_collapsed() {
  150. let cursor = sel.focus().index();
  151. changes.push((cursor, '|'));
  152. } else {
  153. let sel = sel.text_range();
  154. changes.push((sel.start, '{'));
  155. changes.push((sel.end, '}'));
  156. }
  157. if let Some(compose) = self.editor.compose() {
  158. changes.push((compose.start, '['));
  159. changes.push((compose.end, ']'));
  160. }
  161. changes.sort_by(|a, b| b.0.cmp(&a.0));
  162. write!(f, "'")?;
  163. let mut buffer = self.editor.raw_text();
  164. for (byte_idx, c) in buffer.char_indices() {
  165. while let Some((idx, d)) = changes.last() {
  166. if *idx > byte_idx {
  167. break
  168. }
  169. write!(f, "{}", d)?;
  170. let _ = changes.pop();
  171. }
  172. write!(f, "{}", c)?;
  173. }
  174. write!(f, "'")
  175. }
  176. }
  177. */
  178. enum ColoringState {
  179. Start,
  180. IsCommand,
  181. Normal,
  182. }
  183. pub type ChatEditPtr = Arc<ChatEdit>;
  184. pub struct ChatEdit {
  185. node: SceneNodeWeak,
  186. tasks: OnceLock<Vec<smol::Task<()>>>,
  187. render_api: RenderApi,
  188. text_shaper: TextShaperPtr,
  189. key_repeat: SyncMutex<PressedKeysSmoothRepeat>,
  190. glyphs: SyncMutex<Vec<Glyph>>,
  191. root_dc_key: u64,
  192. content_dc_key: u64,
  193. /// DC key for the text
  194. text_dc_key: u64,
  195. cursor_mesh: SyncMutex<Option<GfxDrawMesh>>,
  196. /// DC key for the cursor. Allows updating cursor independently.
  197. cursor_dc_key: u64,
  198. /// DC key for the selection.
  199. select_dc_key: u64,
  200. is_active: PropertyBool,
  201. is_focused: PropertyBool,
  202. min_height: PropertyFloat32,
  203. max_height: PropertyFloat32,
  204. content_height: PropertyFloat32,
  205. rect: PropertyRect,
  206. baseline: PropertyFloat32,
  207. lineheight: PropertyFloat32,
  208. scroll: PropertyFloat32,
  209. scroll_speed: PropertyFloat32,
  210. padding: PropertyPtr,
  211. font_size: PropertyFloat32,
  212. text: PropertyStr,
  213. text_color: PropertyColor,
  214. text_hi_color: PropertyColor,
  215. text_cmd_color: PropertyColor,
  216. cursor_color: PropertyColor,
  217. cursor_width: PropertyFloat32,
  218. cursor_ascent: PropertyFloat32,
  219. cursor_descent: PropertyFloat32,
  220. cursor_blink_time: PropertyUint32,
  221. cursor_idle_time: PropertyUint32,
  222. hi_bg_color: PropertyColor,
  223. cmd_bg_color: PropertyColor,
  224. select_ascent: PropertyFloat32,
  225. select_descent: PropertyFloat32,
  226. handle_descent: PropertyFloat32,
  227. select_text: PropertyPtr,
  228. z_index: PropertyUint32,
  229. priority: PropertyUint32,
  230. debug: PropertyBool,
  231. mouse_btn_held: AtomicBool,
  232. cursor_is_visible: AtomicBool,
  233. blink_is_paused: AtomicBool,
  234. /// Used to explicitly hide the cursor. Must be manually re-enabled.
  235. hide_cursor: AtomicBool,
  236. touch_info: SyncMutex<TouchInfo>,
  237. is_phone_select: AtomicBool,
  238. old_window_scale: AtomicF32,
  239. window_scale: PropertyFloat32,
  240. parent_rect: SyncMutex<Option<Rectangle>>,
  241. is_mouse_hover: AtomicBool,
  242. editor: AsyncMutex<Editor>,
  243. }
  244. impl ChatEdit {
  245. pub async fn new(
  246. node: SceneNodeWeak,
  247. window_scale: PropertyFloat32,
  248. render_api: RenderApi,
  249. text_shaper: TextShaperPtr,
  250. ex: ExecutorPtr,
  251. ) -> Pimpl {
  252. t!("ChatEdit::new()");
  253. let node_ref = &node.upgrade().unwrap();
  254. let is_active = PropertyBool::wrap(node_ref, Role::Internal, "is_active", 0).unwrap();
  255. let is_focused = PropertyBool::wrap(node_ref, Role::Internal, "is_focused", 0).unwrap();
  256. let min_height =
  257. PropertyFloat32::wrap(node_ref, Role::Internal, "height_range", 0).unwrap();
  258. let max_height =
  259. PropertyFloat32::wrap(node_ref, Role::Internal, "height_range", 1).unwrap();
  260. let content_height =
  261. PropertyFloat32::wrap(node_ref, Role::Internal, "content_height", 0).unwrap();
  262. let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap();
  263. let baseline = PropertyFloat32::wrap(node_ref, Role::Internal, "baseline", 0).unwrap();
  264. let lineheight = PropertyFloat32::wrap(node_ref, Role::Internal, "lineheight", 0).unwrap();
  265. let scroll = PropertyFloat32::wrap(node_ref, Role::Internal, "scroll", 0).unwrap();
  266. let scroll_speed =
  267. PropertyFloat32::wrap(node_ref, Role::Internal, "scroll_speed", 0).unwrap();
  268. let padding = node_ref.get_property("padding").unwrap();
  269. let font_size = PropertyFloat32::wrap(node_ref, Role::Internal, "font_size", 0).unwrap();
  270. let text = PropertyStr::wrap(node_ref, Role::Internal, "text", 0).unwrap();
  271. let text_color = PropertyColor::wrap(node_ref, Role::Internal, "text_color").unwrap();
  272. let text_hi_color = PropertyColor::wrap(node_ref, Role::Internal, "text_hi_color").unwrap();
  273. let text_cmd_color =
  274. PropertyColor::wrap(node_ref, Role::Internal, "text_cmd_color").unwrap();
  275. let cursor_color = PropertyColor::wrap(node_ref, Role::Internal, "cursor_color").unwrap();
  276. let cursor_width =
  277. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_width", 0).unwrap();
  278. let cursor_ascent =
  279. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_ascent", 0).unwrap();
  280. let cursor_descent =
  281. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_descent", 0).unwrap();
  282. let hi_bg_color = PropertyColor::wrap(node_ref, Role::Internal, "hi_bg_color").unwrap();
  283. let cmd_bg_color = PropertyColor::wrap(node_ref, Role::Internal, "cmd_bg_color").unwrap();
  284. let select_ascent =
  285. PropertyFloat32::wrap(node_ref, Role::Internal, "select_ascent", 0).unwrap();
  286. let select_descent =
  287. PropertyFloat32::wrap(node_ref, Role::Internal, "select_descent", 0).unwrap();
  288. let handle_descent =
  289. PropertyFloat32::wrap(node_ref, Role::Internal, "handle_descent", 0).unwrap();
  290. let select_text = node_ref.get_property("select_text").unwrap();
  291. let cursor_blink_time =
  292. PropertyUint32::wrap(node_ref, Role::Internal, "cursor_blink_time", 0).unwrap();
  293. let cursor_idle_time =
  294. PropertyUint32::wrap(node_ref, Role::Internal, "cursor_idle_time", 0).unwrap();
  295. let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
  296. let priority = PropertyUint32::wrap(node_ref, Role::Internal, "priority", 0).unwrap();
  297. let debug = PropertyBool::wrap(node_ref, Role::Internal, "debug", 0).unwrap();
  298. let node_name = node_ref.name.clone();
  299. let node_id = node_ref.id;
  300. // Must do this whenever the text changes
  301. let glyphs = text_shaper.shape(text.get(), font_size.get(), window_scale.get());
  302. let self_ = Arc::new(Self {
  303. node,
  304. tasks: OnceLock::new(),
  305. render_api,
  306. text_shaper: text_shaper.clone(),
  307. key_repeat: SyncMutex::new(PressedKeysSmoothRepeat::new(400, 50)),
  308. glyphs: SyncMutex::new(glyphs),
  309. root_dc_key: OsRng.gen(),
  310. content_dc_key: OsRng.gen(),
  311. text_dc_key: OsRng.gen(),
  312. cursor_mesh: SyncMutex::new(None),
  313. cursor_dc_key: OsRng.gen(),
  314. select_dc_key: OsRng.gen(),
  315. is_active,
  316. is_focused,
  317. min_height,
  318. max_height,
  319. content_height,
  320. rect,
  321. baseline,
  322. lineheight: lineheight.clone(),
  323. scroll: scroll.clone(),
  324. scroll_speed,
  325. padding,
  326. font_size: font_size.clone(),
  327. text: text.clone(),
  328. text_color: text_color.clone(),
  329. text_hi_color,
  330. text_cmd_color,
  331. cursor_color,
  332. cursor_width,
  333. cursor_ascent,
  334. cursor_descent,
  335. cursor_blink_time,
  336. cursor_idle_time,
  337. hi_bg_color,
  338. cmd_bg_color,
  339. select_ascent,
  340. select_descent,
  341. handle_descent,
  342. select_text,
  343. z_index,
  344. priority,
  345. debug,
  346. mouse_btn_held: AtomicBool::new(false),
  347. cursor_is_visible: AtomicBool::new(true),
  348. blink_is_paused: AtomicBool::new(false),
  349. hide_cursor: AtomicBool::new(false),
  350. touch_info: SyncMutex::new(TouchInfo::new(scroll)),
  351. is_phone_select: AtomicBool::new(false),
  352. old_window_scale: AtomicF32::new(window_scale.get()),
  353. window_scale: window_scale.clone(),
  354. parent_rect: SyncMutex::new(None),
  355. is_mouse_hover: AtomicBool::new(false),
  356. editor: AsyncMutex::new(
  357. Editor::new(text, font_size, text_color, window_scale, lineheight).await,
  358. ),
  359. });
  360. Pimpl::ChatEdit(self_)
  361. }
  362. fn node(&self) -> SceneNodePtr {
  363. self.node.upgrade().unwrap()
  364. }
  365. fn padding_top(&self) -> f32 {
  366. self.padding.get_f32(0).unwrap()
  367. }
  368. fn padding_bottom(&self) -> f32 {
  369. self.padding.get_f32(1).unwrap()
  370. }
  371. fn abs_to_local(&self, point: &mut Point) {
  372. let rect = self.rect.get();
  373. *point -= rect.pos();
  374. *point -= self.inner_pos();
  375. point.y += self.scroll.get();
  376. }
  377. /// Inner position used for rendering
  378. fn inner_pos(&self) -> Point {
  379. let content_height = self.content_height.get();
  380. let rect_h = self.rect.get_height();
  381. let mut inner_pos = Point::zero();
  382. if content_height < rect_h {
  383. inner_pos.y = (rect_h - content_height) / 2.;
  384. }
  385. inner_pos
  386. }
  387. /// Maximum allowed scroll value
  388. fn max_scroll(&self) -> f32 {
  389. let content_height = self.content_height.get();
  390. let rect_h = self.rect.get_height();
  391. let mut max_scroll = (content_height - rect_h).max(0.);
  392. // Arbitrary little space at the bottom
  393. max_scroll += self.font_size.get() / 3.;
  394. max_scroll
  395. }
  396. fn regen_cursor_mesh(&self) -> GfxDrawMesh {
  397. let cursor_width = self.cursor_width.get();
  398. let cursor_ascent = self.cursor_ascent.get();
  399. let cursor_descent = self.cursor_descent.get();
  400. let baseline = self.baseline.get();
  401. let cursor_rect = Rectangle {
  402. x: 0.,
  403. y: baseline - cursor_ascent,
  404. w: cursor_width,
  405. h: cursor_ascent + cursor_descent,
  406. };
  407. let cursor_color = self.cursor_color.get();
  408. let mut mesh = MeshBuilder::new();
  409. mesh.draw_filled_box(&cursor_rect, cursor_color);
  410. mesh.alloc(&self.render_api).draw_untextured()
  411. }
  412. fn draw_phone_select_handle(&self, mesh: &mut MeshBuilder, pos: Point, side: f32) {
  413. let baseline = self.baseline.get();
  414. let select_ascent = self.select_ascent.get();
  415. let handle_descent = self.handle_descent.get();
  416. let color = self.text_hi_color.get();
  417. // Transparent for fade
  418. let mut color_trans = color.clone();
  419. color_trans[3] = 0.;
  420. let x = pos.x;
  421. let mut y = pos.y + baseline;
  422. // Vertical line downwards. We use this instead of draw_box() so we have a fade.
  423. let verts = vec![
  424. Vertex { pos: [x - side * 1., y - select_ascent], color: color_trans, uv: [0., 0.] },
  425. Vertex { pos: [x + side * 4., y - select_ascent], color: color_trans, uv: [0., 0.] },
  426. Vertex { pos: [x - side * 1., y + handle_descent + 5.], color, uv: [0., 0.] },
  427. Vertex { pos: [x + side * 4., y + handle_descent + 5.], color, uv: [0., 0.] },
  428. ];
  429. let indices = vec![0, 2, 1, 1, 2, 3];
  430. mesh.append(verts, indices);
  431. y += handle_descent;
  432. // The arrow itself.
  433. // Go anti-clockwise
  434. let verts = vec![
  435. Vertex { pos: [x, y], color, uv: [0., 0.] },
  436. Vertex { pos: [x, y + 50.], color, uv: [0., 0.] },
  437. Vertex { pos: [x + side * 30., y + 50.], color, uv: [0., 0.] },
  438. Vertex { pos: [x + side * 50., y + 25.], color, uv: [0., 0.] },
  439. ];
  440. let indices = vec![0, 1, 2, 0, 2, 3];
  441. mesh.append(verts, indices);
  442. }
  443. async fn change_focus(self: Arc<Self>) {
  444. if !self.is_active.get() {
  445. return
  446. }
  447. t!("Focus changed");
  448. // Cursor visibility will change so just redraw everything lol
  449. self.redraw().await;
  450. }
  451. async fn insert_char(&self, key: char) {
  452. t!("insert_char({key})");
  453. let mut tmp = [0; 4];
  454. let key_str = key.encode_utf8(&mut tmp);
  455. let mut txt_ctx = text2::TEXT_CTX.get().await;
  456. let mut editor = self.editor.lock().await;
  457. let mut drv = editor.driver(&mut txt_ctx).await.unwrap();
  458. drv.insert_or_replace_selection(&key_str);
  459. }
  460. async fn handle_shortcut(
  461. &self,
  462. key: char,
  463. mods: &KeyMods,
  464. atom: &mut PropertyAtomicGuard,
  465. ) -> bool {
  466. t!("handle_shortcut({:?}, {:?})", key, mods);
  467. #[cfg(not(target_os = "macos"))]
  468. let action_mod = mods.ctrl;
  469. #[cfg(target_os = "macos")]
  470. let action_mod = mods.logo;
  471. let mut txt_ctx = text2::TEXT_CTX.get().await;
  472. let mut editor = self.editor.lock().await;
  473. let mut drv = editor.driver(&mut txt_ctx).await.unwrap();
  474. match key {
  475. 'a' => {
  476. if action_mod {
  477. drv.select_all();
  478. }
  479. }
  480. 'c' => {
  481. if action_mod {
  482. if let Some(text) = editor.selected_text() {
  483. miniquad::window::clipboard_set(&text);
  484. }
  485. }
  486. }
  487. 'v' => {
  488. if action_mod {
  489. if let Some(text) = miniquad::window::clipboard_get() {
  490. drv.insert_or_replace_selection(&text);
  491. }
  492. }
  493. }
  494. _ => return false,
  495. }
  496. drop(editor);
  497. drop(txt_ctx);
  498. self.redraw().await;
  499. true
  500. }
  501. async fn handle_key(
  502. &self,
  503. key: &KeyCode,
  504. mods: &KeyMods,
  505. atom: &mut PropertyAtomicGuard,
  506. ) -> bool {
  507. #[cfg(not(target_os = "macos"))]
  508. let action_mod = mods.ctrl;
  509. #[cfg(target_os = "macos")]
  510. let action_mod = mods.logo;
  511. t!("handle_key({:?}, {:?}) action_mod={action_mod}", key, mods);
  512. let mut txt_ctx = text2::TEXT_CTX.get().await;
  513. let mut editor = self.editor.lock().await;
  514. let mut drv = editor.driver(&mut txt_ctx).await.unwrap();
  515. match key {
  516. KeyCode::Left => {
  517. if action_mod {
  518. if mods.shift {
  519. drv.select_word_left();
  520. } else {
  521. drv.move_word_left();
  522. }
  523. } else if mods.shift {
  524. drv.select_left();
  525. } else {
  526. drv.move_left();
  527. }
  528. }
  529. KeyCode::Right => {
  530. if action_mod {
  531. if mods.shift {
  532. drv.select_word_right();
  533. } else {
  534. drv.move_word_right();
  535. }
  536. } else if mods.shift {
  537. drv.select_right();
  538. } else {
  539. drv.move_right();
  540. }
  541. }
  542. KeyCode::Up => {
  543. if mods.shift {
  544. drv.select_up();
  545. } else {
  546. drv.move_up();
  547. }
  548. }
  549. KeyCode::Down => {
  550. if mods.shift {
  551. drv.select_down();
  552. } else {
  553. drv.move_down();
  554. }
  555. }
  556. KeyCode::Enter | KeyCode::KpEnter => {
  557. if mods.shift {
  558. drv.insert_or_replace_selection("\n");
  559. }
  560. }
  561. KeyCode::Delete => {
  562. if action_mod {
  563. drv.delete_word();
  564. } else {
  565. drv.delete();
  566. }
  567. }
  568. KeyCode::Backspace => {
  569. if action_mod {
  570. drv.backdelete_word();
  571. } else {
  572. drv.backdelete();
  573. }
  574. }
  575. KeyCode::Home => {
  576. if action_mod {
  577. if mods.shift {
  578. drv.select_to_text_start();
  579. } else {
  580. drv.move_to_text_start();
  581. }
  582. } else if mods.shift {
  583. drv.select_to_line_start();
  584. } else {
  585. drv.move_to_line_start();
  586. }
  587. }
  588. KeyCode::End => {
  589. if action_mod {
  590. if mods.shift {
  591. drv.select_to_text_end();
  592. } else {
  593. drv.move_to_text_end();
  594. }
  595. } else if mods.shift {
  596. drv.select_to_line_end();
  597. } else {
  598. drv.move_to_line_end();
  599. }
  600. }
  601. _ => return false,
  602. }
  603. drop(editor);
  604. drop(txt_ctx);
  605. self.pause_blinking();
  606. self.redraw().await;
  607. return true
  608. }
  609. /// This will select the entire word rather than move the cursor to that location
  610. async fn start_touch_select(&self, touch_pos: Point, atom: &mut PropertyAtomicGuard) {
  611. t!("start_touch_select({touch_pos:?})");
  612. let mut editor = self.editor.lock().await;
  613. editor.select_word_at_point(touch_pos);
  614. editor.refresh(atom).await;
  615. let seltext = editor.selected_text().unwrap();
  616. d!("Selected {seltext:?} from {touch_pos:?}");
  617. self.select_text.clone().set_str(atom, Role::Internal, 0, seltext).unwrap();
  618. drop(editor);
  619. // if start != end {
  620. self.is_phone_select.store(true, Ordering::Relaxed);
  621. self.hide_cursor.store(true, Ordering::Relaxed);
  622. // }
  623. }
  624. async fn handle_touch_start(&self, mut touch_pos: Point) -> bool {
  625. t!("handle_touch_start({touch_pos:?})");
  626. if self.try_handle_drag(touch_pos).await {
  627. return true
  628. }
  629. let rect = self.rect.get();
  630. if !rect.contains(touch_pos) {
  631. t!("rect!cont rect={rect:?}, touch_pos={touch_pos:?}");
  632. return false
  633. }
  634. let mut touch_info = self.touch_info.lock();
  635. touch_info.start(touch_pos);
  636. true
  637. }
  638. async fn get_select_handles(&self) -> Option<(Point, Point)> {
  639. let editor = self.editor.lock().await;
  640. let layout = editor.layout();
  641. let sel = editor.selection();
  642. if sel.is_collapsed() {
  643. assert!(!self.is_phone_select.load(Ordering::Relaxed));
  644. return None
  645. }
  646. assert!(self.is_phone_select.load(Ordering::Relaxed));
  647. let mut first_pos = None;
  648. let mut last_pos = Default::default();
  649. sel.geometry_with(layout, |rect: parley::Rect, _| {
  650. let rect = Rectangle::from(rect);
  651. if first_pos.is_none() {
  652. first_pos = Some(rect.bot_left());
  653. }
  654. last_pos = rect.corner();
  655. });
  656. let first_pos = first_pos.unwrap();
  657. Some((first_pos, last_pos))
  658. }
  659. async fn try_handle_drag(&self, mut touch_pos: Point) -> bool {
  660. let Some((mut first_pos, mut last_pos)) = self.get_select_handles().await else {
  661. return false
  662. };
  663. self.abs_to_local(&mut touch_pos);
  664. t!("localize touch_pos = {touch_pos:?}");
  665. let handle_off_y = self.handle_descent.get();
  666. first_pos.y += handle_off_y;
  667. last_pos.y += handle_off_y;
  668. // Are we within range of either one?
  669. t!("handle center points = ({first_pos:?}, {last_pos:?})");
  670. const TOUCH_RADIUS_SQ: f32 = 10_000.;
  671. let first_dist_sq = first_pos.dist_sq(touch_pos);
  672. let last_dist_sq = last_pos.dist_sq(touch_pos);
  673. let is_first = first_dist_sq <= TOUCH_RADIUS_SQ;
  674. let is_last = last_dist_sq <= TOUCH_RADIUS_SQ;
  675. let mut side = 0;
  676. if is_first && is_last {
  677. // Are we closer to the first or last?
  678. // Break the tie
  679. if first_dist_sq < last_dist_sq {
  680. side = -1;
  681. } else {
  682. side = 1;
  683. }
  684. } else if is_first {
  685. side = -1;
  686. } else if is_last {
  687. side = 1;
  688. }
  689. if side != 0 {
  690. d!("start touch: DragSelectHandle state [side={side}]");
  691. // Set touch_state status to enable begin dragging them
  692. let mut touch_info = self.touch_info.lock();
  693. touch_info.state = TouchStateAction::DragSelectHandle { side };
  694. return true
  695. }
  696. false
  697. }
  698. async fn handle_touch_move(&self, mut touch_pos: Point) -> bool {
  699. //t!("handle_touch_move({touch_pos:?})");
  700. let atom = &mut PropertyAtomicGuard::new();
  701. // We must update with non relative touch_pos bcos when doing vertical scrolling
  702. // we will modify the scroll, which is used by abs_to_local(), which is used
  703. // to then calculate the max scroll. So it ends up jumping around.
  704. // We use the abs touch_pos without scroll adjust applied for vert scrolling.
  705. let touch_state = {
  706. let mut touch_info = self.touch_info.lock();
  707. touch_info.update(&touch_pos);
  708. touch_info.state.clone()
  709. };
  710. match &touch_state {
  711. TouchStateAction::Inactive => return false,
  712. TouchStateAction::StartSelect => {
  713. if self.text.get().is_empty() {
  714. let node = self.node.upgrade().unwrap();
  715. node.trigger("paste_request", vec![]).await.unwrap();
  716. } else {
  717. self.abs_to_local(&mut touch_pos);
  718. self.start_touch_select(touch_pos, atom).await;
  719. self.redraw_select().await;
  720. }
  721. d!("touch state: StartSelect -> Select");
  722. self.touch_info.lock().state = TouchStateAction::Select;
  723. }
  724. TouchStateAction::DragSelectHandle { side } => {
  725. let handle_descent = self.handle_descent.get();
  726. self.abs_to_local(&mut touch_pos);
  727. let editor = self.editor.lock().await;
  728. let sel = editor.selection();
  729. assert!(*side == -1 || *side == 1);
  730. assert!(self.is_phone_select.load(Ordering::Relaxed));
  731. assert!(!sel.is_collapsed());
  732. let mut pos = touch_pos;
  733. // Only allow selecting text that is visible in the box
  734. // We do our calcs relative to (0, 0) so bhs = rect_h
  735. pos.y -= handle_descent + 25.;
  736. //let bhs = wrapped_lines.height();
  737. //pos.y = min_f32(pos.y, bhs);
  738. let mut select = sel.text_range();
  739. let layout = editor.layout();
  740. t!("select (pre): {:?}", sel.text_range());
  741. let mut cursor = parley::Cursor::from_point(layout, pos.x, pos.y).index();
  742. t!("cursor: {cursor}");
  743. // The selection is NOT allowed to cross over itself.
  744. if *side == -1 {
  745. let max_start = sel.focus().previous_visual(layout).index();
  746. t!("side == -1 max={max_start}");
  747. select.start = std::cmp::min(cursor, max_start);
  748. } else {
  749. assert_eq!(*side, 1);
  750. let min_end = sel.anchor().next_visual(layout).index();
  751. t!("side == +1 min={min_end}");
  752. select.end = std::cmp::max(cursor, min_end);
  753. };
  754. t!("set_select({select:?})");
  755. editor.set_selection(select.start, select.end);
  756. drop(editor);
  757. self.redraw_select().await;
  758. }
  759. TouchStateAction::ScrollVert { start_pos, scroll_start } => {
  760. let y_dist = start_pos.y - touch_pos.y;
  761. let mut scroll = scroll_start + y_dist;
  762. scroll = scroll.clamp(0., self.max_scroll());
  763. if (self.scroll.get() - scroll).abs() < VERT_SCROLL_UPDATE_INC {
  764. return true
  765. }
  766. self.scroll.set(atom, scroll);
  767. self.redraw_scroll().await;
  768. }
  769. TouchStateAction::SetCursorPos => {
  770. // TBH I can't even see the cursor under my thumb so I'll just
  771. // comment this for now.
  772. }
  773. _ => {}
  774. }
  775. true
  776. }
  777. async fn handle_touch_end(&self, mut touch_pos: Point) -> bool {
  778. t!("handle_touch_end({touch_pos:?})");
  779. let atom = &mut PropertyAtomicGuard::new();
  780. self.abs_to_local(&mut touch_pos);
  781. let state = self.touch_info.lock().stop();
  782. match state {
  783. TouchStateAction::Inactive => return false,
  784. TouchStateAction::Started { pos: _, instant: _ } | TouchStateAction::SetCursorPos => {
  785. self.touch_set_cursor_pos(touch_pos, atom).await;
  786. self.redraw().await;
  787. }
  788. _ => {}
  789. }
  790. let node = self.node.upgrade().unwrap();
  791. node.trigger("focus_request", vec![]).await.unwrap();
  792. true
  793. }
  794. async fn touch_set_cursor_pos(&self, mut touch_pos: Point, atom: &mut PropertyAtomicGuard) {
  795. t!("touch_set_cursor_pos({touch_pos:?})");
  796. let mut editor = self.editor.lock().await;
  797. editor.move_to_pos(touch_pos);
  798. editor.refresh(atom).await;
  799. drop(editor);
  800. self.pause_blinking();
  801. self.is_phone_select.store(false, Ordering::Relaxed);
  802. self.hide_cursor.store(false, Ordering::Relaxed);
  803. }
  804. /// Whenever the cursor property is modified this MUST be called
  805. /// to recalculate the scroll x property.
  806. fn apply_cursor_scrolling(&self, atom: &mut PropertyAtomicGuard) {
  807. /*
  808. let rect = self.rect.get();
  809. let cursor_pos = self.cursor_pos.get() as usize;
  810. let cursor_width = self.cursor_width.get();
  811. let mut scroll = self.scroll.get();
  812. let cursor_x = {
  813. let font_size = self.font_size.get();
  814. let window_scale = self.window_scale.get();
  815. let baseline = self.baseline.get();
  816. let glyphs = self.glyphs.lock().clone();
  817. let mut glyph_pos_iter =
  818. GlyphPositionIter::new(font_size, window_scale, &glyphs, baseline);
  819. if cursor_pos == 0 {
  820. 0.
  821. } else if cursor_pos == glyphs.len() {
  822. let glyph_pos = glyph_pos_iter.last().unwrap();
  823. let rhs = glyph_pos.rhs() + eol_nudge(font_size, &glyphs);
  824. rhs
  825. } else {
  826. assert!(cursor_pos < glyphs.len());
  827. let glyph_pos = glyph_pos_iter.nth(cursor_pos).expect("glyph pos mismatch glyphs");
  828. glyph_pos.x
  829. }
  830. };
  831. // The LHS and RHS of the cursor box
  832. let cursor_lhs = cursor_x - scroll;
  833. let cursor_rhs = cursor_lhs + cursor_width;
  834. // RHS is outside
  835. if cursor_rhs > rect.w {
  836. // We want a scroll so RHS = w
  837. // cursor_x - scroll + CURSOR_WIDTH = rect.w
  838. scroll = cursor_x + cursor_width - rect.w;
  839. // LHS is negative
  840. } else if cursor_lhs < 0. {
  841. // We want scroll so LHS = 0
  842. // cursor_x - scroll = 0
  843. scroll = cursor_x;
  844. }
  845. self.scroll.set(atom, scroll);
  846. */
  847. }
  848. fn pause_blinking(&self) {
  849. self.blink_is_paused.store(true, Ordering::Relaxed);
  850. self.cursor_is_visible.store(true, Ordering::Relaxed);
  851. }
  852. async fn redraw(&self) {
  853. let atom = &mut PropertyAtomicGuard::new();
  854. let trace_id = rand::random();
  855. let timest = unixtime();
  856. let draw_update = self.make_draw_calls(trace_id, atom).await;
  857. self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
  858. }
  859. /// Called when scroll changes. Moves content up or down. Nothing more.
  860. async fn redraw_scroll(&self) {
  861. let timest = unixtime();
  862. let rect = self.rect.get();
  863. let scroll = self.scroll.get();
  864. let draw_main = vec![(
  865. self.content_dc_key,
  866. GfxDrawCall {
  867. instrs: vec![GfxDrawInstruction::Move(Point::new(0., -scroll))],
  868. dcs: vec![self.text_dc_key, self.cursor_dc_key, self.select_dc_key],
  869. z_index: self.z_index.get(),
  870. },
  871. )];
  872. self.render_api.replace_draw_calls(timest, draw_main);
  873. }
  874. async fn redraw_cursor(&self) {
  875. let timest = unixtime();
  876. let instrs = self.get_cursor_instrs().await;
  877. let draw_calls =
  878. vec![(self.cursor_dc_key, GfxDrawCall { instrs, dcs: vec![], z_index: 2 })];
  879. self.render_api.replace_draw_calls(timest, draw_calls);
  880. }
  881. async fn redraw_select(&self) {
  882. let timest = unixtime();
  883. let instrs = self.regen_select_mesh().await;
  884. let draw_calls =
  885. vec![(self.select_dc_key, GfxDrawCall { instrs, dcs: vec![], z_index: 0 })];
  886. self.render_api.replace_draw_calls(timest, draw_calls);
  887. }
  888. async fn get_cursor_instrs(&self) -> Vec<GfxDrawInstruction> {
  889. if !self.is_focused.get() ||
  890. !self.cursor_is_visible.load(Ordering::Relaxed) ||
  891. self.hide_cursor.load(Ordering::Relaxed)
  892. {
  893. return vec![]
  894. }
  895. let lineheight = self.lineheight.get();
  896. let mut cursor_instrs = vec![];
  897. let mut cursor_pos = self.editor.lock().await.get_cursor_pos();
  898. cursor_pos += self.inner_pos();
  899. cursor_instrs.push(GfxDrawInstruction::Move(cursor_pos));
  900. let cursor_mesh =
  901. self.cursor_mesh.lock().get_or_insert_with(|| self.regen_cursor_mesh()).clone();
  902. cursor_instrs.push(GfxDrawInstruction::Draw(cursor_mesh));
  903. cursor_instrs
  904. }
  905. fn regen_bg_mesh(&self) -> Vec<GfxDrawInstruction> {
  906. let padding_top = self.padding_top();
  907. let padding_bottom = self.padding_bottom();
  908. let mut instrs = vec![];
  909. if self.debug.get() {
  910. let mut rect = self.rect.get();
  911. rect.x = 0.;
  912. rect.y = 0.;
  913. let mut mesh = MeshBuilder::new();
  914. mesh.draw_outline(&rect, [0., 1., 0., 1.], 1.);
  915. instrs.push(GfxDrawInstruction::Draw(mesh.alloc(&self.render_api).draw_untextured()));
  916. rect.y = padding_top;
  917. rect.h -= padding_top + padding_bottom;
  918. let mut mesh = MeshBuilder::new();
  919. mesh.draw_outline(&rect, [0., 1., 0., 0.5], 1.);
  920. instrs.push(GfxDrawInstruction::Draw(mesh.alloc(&self.render_api).draw_untextured()));
  921. }
  922. instrs
  923. }
  924. async fn regen_txt_mesh(&self) -> Vec<GfxDrawInstruction> {
  925. let mut instrs = vec![GfxDrawInstruction::Move(self.inner_pos())];
  926. let editor = self.editor.lock().await;
  927. let layout = editor.layout();
  928. let mut render_instrs = text2::render_layout(layout, &self.render_api);
  929. instrs.append(&mut render_instrs);
  930. instrs
  931. }
  932. async fn regen_select_mesh(&self) -> Vec<GfxDrawInstruction> {
  933. let padding_top = self.padding_top();
  934. let mut instrs = vec![GfxDrawInstruction::Move(self.inner_pos())];
  935. let editor = self.editor.lock().await;
  936. let layout = editor.layout();
  937. let sel = editor.selection();
  938. let sel_color = self.hi_bg_color.get();
  939. if !sel.is_collapsed() {
  940. let mut first_pos = None;
  941. let mut last_pos = Default::default();
  942. let mut mesh = MeshBuilder::new();
  943. sel.geometry_with(layout, |rect: parley::Rect, _| {
  944. let rect = Rectangle::from(rect);
  945. if first_pos.is_none() {
  946. first_pos = Some(rect.pos());
  947. }
  948. last_pos = rect.top_right();
  949. mesh.draw_filled_box(&rect, sel_color);
  950. });
  951. if self.is_phone_select.load(Ordering::Relaxed) {
  952. self.draw_phone_select_handle(&mut mesh, first_pos.unwrap(), -1.);
  953. self.draw_phone_select_handle(&mut mesh, last_pos, 1.);
  954. }
  955. instrs.push(GfxDrawInstruction::Draw(mesh.alloc(&self.render_api).draw_untextured()));
  956. }
  957. instrs
  958. }
  959. fn bounded_height(&self, mut height: f32) -> f32 {
  960. let min_height = self.min_height.get();
  961. let max_height = self.max_height.get();
  962. if height < min_height {
  963. height = min_height;
  964. }
  965. if height > max_height {
  966. height = max_height;
  967. }
  968. height
  969. }
  970. async fn eval_rect(&self, atom: &mut PropertyAtomicGuard) {
  971. let parent_rect = self.parent_rect.lock().clone().unwrap();
  972. // First we evaluate the width based off the parent dimensions
  973. self.rect
  974. .eval_with(
  975. vec![2],
  976. vec![
  977. ("parent_w".to_string(), parent_rect.w),
  978. ("parent_h".to_string(), parent_rect.h),
  979. ],
  980. )
  981. .unwrap();
  982. // Use the width to adjust the height calcs
  983. let rect_w = self.rect.get_width();
  984. let content_height = {
  985. let mut editor = self.editor.lock().await;
  986. editor.set_width(rect_w);
  987. editor.refresh(atom).await;
  988. editor.height()
  989. };
  990. self.content_height.set(atom, content_height);
  991. let outer_height = content_height + self.padding_top() + self.padding_bottom();
  992. let rect_h = self.bounded_height(outer_height);
  993. self.rect.prop().set_f32(atom, Role::Internal, 3, rect_h);
  994. // Finally calculate the position
  995. self.rect
  996. .eval_with(
  997. vec![0, 1],
  998. vec![
  999. ("parent_w".to_string(), parent_rect.w),
  1000. ("parent_h".to_string(), parent_rect.h),
  1001. ("rect_w".to_string(), rect_w),
  1002. ("rect_h".to_string(), rect_h),
  1003. ],
  1004. )
  1005. .unwrap();
  1006. }
  1007. async fn make_draw_calls(&self, trace_id: u32, atom: &mut PropertyAtomicGuard) -> DrawUpdate {
  1008. self.eval_rect(atom).await;
  1009. let rect = self.rect.get();
  1010. let max_scroll = self.max_scroll();
  1011. let mut scroll = self.scroll.get();
  1012. if scroll > max_scroll {
  1013. scroll = max_scroll;
  1014. self.scroll.set(atom, scroll);
  1015. }
  1016. let cursor_instrs = self.get_cursor_instrs().await;
  1017. let txt_instrs = self.regen_txt_mesh().await;
  1018. let sel_instrs = self.regen_select_mesh().await;
  1019. let mut root_instrs = vec![GfxDrawInstruction::ApplyView(rect)];
  1020. let mut bg_instrs = self.regen_bg_mesh();
  1021. root_instrs.append(&mut bg_instrs);
  1022. DrawUpdate {
  1023. key: self.root_dc_key,
  1024. draw_calls: vec![
  1025. (
  1026. self.root_dc_key,
  1027. GfxDrawCall {
  1028. instrs: root_instrs,
  1029. dcs: vec![self.content_dc_key],
  1030. z_index: self.z_index.get(),
  1031. },
  1032. ),
  1033. (
  1034. self.content_dc_key,
  1035. GfxDrawCall {
  1036. instrs: vec![GfxDrawInstruction::Move(Point::new(0., -scroll))],
  1037. dcs: vec![self.text_dc_key, self.cursor_dc_key, self.select_dc_key],
  1038. z_index: 0,
  1039. },
  1040. ),
  1041. (self.select_dc_key, GfxDrawCall { instrs: sel_instrs, dcs: vec![], z_index: 0 }),
  1042. (self.text_dc_key, GfxDrawCall { instrs: txt_instrs, dcs: vec![], z_index: 1 }),
  1043. (
  1044. self.cursor_dc_key,
  1045. GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: 2 },
  1046. ),
  1047. ],
  1048. }
  1049. }
  1050. async fn process_insert_text_method(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  1051. let Ok(method_call) = sub.receive().await else {
  1052. debug!(target: "ui::chatedit", "Event relayer closed");
  1053. return false
  1054. };
  1055. t!("method called: insert_line({method_call:?})");
  1056. assert!(method_call.send_res.is_none());
  1057. fn decode_data(data: &[u8]) -> std::io::Result<String> {
  1058. let mut cur = Cursor::new(&data);
  1059. let text = String::decode(&mut cur)?;
  1060. Ok(text)
  1061. }
  1062. let Ok(text) = decode_data(&method_call.data) else {
  1063. error!(target: "ui::chatedit", "insert_text() method invalid arg data");
  1064. return true
  1065. };
  1066. let Some(self_) = me.upgrade() else {
  1067. // Should not happen
  1068. panic!("self destroyed before insert_text_method_task was stopped!");
  1069. };
  1070. let atom = &mut PropertyAtomicGuard::new();
  1071. //self_.insert_text(&text, atom).await;
  1072. true
  1073. }
  1074. async fn process_focus_method(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  1075. let Ok(method_call) = sub.receive().await else {
  1076. debug!(target: "ui::chatedit", "Event relayer closed");
  1077. return false
  1078. };
  1079. t!("method called: focus({method_call:?})");
  1080. assert!(method_call.send_res.is_none());
  1081. assert!(method_call.data.is_empty());
  1082. let Some(self_) = me.upgrade() else {
  1083. // Should not happen
  1084. panic!("self destroyed before insert_text_method_task was stopped!");
  1085. };
  1086. let mut editor = self_.editor.lock().await;
  1087. editor.focus();
  1088. true
  1089. }
  1090. async fn handle_android_event(&self, ev: AndroidSuggestEvent) {
  1091. t!("handle_android_event({ev:?})");
  1092. if !self.is_active.get() {
  1093. return
  1094. }
  1095. let mut editor = self.editor.lock().await;
  1096. match ev {
  1097. AndroidSuggestEvent::Init => {
  1098. editor.init();
  1099. return
  1100. }
  1101. AndroidSuggestEvent::CreateInputConnect => editor.setup(),
  1102. AndroidSuggestEvent::ComposeRegion { .. } | AndroidSuggestEvent::FinishCompose => {}
  1103. // Destructive text edits
  1104. AndroidSuggestEvent::Compose { .. } |
  1105. AndroidSuggestEvent::DeleteSurroundingText { .. } => {
  1106. // Any editing will collapse selections
  1107. self.is_phone_select.store(false, Ordering::Relaxed);
  1108. self.hide_cursor.store(false, Ordering::Relaxed);
  1109. }
  1110. }
  1111. let atom = &mut PropertyAtomicGuard::new();
  1112. editor.refresh(atom).await;
  1113. drop(editor);
  1114. self.redraw().await;
  1115. }
  1116. }
  1117. impl Drop for ChatEdit {
  1118. fn drop(&mut self) {
  1119. self.render_api
  1120. .replace_draw_calls(unixtime(), vec![(self.text_dc_key, Default::default())]);
  1121. }
  1122. }
  1123. #[async_trait]
  1124. impl UIObject for ChatEdit {
  1125. fn priority(&self) -> u32 {
  1126. self.priority.get()
  1127. }
  1128. async fn start(self: Arc<Self>, ex: ExecutorPtr) {
  1129. let me = Arc::downgrade(&self);
  1130. let node_ref = &self.node.upgrade().unwrap();
  1131. let method_sub = node_ref.subscribe_method_call("insert_text").unwrap();
  1132. let me2 = me.clone();
  1133. let insert_text_task =
  1134. ex.spawn(
  1135. async move { while Self::process_insert_text_method(&me2, &method_sub).await {} },
  1136. );
  1137. let method_sub = node_ref.subscribe_method_call("focus").unwrap();
  1138. let me2 = me.clone();
  1139. let focus_task =
  1140. ex.spawn(async move { while Self::process_focus_method(&me2, &method_sub).await {} });
  1141. let mut on_modify = OnModify::new(ex.clone(), self.node.clone(), me.clone());
  1142. on_modify.when_change(self.is_focused.prop(), Self::change_focus);
  1143. // When text has been changed.
  1144. // Cursor and selection might be invalidated.
  1145. async fn reset(self_: Arc<ChatEdit>) {
  1146. let atom = &mut PropertyAtomicGuard::new();
  1147. //self_.select_text.set_null(Role::Internal, 0).unwrap();
  1148. self_.scroll.set(atom, 0.);
  1149. self_.redraw().await;
  1150. }
  1151. async fn redraw(self_: Arc<ChatEdit>) {
  1152. self_.redraw().await;
  1153. }
  1154. async fn set_text(self_: Arc<ChatEdit>) {
  1155. {
  1156. let text = self_.text.get();
  1157. //let mut text_wrap = self_.text_wrap.lock();
  1158. //text_wrap.editable.end_compose();
  1159. //text_wrap.editable.set_text(text, String::new());
  1160. //text_wrap.clear_cache();
  1161. //let select = &mut text_wrap.select;
  1162. //select.clear();
  1163. }
  1164. self_.redraw().await;
  1165. }
  1166. on_modify.when_change(self.rect.prop(), redraw);
  1167. on_modify.when_change(self.baseline.prop(), redraw);
  1168. on_modify.when_change(self.lineheight.prop(), redraw);
  1169. on_modify.when_change(self.select_ascent.prop(), redraw);
  1170. on_modify.when_change(self.select_descent.prop(), redraw);
  1171. on_modify.when_change(self.handle_descent.prop(), redraw);
  1172. on_modify.when_change(self.padding.clone(), redraw);
  1173. on_modify.when_change(self.text.prop(), set_text);
  1174. // The commented properties are modified on input events
  1175. // So then redraw() will get repeatedly triggered when these properties
  1176. // are changed. We should find a solution. For now the hooks are disabled.
  1177. //on_modify.when_change(scroll.prop(), redraw);
  1178. //on_modify.when_change(cursor_pos.prop(), redraw);
  1179. on_modify.when_change(self.font_size.prop(), redraw);
  1180. // We must also reshape text
  1181. on_modify.when_change(self.text.prop(), reset);
  1182. on_modify.when_change(self.text_color.prop(), redraw);
  1183. on_modify.when_change(self.hi_bg_color.prop(), redraw);
  1184. //on_modify.when_change(selected.clone(), redraw);
  1185. on_modify.when_change(self.z_index.prop(), redraw);
  1186. on_modify.when_change(self.debug.prop(), redraw);
  1187. async fn regen_cursor(self_: Arc<ChatEdit>) {
  1188. let mesh = std::mem::take(&mut *self_.cursor_mesh.lock());
  1189. }
  1190. on_modify.when_change(self.cursor_color.prop(), regen_cursor);
  1191. on_modify.when_change(self.cursor_ascent.prop(), regen_cursor);
  1192. on_modify.when_change(self.cursor_descent.prop(), regen_cursor);
  1193. on_modify.when_change(self.cursor_width.prop(), regen_cursor);
  1194. let me2 = me.clone();
  1195. let cursor_blink_time = self.cursor_blink_time.clone();
  1196. let cursor_idle_time = self.cursor_idle_time.clone();
  1197. let blinking_cursor_task = ex.spawn(async move {
  1198. loop {
  1199. msleep(cursor_blink_time.get() as u64).await;
  1200. let self_ = me2.upgrade().unwrap();
  1201. if self_.blink_is_paused.swap(false, Ordering::Relaxed) {
  1202. msleep(cursor_idle_time.get() as u64).await;
  1203. continue
  1204. }
  1205. if !self_.rect.has_cached() {
  1206. continue
  1207. }
  1208. // Invert the bool
  1209. self_.cursor_is_visible.fetch_not(Ordering::Relaxed);
  1210. self_.redraw_cursor().await;
  1211. }
  1212. });
  1213. let mut tasks = vec![insert_text_task, focus_task, blinking_cursor_task];
  1214. tasks.append(&mut on_modify.tasks);
  1215. #[cfg(target_os = "android")]
  1216. {
  1217. let (sender, recvr) = async_channel::unbounded();
  1218. let composer_id = crate::android::create_composer(sender);
  1219. self.editor.lock().await.composer_id = composer_id;
  1220. t!("Created composer [{composer_id}]");
  1221. let me2 = me.clone();
  1222. let autosuggest_task = ex.spawn(async move {
  1223. loop {
  1224. let Ok(ev) = recvr.recv().await else {
  1225. t!("Event relayer closed");
  1226. break
  1227. };
  1228. let Some(self_) = me2.upgrade() else {
  1229. // Should not happen
  1230. panic!("self destroyed before autosuggest_task was stopped!");
  1231. };
  1232. self_.handle_android_event(ev).await;
  1233. }
  1234. });
  1235. tasks.push(autosuggest_task);
  1236. }
  1237. self.tasks.set(tasks);
  1238. }
  1239. async fn draw(
  1240. &self,
  1241. parent_rect: Rectangle,
  1242. trace_id: u32,
  1243. atom: &mut PropertyAtomicGuard,
  1244. ) -> Option<DrawUpdate> {
  1245. t!("ChatEdit::draw({:?}, {trace_id})", self.node.upgrade().unwrap());
  1246. *self.parent_rect.lock() = Some(parent_rect);
  1247. Some(self.make_draw_calls(trace_id, atom).await)
  1248. }
  1249. async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) -> bool {
  1250. t!("handle_char({key}, {mods:?}, {repeat})");
  1251. // First filter for only single digit keys
  1252. if DISALLOWED_CHARS.contains(&key) {
  1253. return false
  1254. }
  1255. if !self.is_focused.get() {
  1256. return false
  1257. }
  1258. // Must be updated before checking the mods. You can press ctrl+a, then release ctrl
  1259. // before a is released. Then the repeater never gets reset, and uses any old value
  1260. // it has from before for a.
  1261. let actions = {
  1262. let mut repeater = self.key_repeat.lock();
  1263. repeater.key_down(PressedKey::Char(key), repeat)
  1264. };
  1265. let atom = &mut PropertyAtomicGuard::new();
  1266. if mods.ctrl || mods.alt || mods.logo {
  1267. if repeat {
  1268. return false
  1269. }
  1270. return self.handle_shortcut(key, &mods, atom).await
  1271. }
  1272. let atom = &mut PropertyAtomicGuard::new();
  1273. t!("Key {:?} has {} actions", key, actions);
  1274. for _ in 0..actions {
  1275. self.insert_char(key).await;
  1276. }
  1277. self.redraw().await;
  1278. true
  1279. }
  1280. async fn handle_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
  1281. t!("handle_key_down({key:?}, {mods:?}, {repeat})");
  1282. // First filter for only single digit keys
  1283. // Avoid processing events handled by insert_char()
  1284. if !ALLOWED_KEYCODES.contains(&key) {
  1285. return false
  1286. }
  1287. if !self.is_focused.get() {
  1288. return false
  1289. }
  1290. let actions = {
  1291. let mut repeater = self.key_repeat.lock();
  1292. repeater.key_down(PressedKey::Key(key), repeat)
  1293. };
  1294. // Suppress noisy message
  1295. if actions > 0 {
  1296. t!("Key {:?} has {} actions", key, actions);
  1297. }
  1298. let atom = &mut PropertyAtomicGuard::new();
  1299. let mut is_handled = false;
  1300. for _ in 0..actions {
  1301. if self.handle_key(&key, &mods, atom).await {
  1302. is_handled = true;
  1303. }
  1304. }
  1305. is_handled
  1306. }
  1307. async fn handle_mouse_btn_down(&self, btn: MouseButton, mut mouse_pos: Point) -> bool {
  1308. if !self.is_active.get() {
  1309. return false
  1310. }
  1311. let rect = self.rect.get();
  1312. if btn != MouseButton::Left {
  1313. if btn == MouseButton::Right && rect.contains(mouse_pos) {
  1314. if self.text.get().is_empty() {
  1315. let node = self.node.upgrade().unwrap();
  1316. node.trigger("paste_request", vec![]).await.unwrap();
  1317. }
  1318. return true
  1319. }
  1320. return false
  1321. }
  1322. if !rect.contains(mouse_pos) {
  1323. return false
  1324. }
  1325. let atom = &mut PropertyAtomicGuard::new();
  1326. // clicking inside box will:
  1327. // 1. make it active
  1328. // 2. begin selection
  1329. if self.is_focused.get() {
  1330. d!("ChatEdit clicked");
  1331. } else {
  1332. d!("ChatEdit focused");
  1333. self.is_focused.set(atom, true);
  1334. }
  1335. // Move mouse pos within this widget
  1336. self.abs_to_local(&mut mouse_pos);
  1337. {
  1338. let mut txt_ctx = text2::TEXT_CTX.get().await;
  1339. let mut editor = self.editor.lock().await;
  1340. let mut drv = editor.driver(&mut txt_ctx).await.unwrap();
  1341. drv.move_to_point(mouse_pos.x, mouse_pos.y);
  1342. }
  1343. if !self.select_text.is_null(0).unwrap() {
  1344. self.select_text.clone().set_null(atom, Role::Internal, 0).unwrap();
  1345. }
  1346. self.mouse_btn_held.store(true, Ordering::Relaxed);
  1347. self.pause_blinking();
  1348. self.redraw().await;
  1349. true
  1350. }
  1351. async fn handle_mouse_btn_up(&self, btn: MouseButton, mouse_pos: Point) -> bool {
  1352. if !self.is_active.get() {
  1353. return false
  1354. }
  1355. // releasing mouse button will end selection
  1356. self.mouse_btn_held.store(false, Ordering::Relaxed);
  1357. false
  1358. }
  1359. async fn handle_mouse_move(&self, mut mouse_pos: Point) -> bool {
  1360. if !self.is_active.get() {
  1361. return false
  1362. }
  1363. let rect = self.rect.get();
  1364. self.is_mouse_hover.store(rect.contains(mouse_pos), Ordering::Relaxed);
  1365. if !self.mouse_btn_held.load(Ordering::Relaxed) {
  1366. return false
  1367. }
  1368. let atom = &mut PropertyAtomicGuard::new();
  1369. // if active and selection_active, then use x to modify the selection.
  1370. // also implement scrolling when cursor is to the left or right
  1371. // just scroll to the end
  1372. // also set cursor_pos too
  1373. // Move mouse pos within this widget
  1374. self.abs_to_local(&mut mouse_pos);
  1375. let seltext = {
  1376. let mut txt_ctx = text2::TEXT_CTX.get().await;
  1377. let mut editor = self.editor.lock().await;
  1378. let mut drv = editor.driver(&mut txt_ctx).await.unwrap();
  1379. drv.extend_selection_to_point(mouse_pos.x, mouse_pos.y);
  1380. editor.selected_text()
  1381. };
  1382. d!("Select {seltext:?} from {mouse_pos:?}");
  1383. // Will be None when drag select just started
  1384. if let Some(seltext) = seltext {
  1385. self.select_text.clone().set_str(atom, Role::Internal, 0, seltext).unwrap();
  1386. }
  1387. self.pause_blinking();
  1388. //self.apply_cursor_scrolling();
  1389. self.redraw_select().await;
  1390. true
  1391. }
  1392. async fn handle_mouse_wheel(&self, wheel_pos: Point) -> bool {
  1393. if !self.is_mouse_hover.load(Ordering::Relaxed) {
  1394. return false
  1395. }
  1396. let atom = &mut PropertyAtomicGuard::new();
  1397. let mut scroll = self.scroll.get() - wheel_pos.y * self.scroll_speed.get();
  1398. scroll = scroll.clamp(0., self.max_scroll());
  1399. t!("handle_mouse_wheel({wheel_pos:?}) [scroll={scroll}]");
  1400. self.scroll.set(atom, scroll);
  1401. self.redraw_scroll().await;
  1402. true
  1403. }
  1404. async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_pos: Point) -> bool {
  1405. if !self.is_active.get() {
  1406. return false
  1407. }
  1408. // Ignore multi-touch
  1409. if id != 0 {
  1410. return false
  1411. }
  1412. match phase {
  1413. TouchPhase::Started => self.handle_touch_start(touch_pos).await,
  1414. TouchPhase::Moved => self.handle_touch_move(touch_pos).await,
  1415. TouchPhase::Ended => self.handle_touch_end(touch_pos).await,
  1416. TouchPhase::Cancelled => false,
  1417. }
  1418. }
  1419. }