chatedit.rs 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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. PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyRect, PropertyStr,
  44. PropertyUint32, Role,
  45. },
  46. pubsub::Subscription,
  47. scene::{MethodCallSub, Pimpl, SceneNodePtr, SceneNodeWeak},
  48. text::{self, Glyph, GlyphPositionIter, TextShaperPtr},
  49. util::{enumerate_ref, is_whitespace, min_f32, unixtime, zip4, Clipboard},
  50. ExecutorPtr,
  51. };
  52. use super::{
  53. editbox::{
  54. editable::{Editable, RenderedEditable, Selection, TextIdx, TextPos},
  55. eol_nudge,
  56. repeat::{PressedKey, PressedKeysSmoothRepeat},
  57. ALLOWED_KEYCODES, DISALLOWED_CHARS,
  58. },
  59. DrawUpdate, OnModify, UIObject,
  60. };
  61. // Minimum dist to update scroll when finger scrolling.
  62. // Avoid updating too much makes scrolling smoother.
  63. const VERT_SCROLL_UPDATE_INC: f32 = 1.;
  64. macro_rules! d { ($($arg:tt)*) => { debug!(target: "ui::chatview", $($arg)*); } }
  65. macro_rules! t { ($($arg:tt)*) => { trace!(target: "ui::chatview", $($arg)*); } }
  66. fn is_all_whitespace(glyphs: &[Glyph]) -> bool {
  67. for glyph in glyphs {
  68. if !is_whitespace(&glyph.substr) {
  69. return false
  70. }
  71. }
  72. true
  73. }
  74. struct TextWrap {
  75. editable: Editable,
  76. select: Vec<Selection>,
  77. rendered: Option<RenderedEditable>,
  78. font_size: PropertyFloat32,
  79. window_scale: PropertyFloat32,
  80. baseline: PropertyFloat32,
  81. linespacing: PropertyFloat32,
  82. }
  83. impl TextWrap {
  84. fn new(
  85. text_shaper: TextShaperPtr,
  86. font_size: PropertyFloat32,
  87. window_scale: PropertyFloat32,
  88. baseline: PropertyFloat32,
  89. linespacing: PropertyFloat32,
  90. ) -> Self {
  91. Self {
  92. editable: Editable::new(
  93. text_shaper,
  94. font_size.clone(),
  95. window_scale.clone(),
  96. baseline.clone(),
  97. ),
  98. select: vec![],
  99. rendered: None,
  100. font_size,
  101. window_scale,
  102. baseline,
  103. linespacing,
  104. }
  105. }
  106. fn clear_cache(&mut self) {
  107. self.rendered = None;
  108. }
  109. fn get_render(&mut self) -> &RenderedEditable {
  110. if self.rendered.is_none() {
  111. //debug!(target: "ui::chatedit::text_wrap", "Regenerating render cache");
  112. self.rendered = Some(self.editable.render());
  113. }
  114. self.rendered.as_ref().unwrap()
  115. }
  116. fn wrap(&mut self, width: f32) -> WrappedLines {
  117. let font_size = self.font_size.get();
  118. let window_scale = self.window_scale.get();
  119. let baseline = self.baseline.get();
  120. let linespacing = self.linespacing.get();
  121. let rendered = self.get_render();
  122. let wrapped_glyphs = text::wrap(width, font_size, window_scale, &rendered.glyphs);
  123. let mut curr_pos = 0;
  124. let mut lines: Vec<_> = wrapped_glyphs
  125. .into_iter()
  126. .map(|glyphs| {
  127. let off_pos = curr_pos;
  128. curr_pos += glyphs.len();
  129. WrappedLine::new(glyphs, off_pos, font_size, window_scale, baseline)
  130. })
  131. .collect();
  132. if let Some(last) = lines.last_mut() {
  133. assert_eq!(last.last_pos(), rendered.glyphs.len());
  134. last.is_last = true;
  135. }
  136. WrappedLines::new(lines, font_size, linespacing)
  137. }
  138. fn cursor_pos(&mut self) -> TextPos {
  139. let rendered = self.get_render().clone();
  140. self.editable.get_cursor_pos(&rendered)
  141. }
  142. fn set_cursor_with_point(&mut self, point: Point, width: f32) -> TextPos {
  143. let wrapped_lines = self.wrap(width);
  144. let cursor_pos = wrapped_lines.point_to_pos(point);
  145. let rendered = self.get_render();
  146. let glyphs_len = rendered.glyphs.len();
  147. let cidx = rendered.pos_to_idx(cursor_pos);
  148. self.editable.set_cursor_idx(cidx);
  149. //debug!(target: "ui::chatedit::text_wrap", "set_cursor_with_point() -> {cursor_pos} / {glyphs_len}");
  150. cursor_pos
  151. }
  152. fn get_word_boundary(&mut self, pos: TextPos) -> (TextPos, TextPos) {
  153. let rendered = self.get_render();
  154. let final_pos = rendered.glyphs.len();
  155. // Find word start
  156. let mut pos_start = pos;
  157. while pos_start > 0 {
  158. // Is the glyph before this pos just whitespace?
  159. let glyph_str = &rendered.glyphs[pos_start - 1].substr;
  160. if is_whitespace(glyph_str) {
  161. break
  162. }
  163. pos_start -= 1;
  164. }
  165. // Find word end
  166. let mut pos_end = std::cmp::min(pos + 1, final_pos);
  167. while pos_end < final_pos {
  168. let glyph_str = &rendered.glyphs[pos_end].substr;
  169. if is_whitespace(glyph_str) {
  170. break
  171. }
  172. pos_end += 1;
  173. }
  174. (pos_start, pos_end)
  175. }
  176. fn delete_selected(&mut self) {
  177. let selection = std::mem::take(&mut self.select);
  178. let sel = selection.first().unwrap();
  179. let cursor_pos = std::cmp::min(sel.start, sel.end);
  180. let render = self.get_render();
  181. let mut before_text = String::new();
  182. let mut after_text = String::new();
  183. 'next: for (pos, glyph) in render.glyphs.iter().enumerate() {
  184. for select in &selection {
  185. let start = std::cmp::min(select.start, select.end);
  186. let end = std::cmp::max(select.start, select.end);
  187. if start <= pos && pos < end {
  188. continue 'next
  189. }
  190. }
  191. if pos <= cursor_pos {
  192. before_text += &glyph.substr;
  193. } else {
  194. after_text += &glyph.substr;
  195. }
  196. }
  197. self.editable.end_compose();
  198. self.editable.set_text(before_text, after_text);
  199. self.clear_cache();
  200. }
  201. }
  202. struct WrappedLine {
  203. glyphs: Vec<Glyph>,
  204. off_pos: TextPos,
  205. font_size: f32,
  206. window_scale: f32,
  207. baseline: f32,
  208. /// Last line in this paragraph?
  209. /// In which case the cursor does not wrap
  210. is_last: bool,
  211. }
  212. impl WrappedLine {
  213. fn new(
  214. glyphs: Vec<Glyph>,
  215. off_pos: TextPos,
  216. font_size: f32,
  217. window_scale: f32,
  218. baseline: f32,
  219. ) -> Self {
  220. Self { glyphs, off_pos, font_size, window_scale, baseline, is_last: false }
  221. }
  222. fn len(&self) -> usize {
  223. self.glyphs.len()
  224. }
  225. fn first_pos(&self) -> TextPos {
  226. self.off_pos
  227. }
  228. fn last_pos(&self) -> TextPos {
  229. self.off_pos + self.len()
  230. }
  231. fn pos_iter(&self) -> GlyphPositionIter {
  232. GlyphPositionIter::new(self.font_size, self.window_scale, &self.glyphs, self.baseline)
  233. }
  234. fn rhs(&self) -> f32 {
  235. match self.pos_iter().last() {
  236. None => 0.,
  237. Some(rect) => rect.rhs(),
  238. }
  239. }
  240. fn find_closest(&self, x: f32) -> TextPos {
  241. for (pos, glyph_rect) in self.pos_iter().enumerate() {
  242. let next_x = glyph_rect.center().x;
  243. if x < next_x {
  244. return pos
  245. }
  246. }
  247. if self.is_last {
  248. self.glyphs.len()
  249. } else {
  250. self.glyphs.len() - 1
  251. }
  252. }
  253. }
  254. struct WrappedLines {
  255. lines: Vec<WrappedLine>,
  256. font_size: f32,
  257. linespacing: f32,
  258. }
  259. impl WrappedLines {
  260. fn new(lines: Vec<WrappedLine>, font_size: f32, linespacing: f32) -> Self {
  261. Self { lines, font_size, linespacing }
  262. }
  263. /// Convert an (x, y) point to a glyph pos
  264. fn point_to_pos(&self, mut point: Point) -> TextPos {
  265. if self.lines.is_empty() {
  266. return 0
  267. }
  268. let mut pos = 0;
  269. for (line_idx, wrap_line) in self.lines.iter().enumerate() {
  270. // Is it within this line?
  271. if point.y < self.linespacing || wrap_line.is_last {
  272. //debug!(target: "ui::chatedit::wrapped_lines", "point to pos found line: {line_idx}");
  273. pos += wrap_line.find_closest(point.x);
  274. return pos
  275. }
  276. // Continue to the next line
  277. point.y -= self.linespacing;
  278. pos += wrap_line.len();
  279. }
  280. //debug!(target: "ui::chatedit::wrapped_lines", "point to pos using last line");
  281. panic!("point_to_pos() went past the last line")
  282. }
  283. fn height(&self) -> f32 {
  284. self.last_y() + self.linespacing
  285. }
  286. fn last_y(&self) -> f32 {
  287. if self.lines.is_empty() {
  288. return 0.
  289. }
  290. (self.lines.len() - 1) as f32 * self.linespacing
  291. }
  292. fn last_rhs(&self) -> f32 {
  293. if self.lines.is_empty() {
  294. return 0.
  295. }
  296. let last_line = self.lines.last().unwrap();
  297. let mut rhs = last_line.rhs();
  298. rhs += eol_nudge(self.font_size, &last_line.glyphs);
  299. rhs
  300. }
  301. fn get_glyph_info(&self, mut pos: TextPos) -> (Rectangle, usize) {
  302. let mut y = 0.;
  303. for (line_idx, wrap_line) in self.lines.iter().enumerate() {
  304. assert!(!wrap_line.glyphs.is_empty());
  305. if pos < wrap_line.len() {
  306. // Cursor is on this line
  307. let mut pos_iter = wrap_line.pos_iter();
  308. pos_iter.advance_by(pos).unwrap();
  309. let mut glyph_rect = pos_iter.next().unwrap();
  310. return (glyph_rect, line_idx)
  311. }
  312. pos -= wrap_line.len();
  313. y += self.linespacing;
  314. }
  315. let rhs = self.last_rhs();
  316. let last_y = self.last_y();
  317. let final_rect = Rectangle::new(rhs, last_y, 0., self.linespacing);
  318. let last_idx = if self.lines.is_empty() { 0 } else { self.lines.len() - 1 };
  319. (final_rect, last_idx)
  320. }
  321. }
  322. #[derive(Clone)]
  323. struct TouchStartInfo {
  324. pos: Point,
  325. instant: std::time::Instant,
  326. }
  327. impl TouchStartInfo {
  328. fn new(pos: Point) -> Self {
  329. Self { pos, instant: std::time::Instant::now() }
  330. }
  331. }
  332. #[derive(Clone)]
  333. enum TouchStateAction {
  334. Inactive,
  335. Started { pos: Point, instant: std::time::Instant },
  336. StartSelect,
  337. Select,
  338. DragSelectHandle { side: isize },
  339. ScrollVert { start_pos: Point, scroll_start: f32 },
  340. SetCursorPos,
  341. }
  342. struct TouchInfo {
  343. state: TouchStateAction,
  344. start: Option<TouchStartInfo>,
  345. scroll: PropertyFloat32,
  346. }
  347. impl TouchInfo {
  348. fn new(scroll: PropertyFloat32) -> Self {
  349. Self { state: TouchStateAction::Inactive, start: None, scroll }
  350. }
  351. fn start(&mut self, pos: Point) {
  352. debug!(target: "ui::chatedit::touch", "start touch: Started state");
  353. self.state = TouchStateAction::Started { pos, instant: std::time::Instant::now() };
  354. }
  355. fn stop(&mut self) -> TouchStateAction {
  356. debug!(target: "ui::chatedit::touch", "stop touch: Inactive state");
  357. std::mem::replace(&mut self.state, TouchStateAction::Inactive)
  358. }
  359. fn update(&mut self, pos: &Point) {
  360. match &self.state {
  361. TouchStateAction::Started { pos: start_pos, instant } => {
  362. let travel_dist = pos.dist_sq(&start_pos);
  363. let grad = (pos.y - start_pos.y) / (pos.x - start_pos.x);
  364. let elapsed = instant.elapsed().as_millis();
  365. //debug!(target: "ui::chatedit::touch", "TouchInfo::update() [travel_dist={travel_dist}, grad={grad}]");
  366. if travel_dist < 5. {
  367. if elapsed > 1000 {
  368. debug!(target: "ui::chatedit::touch", "update touch state: Started -> StartSelect");
  369. self.state = TouchStateAction::StartSelect;
  370. }
  371. } else if grad.abs() > 0.5 {
  372. // Vertical movement
  373. debug!(target: "ui::chatedit::touch", "update touch state: Started -> ScrollVert");
  374. let scroll_start = self.scroll.get();
  375. self.state =
  376. TouchStateAction::ScrollVert { start_pos: *start_pos, scroll_start };
  377. } else {
  378. // Horizontal movement
  379. debug!(target: "ui::chatedit::touch", "update touch state: Started -> SetCursorPos");
  380. self.state = TouchStateAction::SetCursorPos;
  381. }
  382. }
  383. _ => {}
  384. }
  385. }
  386. }
  387. enum ColoringState {
  388. Start,
  389. IsCommand,
  390. Normal,
  391. }
  392. pub type ChatEditPtr = Arc<ChatEdit>;
  393. pub struct ChatEdit {
  394. node: SceneNodeWeak,
  395. tasks: OnceLock<Vec<smol::Task<()>>>,
  396. render_api: RenderApi,
  397. text_shaper: TextShaperPtr,
  398. key_repeat: SyncMutex<PressedKeysSmoothRepeat>,
  399. glyphs: SyncMutex<Vec<Glyph>>,
  400. main_dc_key: u64,
  401. /// DC key for the text
  402. text_dc_key: u64,
  403. cursor_mesh: SyncMutex<Option<GfxDrawMesh>>,
  404. /// DC key for the cursor. Allows updating cursor independently.
  405. cursor_dc_key: u64,
  406. is_active: PropertyBool,
  407. is_focused: PropertyBool,
  408. max_height: PropertyFloat32,
  409. rect: PropertyRect,
  410. baseline: PropertyFloat32,
  411. linespacing: PropertyFloat32,
  412. descent: PropertyFloat32,
  413. scroll: PropertyFloat32,
  414. scroll_speed: PropertyFloat32,
  415. padding: PropertyPtr,
  416. cursor_pos: PropertyUint32,
  417. font_size: PropertyFloat32,
  418. text: PropertyStr,
  419. text_color: PropertyColor,
  420. text_hi_color: PropertyColor,
  421. text_cmd_color: PropertyColor,
  422. cursor_color: PropertyColor,
  423. cursor_width: PropertyFloat32,
  424. cursor_ascent: PropertyFloat32,
  425. cursor_descent: PropertyFloat32,
  426. cursor_blink_time: PropertyUint32,
  427. cursor_idle_time: PropertyUint32,
  428. hi_bg_color: PropertyColor,
  429. cmd_bg_color: PropertyColor,
  430. select_ascent: PropertyFloat32,
  431. select_descent: PropertyFloat32,
  432. handle_descent: PropertyFloat32,
  433. select_text: PropertyPtr,
  434. z_index: PropertyUint32,
  435. priority: PropertyUint32,
  436. debug: PropertyBool,
  437. text_wrap: SyncMutex<TextWrap>,
  438. mouse_btn_held: AtomicBool,
  439. cursor_is_visible: AtomicBool,
  440. blink_is_paused: AtomicBool,
  441. /// Used to explicitly hide the cursor. Must be manually re-enabled.
  442. hide_cursor: AtomicBool,
  443. touch_info: SyncMutex<TouchInfo>,
  444. is_phone_select: AtomicBool,
  445. old_window_scale: AtomicF32,
  446. window_scale: PropertyFloat32,
  447. parent_rect: SyncMutex<Option<Rectangle>>,
  448. is_mouse_hover: AtomicBool,
  449. }
  450. impl ChatEdit {
  451. pub async fn new(
  452. node: SceneNodeWeak,
  453. window_scale: PropertyFloat32,
  454. render_api: RenderApi,
  455. text_shaper: TextShaperPtr,
  456. ex: ExecutorPtr,
  457. ) -> Pimpl {
  458. t!("ChatEdit::new()");
  459. let node_ref = &node.upgrade().unwrap();
  460. let is_active = PropertyBool::wrap(node_ref, Role::Internal, "is_active", 0).unwrap();
  461. let is_focused = PropertyBool::wrap(node_ref, Role::Internal, "is_focused", 0).unwrap();
  462. let max_height = PropertyFloat32::wrap(node_ref, Role::Internal, "max_height", 0).unwrap();
  463. let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap();
  464. let baseline = PropertyFloat32::wrap(node_ref, Role::Internal, "baseline", 0).unwrap();
  465. let linespacing =
  466. PropertyFloat32::wrap(node_ref, Role::Internal, "linespacing", 0).unwrap();
  467. let descent = PropertyFloat32::wrap(node_ref, Role::Internal, "descent", 0).unwrap();
  468. let scroll = PropertyFloat32::wrap(node_ref, Role::Internal, "scroll", 0).unwrap();
  469. let scroll_speed =
  470. PropertyFloat32::wrap(node_ref, Role::Internal, "scroll_speed", 0).unwrap();
  471. let padding = node_ref.get_property("padding").unwrap();
  472. let cursor_pos = PropertyUint32::wrap(node_ref, Role::Internal, "cursor_pos", 0).unwrap();
  473. let font_size = PropertyFloat32::wrap(node_ref, Role::Internal, "font_size", 0).unwrap();
  474. let text = PropertyStr::wrap(node_ref, Role::Internal, "text", 0).unwrap();
  475. let text_color = PropertyColor::wrap(node_ref, Role::Internal, "text_color").unwrap();
  476. let text_hi_color = PropertyColor::wrap(node_ref, Role::Internal, "text_hi_color").unwrap();
  477. let text_cmd_color =
  478. PropertyColor::wrap(node_ref, Role::Internal, "text_cmd_color").unwrap();
  479. let cursor_color = PropertyColor::wrap(node_ref, Role::Internal, "cursor_color").unwrap();
  480. let cursor_width =
  481. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_width", 0).unwrap();
  482. let cursor_ascent =
  483. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_ascent", 0).unwrap();
  484. let cursor_descent =
  485. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_descent", 0).unwrap();
  486. let hi_bg_color = PropertyColor::wrap(node_ref, Role::Internal, "hi_bg_color").unwrap();
  487. let cmd_bg_color = PropertyColor::wrap(node_ref, Role::Internal, "cmd_bg_color").unwrap();
  488. let select_ascent =
  489. PropertyFloat32::wrap(node_ref, Role::Internal, "select_ascent", 0).unwrap();
  490. let select_descent =
  491. PropertyFloat32::wrap(node_ref, Role::Internal, "select_descent", 0).unwrap();
  492. let handle_descent =
  493. PropertyFloat32::wrap(node_ref, Role::Internal, "handle_descent", 0).unwrap();
  494. let select_text = node_ref.get_property("select_text").unwrap();
  495. let cursor_blink_time =
  496. PropertyUint32::wrap(node_ref, Role::Internal, "cursor_blink_time", 0).unwrap();
  497. let cursor_idle_time =
  498. PropertyUint32::wrap(node_ref, Role::Internal, "cursor_idle_time", 0).unwrap();
  499. let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
  500. let priority = PropertyUint32::wrap(node_ref, Role::Internal, "priority", 0).unwrap();
  501. let debug = PropertyBool::wrap(node_ref, Role::Internal, "debug", 0).unwrap();
  502. let node_name = node_ref.name.clone();
  503. let node_id = node_ref.id;
  504. // Must do this whenever the text changes
  505. let glyphs = text_shaper.shape(text.get(), font_size.get(), window_scale.get());
  506. let self_ = Arc::new(Self {
  507. node,
  508. tasks: OnceLock::new(),
  509. render_api,
  510. text_shaper: text_shaper.clone(),
  511. key_repeat: SyncMutex::new(PressedKeysSmoothRepeat::new(400, 50)),
  512. glyphs: SyncMutex::new(glyphs),
  513. main_dc_key: OsRng.gen(),
  514. text_dc_key: OsRng.gen(),
  515. cursor_mesh: SyncMutex::new(None),
  516. cursor_dc_key: OsRng.gen(),
  517. is_active,
  518. is_focused,
  519. max_height,
  520. rect,
  521. baseline: baseline.clone(),
  522. linespacing: linespacing.clone(),
  523. descent,
  524. scroll: scroll.clone(),
  525. scroll_speed,
  526. padding,
  527. cursor_pos,
  528. font_size: font_size.clone(),
  529. text,
  530. text_color,
  531. text_hi_color,
  532. text_cmd_color,
  533. cursor_color,
  534. cursor_width,
  535. cursor_ascent,
  536. cursor_descent,
  537. cursor_blink_time,
  538. cursor_idle_time,
  539. hi_bg_color,
  540. cmd_bg_color,
  541. select_ascent,
  542. select_descent,
  543. handle_descent,
  544. select_text,
  545. z_index,
  546. priority,
  547. debug,
  548. text_wrap: SyncMutex::new(TextWrap::new(
  549. text_shaper,
  550. font_size,
  551. window_scale.clone(),
  552. baseline,
  553. linespacing,
  554. )),
  555. mouse_btn_held: AtomicBool::new(false),
  556. cursor_is_visible: AtomicBool::new(true),
  557. blink_is_paused: AtomicBool::new(false),
  558. hide_cursor: AtomicBool::new(false),
  559. touch_info: SyncMutex::new(TouchInfo::new(scroll)),
  560. is_phone_select: AtomicBool::new(false),
  561. old_window_scale: AtomicF32::new(window_scale.get()),
  562. window_scale,
  563. parent_rect: SyncMutex::new(None),
  564. is_mouse_hover: AtomicBool::new(false),
  565. });
  566. //self_
  567. // .text_wrap
  568. // .lock()
  569. // .editable
  570. // .set_text("".to_string(), "king!😁🍆jelly 🍆1234".to_string());
  571. //self_.text_wrap.lock().editable.set_text(
  572. // "".to_string(),
  573. // "A berry is a small, pulpy, and often edible fruit. Typically, berries are juicy, rounded, brightly colored, sweet, sour or tart, and do not have a stone or pit, although many pips or seeds may be present. Common examples of berries in the culinary sense are strawberries, raspberries, blueberries, blackberries, white currants, blackcurrants, and redcurrants. In Britain, soft fruit is a horticultural term for such fruits. The common usage of the term berry is different from the scientific or botanical definition of a berry, which refers to a fruit produced from the ovary of a single flower where the outer layer of the ovary wall develops into an edible fleshy portion (pericarp). The botanical definition includes many fruits that are not commonly known or referred to as berries, such as grapes, tomatoes, cucumbers, eggplants, bananas, and chili peppers.".to_string()
  574. //);
  575. //self_
  576. // .text_wrap
  577. // .lock()
  578. // .editable
  579. // .set_text("A berry is small and pulpy.".to_string(), "".to_string());
  580. Pimpl::ChatEdit(self_)
  581. }
  582. fn node(&self) -> SceneNodePtr {
  583. self.node.upgrade().unwrap()
  584. }
  585. fn wrap_width(&self) -> f32 {
  586. let w = self.rect.prop().get_f32(2).unwrap() - self.cursor_width.get();
  587. if w < 0. {
  588. return 0.
  589. }
  590. w
  591. }
  592. fn abs_to_local(&self, point: &mut Point) {
  593. let rect = self.rect.get();
  594. *point -= rect.pos();
  595. point.y += self.scroll.get();
  596. }
  597. /// Called whenever the text or any text property changes.
  598. fn regen_text_mesh(&self) -> GfxDrawMesh {
  599. let is_focused = self.is_focused.get();
  600. let text = self.text.get();
  601. let font_size = self.font_size.get();
  602. let window_scale = self.window_scale.get();
  603. let text_color = self.text_color.get();
  604. let text_hi_color = self.text_hi_color.get();
  605. let text_cmd_color = self.text_cmd_color.get();
  606. let linespacing = self.linespacing.get();
  607. let baseline = self.baseline.get();
  608. let scroll = self.scroll.get();
  609. let cursor_color = self.cursor_color.get();
  610. let debug = self.debug.get();
  611. let parent_rect = self.parent_rect.lock().clone().unwrap();
  612. self.rect.eval_with(
  613. vec![2],
  614. vec![("parent_w".to_string(), parent_rect.w), ("parent_h".to_string(), parent_rect.h)],
  615. );
  616. // Height is calculated from width based on wrapping
  617. let width = self.wrap_width();
  618. let (atlas, wrapped_lines, selections, under_start, under_end) = {
  619. let mut text_wrap = self.text_wrap.lock();
  620. // Must happen after rect eval, which is inside regen_text_mesh
  621. // Maybe we should take the eval out of here.
  622. self.clamp_scroll(&mut text_wrap);
  623. let rendered = text_wrap.get_render();
  624. let under_start = rendered.under_start;
  625. let under_end = rendered.under_end;
  626. let atlas = text::make_texture_atlas(&self.render_api, &rendered.glyphs);
  627. let wrapped_lines = text_wrap.wrap(width);
  628. let selections = text_wrap.select.clone();
  629. (atlas, wrapped_lines, selections, under_start, under_end)
  630. };
  631. let mut height = wrapped_lines.height() + self.descent.get();
  632. height = height.clamp(0., self.max_height.get());
  633. self.rect.prop().set_f32(Role::Internal, 3, height);
  634. // Eval the rect
  635. let parent_rect = self.parent_rect.lock().clone().unwrap();
  636. self.rect.eval_with(
  637. vec![0, 1, 3],
  638. vec![
  639. ("parent_w".to_string(), parent_rect.w),
  640. ("parent_h".to_string(), parent_rect.h),
  641. ("rect_h".to_string(), height),
  642. ],
  643. );
  644. let mut clip = self.rect.get();
  645. //debug!(target: "ui::chatedit", "Rendering text '{text}' rect={clip:?} width={width}");
  646. clip.x = 0.;
  647. clip.y = 0.;
  648. let mut mesh = MeshBuilder::with_clip(clip.clone());
  649. let mut curr_y = -scroll;
  650. //debug!(target: "ui::chatedit", "regen_text_mesh() selections={selections:?}");
  651. for (line_idx, wrap_line) in wrapped_lines.lines.iter().enumerate() {
  652. // Instead of bools, maybe we should have a GlyphStyle enum
  653. let select_marks = self.mark_selected_glyphs(&wrap_line, &selections);
  654. let hi_bg_color = self.hi_bg_color.get();
  655. self.draw_text_bg_box(&mut mesh, &select_marks, &wrap_line, curr_y, hi_bg_color);
  656. let cmd_marks = if line_idx == 0 {
  657. self.mark_command_glyphs(&wrap_line)
  658. } else {
  659. vec![false; wrap_line.len()]
  660. };
  661. let cmd_bg_color = self.cmd_bg_color.get();
  662. self.draw_text_bg_box(&mut mesh, &cmd_marks, &wrap_line, curr_y, cmd_bg_color);
  663. if under_start != under_end {
  664. assert!(under_start < under_end);
  665. self.draw_underline(&mut mesh, &wrap_line, curr_y, under_start, under_end);
  666. }
  667. let pos_iter = wrap_line.pos_iter();
  668. for (_, mut glyph_rect, glyph, is_selected, is_cmd) in zip4(
  669. pos_iter,
  670. wrap_line.glyphs.iter(),
  671. select_marks.into_iter(),
  672. cmd_marks.into_iter(),
  673. ) {
  674. let uv_rect = atlas.fetch_uv(glyph.glyph_id).expect("missing glyph UV rect");
  675. glyph_rect.y += curr_y;
  676. //mesh.draw_outline(&glyph_rect, COLOR_BLUE, 2.);
  677. let mut color = text_color.clone();
  678. if is_selected {
  679. color = text_hi_color.clone();
  680. } else if is_cmd {
  681. color = text_cmd_color.clone();
  682. }
  683. if glyph.sprite.has_color {
  684. color = COLOR_WHITE;
  685. }
  686. mesh.draw_box(&glyph_rect, color, uv_rect);
  687. if self.debug.get() {
  688. mesh.draw_outline(&glyph_rect, COLOR_RED, 1.);
  689. }
  690. }
  691. curr_y += linespacing;
  692. }
  693. // Just an assert
  694. if self.is_phone_select.load(Ordering::Relaxed) {
  695. assert_eq!(selections.len(), 1);
  696. let select = selections.last().unwrap();
  697. self.draw_phone_select_handle(&mut mesh, select.start, &wrapped_lines, -1.);
  698. self.draw_phone_select_handle(&mut mesh, select.end, &wrapped_lines, 1.);
  699. }
  700. if self.debug.get() {
  701. mesh.draw_outline(&clip, COLOR_BLUE, 1.);
  702. }
  703. mesh.alloc(&self.render_api).draw_with_texture(atlas.texture)
  704. }
  705. fn regen_cursor_mesh(&self) -> GfxDrawMesh {
  706. let cursor_width = self.cursor_width.get();
  707. let cursor_ascent = self.cursor_ascent.get();
  708. let cursor_descent = self.cursor_descent.get();
  709. let baseline = self.baseline.get();
  710. let cursor_rect = Rectangle {
  711. x: 0.,
  712. y: baseline - cursor_ascent,
  713. w: cursor_width,
  714. h: cursor_ascent + cursor_descent,
  715. };
  716. let cursor_color = self.cursor_color.get();
  717. let mut mesh = MeshBuilder::new();
  718. mesh.draw_filled_box(&cursor_rect, cursor_color);
  719. mesh.alloc(&self.render_api).draw_untextured()
  720. }
  721. fn get_cursor_pos(&self) -> Point {
  722. assert!(self.is_focused.get());
  723. let font_size = self.font_size.get();
  724. let window_scale = self.window_scale.get();
  725. let linespacing = self.linespacing.get();
  726. let scroll = self.scroll.get();
  727. let width = self.wrap_width();
  728. let (cursor_pos, wrapped_lines) = {
  729. let mut text_wrap = self.text_wrap.lock();
  730. (text_wrap.cursor_pos(), text_wrap.wrap(width))
  731. };
  732. let glyph_info = wrapped_lines.get_glyph_info(cursor_pos);
  733. let lineidx = glyph_info.1;
  734. let mut point = glyph_info.0.pos();
  735. point.x = point.x.clamp(0., width);
  736. point.y = lineidx as f32 * linespacing - scroll;
  737. point
  738. }
  739. fn mark_selected_glyphs(
  740. &self,
  741. wrap_line: &WrappedLine,
  742. selections: &Vec<Selection>,
  743. ) -> Vec<bool> {
  744. let mut marks = vec![false; wrap_line.len()];
  745. for select in selections {
  746. let mut start = std::cmp::min(select.start, select.end);
  747. let mut end = std::cmp::max(select.start, select.end);
  748. let off_pos = wrap_line.off_pos;
  749. if end < off_pos || start > wrap_line.last_pos() {
  750. continue
  751. }
  752. end = std::cmp::min(end, wrap_line.last_pos()) - off_pos;
  753. start = start.saturating_sub(off_pos);
  754. for i in start..end {
  755. marks[i] = true;
  756. }
  757. }
  758. marks
  759. }
  760. fn mark_command_glyphs(&self, wrap_line: &WrappedLine) -> Vec<bool> {
  761. let mut state = ColoringState::Start;
  762. let mut marks = vec![false; wrap_line.len()];
  763. for (idx, glyph) in wrap_line.glyphs.iter().enumerate() {
  764. match state {
  765. ColoringState::Start => {
  766. if glyph.substr == "/" {
  767. state = ColoringState::IsCommand
  768. } else {
  769. state = ColoringState::Normal
  770. }
  771. }
  772. ColoringState::IsCommand => {
  773. if is_whitespace(&glyph.substr) {
  774. state = ColoringState::Normal
  775. }
  776. }
  777. _ => {}
  778. }
  779. match state {
  780. ColoringState::IsCommand => marks[idx] = true,
  781. _ => {}
  782. }
  783. }
  784. marks
  785. }
  786. fn draw_text_bg_box(
  787. &self,
  788. mesh: &mut MeshBuilder,
  789. select_marks: &Vec<bool>,
  790. wrap_line: &WrappedLine,
  791. y_off: f32,
  792. color: Color,
  793. ) {
  794. let font_size = self.font_size.get();
  795. let baseline = self.baseline.get();
  796. let select_ascent = self.select_ascent.get();
  797. let select_descent = self.select_descent.get();
  798. if select_marks.iter().all(|b| !b) {
  799. return
  800. }
  801. let mut start_x = None;
  802. let mut end_x = None;
  803. for (glyph_rect, is_selected) in wrap_line.pos_iter().zip(select_marks.iter()) {
  804. if *is_selected && start_x.is_none() {
  805. start_x = Some(glyph_rect.x);
  806. }
  807. if !*is_selected && start_x.is_some() && end_x.is_none() {
  808. end_x = Some(glyph_rect.x);
  809. }
  810. }
  811. let mut start_x = start_x.unwrap();
  812. if select_marks[0] {
  813. start_x = 0.;
  814. }
  815. let end_x = match end_x {
  816. Some(end_x) => end_x,
  817. None => {
  818. assert!(*select_marks.last().unwrap());
  819. wrap_line.rhs() + eol_nudge(font_size, &wrap_line.glyphs)
  820. }
  821. };
  822. let select_ascent = self.select_ascent.get();
  823. let select_descent = self.select_descent.get();
  824. let select_rect = Rectangle {
  825. x: start_x,
  826. y: y_off + baseline - select_ascent,
  827. w: end_x - start_x,
  828. h: select_ascent + select_descent,
  829. };
  830. mesh.draw_box(&select_rect, color, &Rectangle::zero());
  831. }
  832. fn draw_underline(
  833. &self,
  834. mesh: &mut MeshBuilder,
  835. wrap_line: &WrappedLine,
  836. y_off: f32,
  837. under_start: usize,
  838. under_end: usize,
  839. ) {
  840. if under_start >= wrap_line.last_pos() {
  841. return
  842. }
  843. if under_end < wrap_line.first_pos() {
  844. return
  845. }
  846. assert!(under_start < under_end);
  847. let baseline = self.baseline.get();
  848. let text_color = self.text_color.get();
  849. // Doing it like this but with advance should be easier and shorter.
  850. //let start = start as isize - self.start_pos as isize;
  851. //let end = end as isize - self.start_pos as isize;
  852. //let line_start = std::cmp::max(0, start) as usize;
  853. //assert!(end > 0);
  854. //let line_end = std::cmp::min(self.marks.len() as isize, end) as usize;
  855. let mut start_x = 0.;
  856. let mut end_x = 0.;
  857. // When cursor lands at the end of the line
  858. let mut rhs = 0.;
  859. for (glyph_idx, mut glyph_rect) in wrap_line.pos_iter().enumerate() {
  860. if glyph_idx == under_start {
  861. start_x = glyph_rect.x;
  862. }
  863. if glyph_idx == under_end {
  864. end_x = glyph_rect.x;
  865. }
  866. rhs = glyph_rect.rhs();
  867. }
  868. if under_start == 0 {
  869. start_x = 0.;
  870. }
  871. if under_end == wrap_line.last_pos() {
  872. end_x = rhs;
  873. }
  874. // We don't need to do manual clipping since MeshBuilder should do that
  875. let underline_rect =
  876. Rectangle { x: start_x, y: y_off + baseline + 6., w: end_x - start_x, h: 4. };
  877. mesh.draw_box(&underline_rect, text_color, &Rectangle::zero());
  878. }
  879. fn draw_phone_select_handle(
  880. &self,
  881. mesh: &mut MeshBuilder,
  882. gpos: TextPos,
  883. wrapped_lines: &WrappedLines,
  884. side: f32,
  885. ) {
  886. //debug!(target: "ui::chatedit", "draw_phone_select_handle(..., {x}, {side})");
  887. let baseline = self.baseline.get();
  888. let select_ascent = self.select_ascent.get();
  889. let handle_descent = self.handle_descent.get();
  890. let color = self.text_hi_color.get();
  891. let linespacing = self.linespacing.get();
  892. let scroll = self.scroll.get();
  893. // Transparent for fade
  894. let mut color_trans = color.clone();
  895. color_trans[3] = 0.;
  896. // find start_x
  897. let (glyph_rect, line_idx) = wrapped_lines.get_glyph_info(gpos);
  898. let x = glyph_rect.x;
  899. let y_off = line_idx as f32 * linespacing - scroll;
  900. let y = y_off + baseline + handle_descent;
  901. if y < 0. {
  902. return
  903. }
  904. // Vertical line downwards. We use this instead of draw_box() so we have a fade.
  905. let verts = vec![
  906. Vertex {
  907. pos: [x - side * 1., y_off + baseline - select_ascent],
  908. color: color_trans,
  909. uv: [0., 0.],
  910. },
  911. Vertex {
  912. pos: [x + side * 4., y_off + baseline - select_ascent],
  913. color: color_trans,
  914. uv: [0., 0.],
  915. },
  916. Vertex {
  917. pos: [x - side * 1., y_off + baseline + handle_descent + 5.],
  918. color,
  919. uv: [0., 0.],
  920. },
  921. Vertex {
  922. pos: [x + side * 4., y_off + baseline + handle_descent + 5.],
  923. color,
  924. uv: [0., 0.],
  925. },
  926. ];
  927. let indices = vec![0, 2, 1, 1, 2, 3];
  928. mesh.append(verts, indices);
  929. // The arrow itself.
  930. // Go anti-clockwise
  931. let verts = vec![
  932. Vertex { pos: [x, y], color, uv: [0., 0.] },
  933. Vertex { pos: [x, y + 50.], color, uv: [0., 0.] },
  934. Vertex { pos: [x + side * 30., y + 50.], color, uv: [0., 0.] },
  935. Vertex { pos: [x + side * 50., y + 25.], color, uv: [0., 0.] },
  936. ];
  937. let indices = vec![0, 1, 2, 0, 2, 3];
  938. mesh.append(verts, indices);
  939. }
  940. async fn change_focus(self: Arc<Self>) {
  941. if !self.is_active.get() {
  942. return
  943. }
  944. t!("Focus changed");
  945. // Cursor visibility will change so just redraw everything lol
  946. self.redraw().await;
  947. }
  948. async fn insert_char(&self, key: char) {
  949. t!("insert_char({key})");
  950. let mut tmp = [0; 4];
  951. let key_str = key.encode_utf8(&mut tmp);
  952. self.insert_text(key_str).await
  953. }
  954. async fn insert_text(&self, text: &str) {
  955. t!("insert_text({text})");
  956. let text = {
  957. let mut text_wrap = &mut self.text_wrap.lock();
  958. text_wrap.clear_cache();
  959. if !text_wrap.select.is_empty() {
  960. text_wrap.delete_selected();
  961. self.update_select_text(&mut text_wrap);
  962. self.is_phone_select.store(false, Ordering::Relaxed);
  963. // Reshow cursor (if hidden)
  964. self.hide_cursor.store(false, Ordering::Relaxed);
  965. }
  966. text_wrap.editable.compose(text, true);
  967. text_wrap.editable.get_text()
  968. };
  969. self.text.set(text);
  970. self.pause_blinking();
  971. self.redraw().await;
  972. }
  973. async fn handle_shortcut(&self, key: char, mods: &KeyMods) -> bool {
  974. t!("handle_shortcut({:?}, {:?})", key, mods);
  975. match key {
  976. 'a' => {
  977. if mods.ctrl {
  978. {
  979. let mut text_wrap = self.text_wrap.lock();
  980. let rendered = text_wrap.get_render();
  981. let end_pos = rendered.glyphs.len();
  982. let select = &mut text_wrap.select;
  983. select.clear();
  984. select.push(Selection::new(0, end_pos));
  985. self.update_select_text(&mut text_wrap);
  986. }
  987. self.redraw().await;
  988. return true
  989. }
  990. }
  991. 'c' => {
  992. if mods.ctrl {
  993. //self.copy_highlighted().unwrap();
  994. return true
  995. }
  996. }
  997. 'v' => {
  998. if mods.ctrl {
  999. let mut clip = Clipboard::new();
  1000. if let Some(text) = clip.get() {
  1001. self.insert_text(&text).await;
  1002. }
  1003. return true
  1004. }
  1005. }
  1006. _ => {}
  1007. }
  1008. false
  1009. }
  1010. async fn handle_key(&self, key: &KeyCode, mods: &KeyMods) -> bool {
  1011. t!("handle_key({:?}, {:?})", key, mods);
  1012. match key {
  1013. KeyCode::Left => {
  1014. if !self.adjust_cursor(&mods, |editable| editable.move_cursor(-1)) {
  1015. return false
  1016. }
  1017. self.pause_blinking();
  1018. //self.apply_cursor_scrolling();
  1019. self.redraw().await;
  1020. return true
  1021. }
  1022. KeyCode::Right => {
  1023. if !self.adjust_cursor(&mods, |editable| editable.move_cursor(1)) {
  1024. return false
  1025. }
  1026. self.pause_blinking();
  1027. //self.apply_cursor_scrolling();
  1028. self.redraw().await;
  1029. return true
  1030. }
  1031. KeyCode::Kp0 => {
  1032. self.insert_char('0').await;
  1033. return true
  1034. }
  1035. KeyCode::Kp1 => {
  1036. self.insert_char('1').await;
  1037. return true
  1038. }
  1039. KeyCode::Kp2 => {
  1040. self.insert_char('2').await;
  1041. return true
  1042. }
  1043. KeyCode::Kp3 => {
  1044. self.insert_char('3').await;
  1045. return true
  1046. }
  1047. KeyCode::Kp4 => {
  1048. self.insert_char('4').await;
  1049. return true
  1050. }
  1051. KeyCode::Kp5 => {
  1052. self.insert_char('5').await;
  1053. return true
  1054. }
  1055. KeyCode::Kp6 => {
  1056. self.insert_char('6').await;
  1057. return true
  1058. }
  1059. KeyCode::Kp7 => {
  1060. self.insert_char('7').await;
  1061. return true
  1062. }
  1063. KeyCode::Kp8 => {
  1064. self.insert_char('8').await;
  1065. return true
  1066. }
  1067. KeyCode::Kp9 => {
  1068. self.insert_char('9').await;
  1069. return true
  1070. }
  1071. KeyCode::KpDecimal => {
  1072. self.insert_char('.').await;
  1073. return true
  1074. }
  1075. KeyCode::Enter | KeyCode::KpEnter => {
  1076. if mods.shift {
  1077. // Does nothing for now. Later will enable multiline.
  1078. }
  1079. }
  1080. KeyCode::Delete => {
  1081. self.delete(0, 1);
  1082. self.clamp_scroll(&mut self.text_wrap.lock());
  1083. self.pause_blinking();
  1084. self.redraw().await;
  1085. return true
  1086. }
  1087. KeyCode::Backspace => {
  1088. self.delete(1, 0);
  1089. self.clamp_scroll(&mut self.text_wrap.lock());
  1090. self.pause_blinking();
  1091. self.redraw().await;
  1092. return true
  1093. }
  1094. KeyCode::Home => {
  1095. self.adjust_cursor(&mods, |editable| editable.move_start());
  1096. self.pause_blinking();
  1097. //self.apply_cursor_scrolling();
  1098. self.redraw().await;
  1099. return true
  1100. }
  1101. KeyCode::End => {
  1102. self.adjust_cursor(&mods, |editable| editable.move_end());
  1103. self.pause_blinking();
  1104. //self.apply_cursor_scrolling();
  1105. self.redraw().await;
  1106. return true
  1107. }
  1108. _ => {}
  1109. }
  1110. false
  1111. }
  1112. fn delete(&self, before: usize, after: usize) {
  1113. let mut text_wrap = &mut self.text_wrap.lock();
  1114. if text_wrap.select.is_empty() {
  1115. text_wrap.editable.delete(before, after);
  1116. text_wrap.clear_cache();
  1117. } else {
  1118. text_wrap.delete_selected();
  1119. self.update_select_text(&mut text_wrap);
  1120. }
  1121. self.is_phone_select.store(false, Ordering::Relaxed);
  1122. // Reshow cursor (if hidden)
  1123. self.hide_cursor.store(false, Ordering::Relaxed);
  1124. let text = text_wrap.editable.get_text();
  1125. self.text.set(text);
  1126. }
  1127. fn adjust_cursor(&self, mods: &KeyMods, move_cursor: impl Fn(&mut Editable)) -> bool {
  1128. if mods.ctrl || mods.alt || mods.logo {
  1129. return false
  1130. }
  1131. let mut text_wrap = &mut self.text_wrap.lock();
  1132. let rendered = text_wrap.get_render().clone();
  1133. let prev_cursor_pos = text_wrap.editable.get_cursor_pos(&rendered);
  1134. move_cursor(&mut text_wrap.editable);
  1135. let cursor_pos = text_wrap.editable.get_cursor_pos(&rendered);
  1136. d!("Adjust cursor pos to {cursor_pos}");
  1137. let select = &mut text_wrap.select;
  1138. // Start selection if shift is held
  1139. if mods.shift {
  1140. // Create a new selection
  1141. if select.is_empty() {
  1142. select.push(Selection::new(prev_cursor_pos, cursor_pos));
  1143. }
  1144. // Update the selection
  1145. select.last_mut().unwrap().end = cursor_pos;
  1146. } else {
  1147. select.clear();
  1148. }
  1149. self.update_select_text(&mut text_wrap);
  1150. true
  1151. }
  1152. /// This will select the entire word rather than move the cursor to that location
  1153. fn start_touch_select(&self, touch_pos: Point) {
  1154. let mut text_wrap = &mut self.text_wrap.lock();
  1155. text_wrap.clear_cache();
  1156. text_wrap.editable.end_compose();
  1157. let width = self.wrap_width();
  1158. let wrapped_lines = text_wrap.wrap(width);
  1159. let pos = wrapped_lines.point_to_pos(touch_pos);
  1160. let (word_start, word_end) = text_wrap.get_word_boundary(pos);
  1161. // begin selection
  1162. let select = &mut text_wrap.select;
  1163. select.clear();
  1164. if word_start != word_end {
  1165. select.push(Selection::new(word_start, word_end));
  1166. self.is_phone_select.store(true, Ordering::Relaxed);
  1167. // redraw() will now hide the cursor
  1168. self.hide_cursor.store(true, Ordering::Relaxed);
  1169. }
  1170. d!("Selected {select:?} from {touch_pos:?}");
  1171. self.update_select_text(&mut text_wrap);
  1172. }
  1173. /// Call this whenever the selection changes to update the external property
  1174. fn update_select_text(&self, text_wrap: &mut TextWrap) {
  1175. let select = &text_wrap.select;
  1176. let Some(select) = select.first().cloned() else {
  1177. self.select_text.set_null(Role::Internal, 0).unwrap();
  1178. return
  1179. };
  1180. let start = std::cmp::min(select.start, select.end);
  1181. let end = std::cmp::max(select.start, select.end);
  1182. let rendered = text_wrap.get_render();
  1183. let glyphs = &rendered.glyphs[start..end];
  1184. let text = text::glyph_str(glyphs);
  1185. self.select_text.set_str(Role::Internal, 0, text).unwrap();
  1186. }
  1187. /// Call this whenever the cursor pos changes to update the external property
  1188. fn update_cursor_pos(&self, text_wrap: &mut TextWrap) {
  1189. let cursor_off = text_wrap.editable.get_text_before().len() as u32;
  1190. self.cursor_pos.set(cursor_off);
  1191. }
  1192. /*
  1193. fn copy_highlighted(&self) -> Result<()> {
  1194. let start = self.selected.get_u32(0)? as usize;
  1195. let end = self.selected.get_u32(1)? as usize;
  1196. let sel_start = std::cmp::min(start, end);
  1197. let sel_end = std::cmp::max(start, end);
  1198. let mut text = String::new();
  1199. let glyphs = self.glyphs.lock().clone();
  1200. for (glyph_idx, glyph) in glyphs.iter().enumerate() {
  1201. if sel_start <= glyph_idx && glyph_idx < sel_end {
  1202. text.push_str(&glyph.substr);
  1203. }
  1204. }
  1205. info!(target: "ui::chatedit", "Copied '{}'", text);
  1206. window::clipboard_set(&text);
  1207. Ok(())
  1208. }
  1209. async fn paste_text(&self, key: String) {
  1210. let mut text = String::new();
  1211. let cursor_pos = self.cursor_pos.get();
  1212. if cursor_pos == 0 {
  1213. text = key.clone();
  1214. }
  1215. let glyphs = self.glyphs.lock().clone();
  1216. for (glyph_idx, glyph) in glyphs.iter().enumerate() {
  1217. text.push_str(&glyph.substr);
  1218. if cursor_pos == glyph_idx as u32 + 1 {
  1219. text.push_str(&key);
  1220. }
  1221. }
  1222. self.text.set(text);
  1223. // Not always true lol
  1224. self.cursor_pos.set(cursor_pos + 1);
  1225. self.apply_cursor_scrolling();
  1226. self.redraw().await;
  1227. }
  1228. */
  1229. async fn handle_touch_start(&self, mut touch_pos: Point) -> bool {
  1230. t!("handle_touch_start({touch_pos:?})");
  1231. let mut touch_info = self.touch_info.lock();
  1232. if self.try_handle_drag(&mut touch_info, touch_pos) {
  1233. return true
  1234. }
  1235. let rect = self.rect.get();
  1236. if !rect.contains(touch_pos) {
  1237. return false
  1238. }
  1239. touch_info.start(touch_pos);
  1240. true
  1241. }
  1242. fn try_handle_drag(&self, touch_info: &mut TouchInfo, mut touch_pos: Point) -> bool {
  1243. // Is the handle visible? Use y within rect before adding the scroll.
  1244. let relative_y = touch_pos.y - self.rect.get().y;
  1245. if relative_y < 0. {
  1246. return false
  1247. }
  1248. self.abs_to_local(&mut touch_pos);
  1249. let linespacing = self.linespacing.get();
  1250. let baseline = self.baseline.get();
  1251. let select_descent = self.select_descent.get();
  1252. let scroll = self.scroll.get();
  1253. let mut text_wrap = self.text_wrap.lock();
  1254. let width = self.wrap_width();
  1255. let wrapped_lines = text_wrap.wrap(width);
  1256. let selections = &text_wrap.select;
  1257. if self.is_phone_select.load(Ordering::Relaxed) && selections.len() == 1 {
  1258. let select = selections.first().unwrap();
  1259. let handle_off_y = baseline + self.handle_descent.get();
  1260. // Get left handle centerpoint
  1261. let (glyph_rect, line_idx) = wrapped_lines.get_glyph_info(select.start);
  1262. let mut p1 = glyph_rect.pos();
  1263. // We always want the handles to be aligned so ignore the glyph's y pos
  1264. p1.y = line_idx as f32 * linespacing + handle_off_y;
  1265. // Get right handle centerpoint
  1266. let (glyph_rect, line_idx) = wrapped_lines.get_glyph_info(select.end);
  1267. let mut p2 = glyph_rect.top_right();
  1268. p2.y = line_idx as f32 * linespacing + handle_off_y;
  1269. // Are we within range of either one?
  1270. t!("handle center points = ({p1:?}, {p2:?})");
  1271. const TOUCH_RADIUS_SQ: f32 = 10_000.;
  1272. if p1.dist_sq(&touch_pos) <= TOUCH_RADIUS_SQ {
  1273. d!("start touch: DragSelectHandle state [side=-1]");
  1274. // Set touch_state status to enable begin dragging them
  1275. touch_info.state = TouchStateAction::DragSelectHandle { side: -1 };
  1276. return true;
  1277. }
  1278. if p2.dist_sq(&touch_pos) <= TOUCH_RADIUS_SQ {
  1279. d!("start touch: DragSelectHandle state [side=1]");
  1280. // Set touch_state status to enable begin dragging them
  1281. touch_info.state = TouchStateAction::DragSelectHandle { side: 1 };
  1282. return true;
  1283. }
  1284. }
  1285. false
  1286. }
  1287. async fn handle_touch_move(&self, mut touch_pos: Point) -> bool {
  1288. t!("handle_touch_move({touch_pos:?})");
  1289. // We must update with non relative touch_pos bcos when doing vertical scrolling
  1290. // we will modify the scroll, which is used by abs_to_local(), which is used
  1291. // to then calculate the max scroll. So it ends up jumping around.
  1292. // We use the abs touch_pos without scroll adjust applied for vert scrolling.
  1293. let touch_state = {
  1294. let mut touch_info = self.touch_info.lock();
  1295. touch_info.update(&touch_pos);
  1296. touch_info.state.clone()
  1297. };
  1298. match &touch_state {
  1299. TouchStateAction::Inactive => return false,
  1300. TouchStateAction::StartSelect => {
  1301. if self.text.get().is_empty() {
  1302. let node = self.node.upgrade().unwrap();
  1303. node.trigger("paste_request", vec![]).await.unwrap();
  1304. } else {
  1305. self.abs_to_local(&mut touch_pos);
  1306. self.start_touch_select(touch_pos);
  1307. self.redraw().await;
  1308. }
  1309. d!("touch state: StartSelect -> Select");
  1310. self.touch_info.lock().state = TouchStateAction::Select;
  1311. }
  1312. TouchStateAction::DragSelectHandle { side } => {
  1313. self.abs_to_local(&mut touch_pos);
  1314. {
  1315. let linespacing = self.linespacing.get();
  1316. let baseline = self.baseline.get();
  1317. let handle_descent = self.handle_descent.get();
  1318. let mut text_wrap = self.text_wrap.lock();
  1319. let width = self.wrap_width();
  1320. let wrapped_lines = text_wrap.wrap(width);
  1321. let rendered = text_wrap.get_render().clone();
  1322. let selections = &mut text_wrap.select;
  1323. assert!(*side == -1 || *side == 1);
  1324. assert!(self.is_phone_select.load(Ordering::Relaxed));
  1325. assert_eq!(selections.len(), 1);
  1326. let select = selections.first_mut().unwrap();
  1327. let mut point = touch_pos;
  1328. // Only allow selecting text that is visible in the box
  1329. // We do our calcs relative to (0, 0) so bhs = rect_h
  1330. point.y -= handle_descent + 25.;
  1331. let bhs = wrapped_lines.height();
  1332. point.y = min_f32(point.y, bhs);
  1333. let mut pos = wrapped_lines.point_to_pos(point);
  1334. if *side == -1 {
  1335. let select_other_pos = &mut select.end;
  1336. if pos >= *select_other_pos {
  1337. pos = *select_other_pos - 1;
  1338. }
  1339. select.start = pos;
  1340. } else {
  1341. let select_other_pos = &mut select.start;
  1342. if pos <= *select_other_pos {
  1343. pos = *select_other_pos + 1;
  1344. }
  1345. select.end = pos;
  1346. }
  1347. self.update_select_text(&mut text_wrap);
  1348. }
  1349. self.redraw().await;
  1350. }
  1351. TouchStateAction::ScrollVert { start_pos, scroll_start } => {
  1352. let max_scroll = {
  1353. let mut text_wrap = self.text_wrap.lock();
  1354. self.max_scroll(&mut text_wrap)
  1355. };
  1356. let y_dist = start_pos.y - touch_pos.y;
  1357. let mut scroll = scroll_start + y_dist;
  1358. scroll = scroll.clamp(0., max_scroll);
  1359. if (self.scroll.get() - scroll).abs() < VERT_SCROLL_UPDATE_INC {
  1360. return true
  1361. }
  1362. self.scroll.set(scroll);
  1363. self.redraw().await;
  1364. }
  1365. TouchStateAction::SetCursorPos => {
  1366. // TBH I can't even see the cursor under my thumb so I'll just
  1367. // comment this for now.
  1368. //self.abs_to_local(&mut touch_pos);
  1369. //self.touch_set_cursor_pos(touch_pos).await
  1370. }
  1371. _ => {}
  1372. }
  1373. true
  1374. }
  1375. async fn handle_touch_end(&self, mut touch_pos: Point) -> bool {
  1376. t!("handle_touch_end({touch_pos:?})");
  1377. self.abs_to_local(&mut touch_pos);
  1378. let state = self.touch_info.lock().stop();
  1379. match state {
  1380. TouchStateAction::Inactive => return false,
  1381. TouchStateAction::Started { pos: _, instant: _ } | TouchStateAction::SetCursorPos => {
  1382. self.touch_set_cursor_pos(touch_pos).await
  1383. }
  1384. _ => {}
  1385. }
  1386. let node = self.node.upgrade().unwrap();
  1387. node.trigger("keyboard_request", vec![]).await.unwrap();
  1388. true
  1389. }
  1390. async fn touch_set_cursor_pos(&self, mut touch_pos: Point) {
  1391. t!("touch_set_cursor_pos({touch_pos:?})");
  1392. let width = self.wrap_width();
  1393. {
  1394. let mut text_wrap = self.text_wrap.lock();
  1395. let cursor_pos = text_wrap.set_cursor_with_point(touch_pos, width);
  1396. self.update_cursor_pos(&mut text_wrap);
  1397. let select = &mut text_wrap.select;
  1398. let select_is_empty = select.is_empty();
  1399. select.clear();
  1400. if !select_is_empty {
  1401. self.update_select_text(&mut text_wrap);
  1402. }
  1403. }
  1404. self.is_phone_select.store(false, Ordering::Relaxed);
  1405. // Reshow cursor (if hidden)
  1406. self.pause_blinking();
  1407. self.hide_cursor.store(false, Ordering::Relaxed);
  1408. self.redraw().await;
  1409. }
  1410. /// Whenever the cursor property is modified this MUST be called
  1411. /// to recalculate the scroll x property.
  1412. fn apply_cursor_scrolling(&self) {
  1413. let rect = self.rect.get();
  1414. let cursor_pos = self.cursor_pos.get() as usize;
  1415. let cursor_width = self.cursor_width.get();
  1416. let mut scroll = self.scroll.get();
  1417. let cursor_x = {
  1418. let font_size = self.font_size.get();
  1419. let window_scale = self.window_scale.get();
  1420. let baseline = self.baseline.get();
  1421. let glyphs = self.glyphs.lock().clone();
  1422. let mut glyph_pos_iter =
  1423. GlyphPositionIter::new(font_size, window_scale, &glyphs, baseline);
  1424. if cursor_pos == 0 {
  1425. 0.
  1426. } else if cursor_pos == glyphs.len() {
  1427. let glyph_pos = glyph_pos_iter.last().unwrap();
  1428. let rhs = glyph_pos.rhs() + eol_nudge(font_size, &glyphs);
  1429. rhs
  1430. } else {
  1431. assert!(cursor_pos < glyphs.len());
  1432. let glyph_pos = glyph_pos_iter.nth(cursor_pos).expect("glyph pos mismatch glyphs");
  1433. glyph_pos.x
  1434. }
  1435. };
  1436. // The LHS and RHS of the cursor box
  1437. let cursor_lhs = cursor_x - scroll;
  1438. let cursor_rhs = cursor_lhs + cursor_width;
  1439. // RHS is outside
  1440. if cursor_rhs > rect.w {
  1441. // We want a scroll so RHS = w
  1442. // cursor_x - scroll + CURSOR_WIDTH = rect.w
  1443. scroll = cursor_x + cursor_width - rect.w;
  1444. // LHS is negative
  1445. } else if cursor_lhs < 0. {
  1446. // We want scroll so LHS = 0
  1447. // cursor_x - scroll = 0
  1448. scroll = cursor_x;
  1449. }
  1450. self.scroll.set(scroll);
  1451. }
  1452. fn max_scroll(&self, text_wrap: &mut TextWrap) -> f32 {
  1453. let width = self.wrap_width();
  1454. let mut inner_height = text_wrap.wrap(width).height();
  1455. // Top padding
  1456. inner_height += self.padding.get_f32(0).unwrap();
  1457. // Bottom padding
  1458. inner_height += self.padding.get_f32(1).unwrap();
  1459. let max_height = self.max_height.get();
  1460. if inner_height < max_height {
  1461. return 0.
  1462. }
  1463. let max_scroll = inner_height - max_height;
  1464. max_scroll.clamp(0., f32::MAX)
  1465. }
  1466. /// When we resize the screen, the rect changes so we may need to alter the scroll.
  1467. /// Or if we delete text.
  1468. fn clamp_scroll(&self, text_wrap: &mut TextWrap) {
  1469. let max_scroll = self.max_scroll(text_wrap);
  1470. let mut scroll = self.scroll.get();
  1471. if scroll > max_scroll {
  1472. self.scroll.set(max_scroll);
  1473. }
  1474. }
  1475. fn pause_blinking(&self) {
  1476. self.blink_is_paused.store(true, Ordering::Relaxed);
  1477. self.cursor_is_visible.store(true, Ordering::Relaxed);
  1478. }
  1479. async fn redraw(&self) {
  1480. let timest = unixtime();
  1481. t!("redraw()");
  1482. let Some(draw_update) = self.make_draw_calls() else {
  1483. error!(target: "ui::chatedit", "Text failed to draw");
  1484. return;
  1485. };
  1486. self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
  1487. }
  1488. fn redraw_cursor(&self) {
  1489. let timest = unixtime();
  1490. let cursor_instrs = self.get_cursor_instrs();
  1491. let draw_calls = vec![(
  1492. self.cursor_dc_key,
  1493. GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
  1494. )];
  1495. self.render_api.replace_draw_calls(timest, draw_calls);
  1496. }
  1497. fn get_cursor_instrs(&self) -> Vec<GfxDrawInstruction> {
  1498. if !self.is_focused.get() ||
  1499. !self.cursor_is_visible.load(Ordering::Relaxed) ||
  1500. self.hide_cursor.load(Ordering::Relaxed)
  1501. {
  1502. return vec![]
  1503. }
  1504. let mut cursor_instrs = vec![];
  1505. let cursor_pos = self.get_cursor_pos();
  1506. // There is some mess here since ApplyView is in abs screen coords but should be
  1507. // relative, and also work together with Move. We will fix this later in gfx subsys.
  1508. let mut view_rect = self.rect.get();
  1509. view_rect.h = self.max_height.get();
  1510. cursor_instrs.push(GfxDrawInstruction::ApplyView(view_rect));
  1511. cursor_instrs.push(GfxDrawInstruction::Move(cursor_pos));
  1512. let cursor_mesh = {
  1513. let mut cursor_mesh = self.cursor_mesh.lock();
  1514. if cursor_mesh.is_none() {
  1515. *cursor_mesh = Some(self.regen_cursor_mesh());
  1516. }
  1517. cursor_mesh.clone().unwrap()
  1518. };
  1519. cursor_instrs.push(GfxDrawInstruction::Draw(cursor_mesh));
  1520. cursor_instrs
  1521. }
  1522. fn make_draw_calls(&self) -> Option<DrawUpdate> {
  1523. let text_mesh = self.regen_text_mesh();
  1524. let cursor_instrs = self.get_cursor_instrs();
  1525. let rect = self.rect.get();
  1526. Some(DrawUpdate {
  1527. key: self.main_dc_key,
  1528. draw_calls: vec![
  1529. (
  1530. self.main_dc_key,
  1531. GfxDrawCall {
  1532. instrs: vec![],
  1533. dcs: vec![self.text_dc_key, self.cursor_dc_key],
  1534. z_index: self.z_index.get(),
  1535. },
  1536. ),
  1537. (
  1538. self.text_dc_key,
  1539. GfxDrawCall {
  1540. instrs: vec![
  1541. GfxDrawInstruction::Move(rect.pos()),
  1542. GfxDrawInstruction::Draw(text_mesh),
  1543. ],
  1544. dcs: vec![],
  1545. z_index: self.z_index.get(),
  1546. },
  1547. ),
  1548. (
  1549. self.cursor_dc_key,
  1550. GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
  1551. ),
  1552. ],
  1553. })
  1554. }
  1555. async fn process_insert_text_method(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  1556. let Ok(method_call) = sub.receive().await else {
  1557. debug!(target: "ui::chatedit", "Event relayer closed");
  1558. return false
  1559. };
  1560. t!("method called: insert_line({method_call:?})");
  1561. assert!(method_call.send_res.is_none());
  1562. fn decode_data(data: &[u8]) -> std::io::Result<String> {
  1563. let mut cur = Cursor::new(&data);
  1564. let text = String::decode(&mut cur)?;
  1565. Ok(text)
  1566. }
  1567. let Ok(text) = decode_data(&method_call.data) else {
  1568. error!(target: "ui::chatedit", "insert_text() method invalid arg data");
  1569. return true
  1570. };
  1571. let Some(self_) = me.upgrade() else {
  1572. // Should not happen
  1573. panic!("self destroyed before insert_text_method_task was stopped!");
  1574. };
  1575. self_.insert_text(&text).await;
  1576. true
  1577. }
  1578. }
  1579. impl Drop for ChatEdit {
  1580. fn drop(&mut self) {
  1581. self.render_api
  1582. .replace_draw_calls(unixtime(), vec![(self.text_dc_key, Default::default())]);
  1583. }
  1584. }
  1585. #[async_trait]
  1586. impl UIObject for ChatEdit {
  1587. fn priority(&self) -> u32 {
  1588. self.priority.get()
  1589. }
  1590. async fn start(self: Arc<Self>, ex: ExecutorPtr) {
  1591. let me = Arc::downgrade(&self);
  1592. let node_ref = &self.node.upgrade().unwrap();
  1593. let node_name = node_ref.name.clone();
  1594. let node_id = node_ref.id;
  1595. let method_sub = node_ref.subscribe_method_call("insert_text").unwrap();
  1596. let me2 = me.clone();
  1597. let insert_text_task =
  1598. ex.spawn(
  1599. async move { while Self::process_insert_text_method(&me2, &method_sub).await {} },
  1600. );
  1601. let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone());
  1602. on_modify.when_change(self.is_focused.prop(), Self::change_focus);
  1603. // When text has been changed.
  1604. // Cursor and selection might be invalidated.
  1605. async fn reset(self_: Arc<ChatEdit>) {
  1606. self_.cursor_pos.set(0);
  1607. //self_.select_text.set_null(Role::Internal, 0).unwrap();
  1608. self_.scroll.set(0.);
  1609. self_.redraw();
  1610. }
  1611. async fn redraw(self_: Arc<ChatEdit>) {
  1612. self_.redraw().await;
  1613. }
  1614. async fn set_text(self_: Arc<ChatEdit>) {
  1615. {
  1616. let text = self_.text.get();
  1617. let mut text_wrap = self_.text_wrap.lock();
  1618. text_wrap.editable.end_compose();
  1619. text_wrap.editable.set_text(text, String::new());
  1620. text_wrap.clear_cache();
  1621. let select = &mut text_wrap.select;
  1622. select.clear();
  1623. }
  1624. self_.redraw().await;
  1625. }
  1626. on_modify.when_change(self.rect.prop(), redraw);
  1627. on_modify.when_change(self.baseline.prop(), redraw);
  1628. on_modify.when_change(self.linespacing.prop(), redraw);
  1629. on_modify.when_change(self.select_ascent.prop(), redraw);
  1630. on_modify.when_change(self.select_descent.prop(), redraw);
  1631. on_modify.when_change(self.handle_descent.prop(), redraw);
  1632. on_modify.when_change(self.padding.clone(), redraw);
  1633. on_modify.when_change(self.text.prop(), set_text);
  1634. // The commented properties are modified on input events
  1635. // So then redraw() will get repeatedly triggered when these properties
  1636. // are changed. We should find a solution. For now the hooks are disabled.
  1637. //on_modify.when_change(scroll.prop(), redraw);
  1638. //on_modify.when_change(cursor_pos.prop(), redraw);
  1639. on_modify.when_change(self.font_size.prop(), redraw);
  1640. // We must also reshape text
  1641. on_modify.when_change(self.text.prop(), reset);
  1642. on_modify.when_change(self.text_color.prop(), redraw);
  1643. on_modify.when_change(self.hi_bg_color.prop(), redraw);
  1644. //on_modify.when_change(selected.clone(), redraw);
  1645. on_modify.when_change(self.z_index.prop(), redraw);
  1646. on_modify.when_change(self.debug.prop(), redraw);
  1647. async fn regen_cursor(self_: Arc<ChatEdit>) {
  1648. let mesh = std::mem::take(&mut *self_.cursor_mesh.lock());
  1649. }
  1650. on_modify.when_change(self.cursor_color.prop(), regen_cursor);
  1651. on_modify.when_change(self.cursor_ascent.prop(), regen_cursor);
  1652. on_modify.when_change(self.cursor_descent.prop(), regen_cursor);
  1653. on_modify.when_change(self.cursor_width.prop(), regen_cursor);
  1654. let me2 = me.clone();
  1655. let cursor_blink_time = self.cursor_blink_time.clone();
  1656. let cursor_idle_time = self.cursor_idle_time.clone();
  1657. let blinking_cursor_task = ex.spawn(async move {
  1658. loop {
  1659. msleep(cursor_blink_time.get() as u64).await;
  1660. let self_ = me2.upgrade().unwrap();
  1661. if self_.blink_is_paused.swap(false, Ordering::Relaxed) {
  1662. msleep(cursor_idle_time.get() as u64).await;
  1663. continue
  1664. }
  1665. if !self_.rect.has_cached() {
  1666. continue
  1667. }
  1668. // Invert the bool
  1669. self_.cursor_is_visible.fetch_not(Ordering::Relaxed);
  1670. self_.redraw_cursor();
  1671. }
  1672. });
  1673. let mut tasks = vec![insert_text_task, blinking_cursor_task];
  1674. tasks.append(&mut on_modify.tasks);
  1675. self.tasks.set(tasks);
  1676. }
  1677. async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
  1678. t!("ChatEdit::draw({:?})", self.node.upgrade().unwrap());
  1679. *self.parent_rect.lock() = Some(parent_rect);
  1680. self.make_draw_calls()
  1681. }
  1682. async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) -> bool {
  1683. t!("handle_char({key}, {mods:?}, {repeat})");
  1684. // First filter for only single digit keys
  1685. if DISALLOWED_CHARS.contains(&key) {
  1686. return false
  1687. }
  1688. if !self.is_focused.get() {
  1689. return false
  1690. }
  1691. // Must be updated before checking the mods. You can press ctrl+a, then release ctrl
  1692. // before a is released. Then the repeater never gets reset, and uses any old value
  1693. // it has from before for a.
  1694. let actions = {
  1695. let mut repeater = self.key_repeat.lock();
  1696. repeater.key_down(PressedKey::Char(key), repeat)
  1697. };
  1698. if mods.ctrl || mods.alt {
  1699. if repeat {
  1700. return false
  1701. }
  1702. return self.handle_shortcut(key, &mods).await
  1703. }
  1704. t!("Key {:?} has {} actions", key, actions);
  1705. for _ in 0..actions {
  1706. self.insert_char(key).await;
  1707. }
  1708. true
  1709. }
  1710. async fn handle_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
  1711. t!("handle_key_down({key:?}, {mods:?}, {repeat})");
  1712. // First filter for only single digit keys
  1713. // Avoid processing events handled by insert_char()
  1714. if !ALLOWED_KEYCODES.contains(&key) {
  1715. return false
  1716. }
  1717. if !self.is_focused.get() {
  1718. return false
  1719. }
  1720. let actions = {
  1721. let mut repeater = self.key_repeat.lock();
  1722. repeater.key_down(PressedKey::Key(key), repeat)
  1723. };
  1724. // Suppress noisy message
  1725. if actions > 0 {
  1726. t!("Key {:?} has {} actions", key, actions);
  1727. }
  1728. let mut is_handled = false;
  1729. for _ in 0..actions {
  1730. if self.handle_key(&key, &mods).await {
  1731. is_handled = true;
  1732. }
  1733. }
  1734. is_handled
  1735. }
  1736. async fn handle_mouse_btn_down(&self, btn: MouseButton, mut mouse_pos: Point) -> bool {
  1737. if !self.is_active.get() {
  1738. return false
  1739. }
  1740. let rect = self.rect.get();
  1741. if btn != MouseButton::Left {
  1742. if btn == MouseButton::Right && rect.contains(mouse_pos) {
  1743. if self.text.get().is_empty() {
  1744. let node = self.node.upgrade().unwrap();
  1745. node.trigger("paste_request", vec![]).await.unwrap();
  1746. }
  1747. return true
  1748. }
  1749. return false
  1750. }
  1751. if !rect.contains(mouse_pos) {
  1752. return false
  1753. }
  1754. // clicking inside box will:
  1755. // 1. make it active
  1756. // 2. begin selection
  1757. if self.is_focused.get() {
  1758. d!("ChatEdit clicked");
  1759. } else {
  1760. d!("ChatEdit focused");
  1761. self.is_focused.set(true);
  1762. }
  1763. // Move mouse pos within this widget
  1764. self.abs_to_local(&mut mouse_pos);
  1765. let width = self.wrap_width();
  1766. {
  1767. let mut text_wrap = self.text_wrap.lock();
  1768. let cursor_pos = text_wrap.set_cursor_with_point(mouse_pos, width);
  1769. self.update_cursor_pos(&mut text_wrap);
  1770. d!("Mouse move cursor pos to {cursor_pos}");
  1771. // begin selection
  1772. let select = &mut text_wrap.select;
  1773. let select_is_empty = select.is_empty();
  1774. select.clear();
  1775. if !select_is_empty {
  1776. self.update_select_text(&mut text_wrap);
  1777. }
  1778. self.mouse_btn_held.store(true, Ordering::Relaxed);
  1779. }
  1780. self.pause_blinking();
  1781. self.redraw().await;
  1782. true
  1783. }
  1784. async fn handle_mouse_btn_up(&self, btn: MouseButton, mouse_pos: Point) -> bool {
  1785. if !self.is_active.get() {
  1786. return false
  1787. }
  1788. // releasing mouse button will end selection
  1789. self.mouse_btn_held.store(false, Ordering::Relaxed);
  1790. false
  1791. }
  1792. async fn handle_mouse_move(&self, mut mouse_pos: Point) -> bool {
  1793. if !self.is_active.get() {
  1794. return false
  1795. }
  1796. let rect = self.rect.get();
  1797. self.is_mouse_hover.store(rect.contains(mouse_pos), Ordering::Relaxed);
  1798. if !self.mouse_btn_held.load(Ordering::Relaxed) {
  1799. return false;
  1800. }
  1801. // if active and selection_active, then use x to modify the selection.
  1802. // also implement scrolling when cursor is to the left or right
  1803. // just scroll to the end
  1804. // also set cursor_pos too
  1805. // Move mouse pos within this widget
  1806. self.abs_to_local(&mut mouse_pos);
  1807. let width = self.wrap_width();
  1808. {
  1809. let mut text_wrap = self.text_wrap.lock();
  1810. let cursor_pos = text_wrap.set_cursor_with_point(mouse_pos, width);
  1811. self.update_cursor_pos(&mut text_wrap);
  1812. // modify current selection
  1813. let select = &mut text_wrap.select;
  1814. if select.is_empty() {
  1815. select.push(Selection::new(cursor_pos, cursor_pos));
  1816. }
  1817. select.first_mut().unwrap().end = cursor_pos;
  1818. self.update_select_text(&mut text_wrap);
  1819. }
  1820. self.pause_blinking();
  1821. //self.apply_cursor_scrolling();
  1822. self.redraw().await;
  1823. true
  1824. }
  1825. async fn handle_mouse_wheel(&self, wheel_pos: Point) -> bool {
  1826. if !self.is_mouse_hover.load(Ordering::Relaxed) {
  1827. return false
  1828. }
  1829. let max_scroll = {
  1830. let mut text_wrap = self.text_wrap.lock();
  1831. self.max_scroll(&mut text_wrap)
  1832. };
  1833. let mut scroll = self.scroll.get() - wheel_pos.y * self.scroll_speed.get();
  1834. scroll = scroll.clamp(0., max_scroll);
  1835. t!("handle_mouse_wheel({wheel_pos:?}) [scroll={scroll}]");
  1836. self.scroll.set(scroll);
  1837. self.redraw().await;
  1838. true
  1839. }
  1840. async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_pos: Point) -> bool {
  1841. if !self.is_active.get() {
  1842. return false
  1843. }
  1844. // Ignore multi-touch
  1845. if id != 0 {
  1846. return false
  1847. }
  1848. match phase {
  1849. TouchPhase::Started => self.handle_touch_start(touch_pos).await,
  1850. TouchPhase::Moved => self.handle_touch_move(touch_pos).await,
  1851. TouchPhase::Ended => self.handle_touch_end(touch_pos).await,
  1852. TouchPhase::Cancelled => false,
  1853. }
  1854. }
  1855. async fn handle_compose_text(&self, suggest_text: &str, is_commit: bool) -> bool {
  1856. t!("handle_compose_text({suggest_text}, {is_commit})");
  1857. if !self.is_active.get() {
  1858. return false
  1859. }
  1860. let text = {
  1861. let mut text_wrap = self.text_wrap.lock();
  1862. text_wrap.clear_cache();
  1863. text_wrap.editable.compose(suggest_text, is_commit);
  1864. self.clamp_scroll(&mut text_wrap);
  1865. text_wrap.editable.get_text()
  1866. };
  1867. self.text.set(text);
  1868. //self.apply_cursor_scrolling();
  1869. self.redraw().await;
  1870. true
  1871. }
  1872. async fn handle_set_compose_region(&self, start: usize, end: usize) -> bool {
  1873. t!("handle_set_compose_region({start}, {end})");
  1874. if !self.is_active.get() {
  1875. return false
  1876. }
  1877. {
  1878. let mut text_wrap = self.text_wrap.lock();
  1879. text_wrap.clear_cache();
  1880. text_wrap.editable.set_compose_region(start, end);
  1881. }
  1882. self.redraw().await;
  1883. true
  1884. }
  1885. }