mod.rs 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687
  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 miniquad::{window, KeyCode, KeyMods, MouseButton, TouchPhase};
  23. use rand::{rngs::OsRng, Rng};
  24. use std::{
  25. collections::HashMap,
  26. sync::{
  27. atomic::{AtomicBool, Ordering},
  28. Arc, Mutex as SyncMutex, OnceLock, Weak,
  29. },
  30. time::Instant,
  31. };
  32. use crate::{
  33. error::Result,
  34. gfx::{
  35. GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, GraphicsEventPublisherPtr,
  36. Point, Rectangle, RenderApi, Vertex,
  37. },
  38. mesh::{MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_WHITE},
  39. prop::{
  40. PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyRect, PropertyStr,
  41. PropertyUint32, Role,
  42. },
  43. pubsub::Subscription,
  44. scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
  45. text::{self, Glyph, GlyphPositionIter, TextShaperPtr},
  46. util::{enumerate_ref, is_whitespace, zip3},
  47. ExecutorPtr,
  48. };
  49. use super::{DrawUpdate, OnModify, UIObject};
  50. mod editable;
  51. use editable::{Editable, Selection, TextIdx, TextPos};
  52. // EOL whitespace is given a nudge since it has a width of 0 after text shaping
  53. const CURSOR_EOL_WS_NUDGE: f32 = 0.8;
  54. // EOL chars are more aesthetic when given a smallish nudge
  55. const CURSOR_EOL_NUDGE: f32 = 0.2;
  56. fn eol_nudge(font_size: f32, glyphs: &Vec<Glyph>) -> f32 {
  57. if is_whitespace(&glyphs.last().unwrap().substr) {
  58. (font_size * CURSOR_EOL_WS_NUDGE).round()
  59. } else {
  60. (font_size * CURSOR_EOL_NUDGE).round()
  61. }
  62. }
  63. #[derive(Debug, Clone, Eq, Hash, PartialEq)]
  64. enum PressedKey {
  65. Char(char),
  66. Key(KeyCode),
  67. }
  68. /// On key press (repeat=false), we immediately process the event.
  69. /// Then there's a delay (repeat=true) and then for every step time
  70. /// while key press events are being sent, we allow an event.
  71. /// This ensures smooth typing in the editbox.
  72. struct PressedKeysSmoothRepeat {
  73. /// When holding keys, we track from start and last sent time.
  74. /// This is useful for initial delay and smooth scrolling.
  75. pressed_keys: HashMap<PressedKey, RepeatingKeyTimer>,
  76. /// Initial delay before allowing keys
  77. start_delay: u32,
  78. /// Minimum time between repeated keys
  79. step_time: u32,
  80. }
  81. impl PressedKeysSmoothRepeat {
  82. fn new(start_delay: u32, step_time: u32) -> Self {
  83. Self { pressed_keys: HashMap::new(), start_delay, step_time }
  84. }
  85. fn key_down(&mut self, key: PressedKey, repeat: bool) -> u32 {
  86. //debug!(target: "PressedKeysSmoothRepeat", "key_down({:?}, {})", key, repeat);
  87. if !repeat {
  88. self.pressed_keys.remove(&key);
  89. return 1;
  90. }
  91. // Insert key if not exists
  92. if !self.pressed_keys.contains_key(&key) {
  93. //debug!(target: "PressedKeysSmoothRepeat", "insert key {:?}", key);
  94. self.pressed_keys.insert(key.clone(), RepeatingKeyTimer::new());
  95. }
  96. let repeater = self.pressed_keys.get_mut(&key).expect("repeat map");
  97. repeater.update(self.start_delay, self.step_time)
  98. }
  99. /*
  100. fn key_up(&mut self, key: &PressedKey) {
  101. //debug!(target: "PressedKeysSmoothRepeat", "key_up({:?})", key);
  102. assert!(self.pressed_keys.contains_key(key));
  103. self.pressed_keys.remove(key).expect("key was pressed");
  104. }
  105. */
  106. }
  107. struct RepeatingKeyTimer {
  108. start: Instant,
  109. actions: u32,
  110. }
  111. impl RepeatingKeyTimer {
  112. fn new() -> Self {
  113. Self { start: Instant::now(), actions: 0 }
  114. }
  115. fn update(&mut self, start_delay: u32, step_time: u32) -> u32 {
  116. let elapsed = self.start.elapsed().as_millis();
  117. //debug!(target: "RepeatingKeyTimer", "update() elapsed={}, actions={}",
  118. // elapsed, self.actions);
  119. if elapsed < start_delay as u128 {
  120. return 0
  121. }
  122. let total_actions = ((elapsed - start_delay as u128) / step_time as u128) as u32;
  123. let remaining_actions = total_actions - self.actions;
  124. self.actions = total_actions;
  125. remaining_actions
  126. }
  127. }
  128. #[derive(Clone)]
  129. struct TouchStartInfo {
  130. pos: Point,
  131. instant: std::time::Instant,
  132. }
  133. impl TouchStartInfo {
  134. fn new(pos: Point) -> Self {
  135. Self { pos, instant: std::time::Instant::now() }
  136. }
  137. }
  138. #[derive(Clone)]
  139. enum TouchStateAction {
  140. Inactive,
  141. Started { pos: Point, instant: std::time::Instant },
  142. StartSelect,
  143. Select,
  144. DragSelectHandle { side: isize },
  145. ScrollHoriz { start_pos: Point, scroll_start: f32 },
  146. }
  147. struct TouchInfo {
  148. state: TouchStateAction,
  149. start: Option<TouchStartInfo>,
  150. scroll: PropertyFloat32,
  151. }
  152. impl TouchInfo {
  153. fn new(scroll: PropertyFloat32) -> Self {
  154. Self { state: TouchStateAction::Inactive, start: None, scroll }
  155. }
  156. fn start(&mut self, pos: Point) {
  157. debug!(target: "ui::editbox", "TouchStateAction::Started");
  158. self.state = TouchStateAction::Started { pos, instant: std::time::Instant::now() };
  159. }
  160. fn stop(&mut self) -> TouchStateAction {
  161. std::mem::replace(&mut self.state, TouchStateAction::Inactive)
  162. }
  163. fn update(&mut self, pos: &Point) {
  164. match &self.state {
  165. TouchStateAction::Started { pos: start_pos, instant } => {
  166. let travel_dist = pos.dist_sq(&start_pos);
  167. let x_dist = pos.x - start_pos.x;
  168. let elapsed = instant.elapsed().as_millis();
  169. if travel_dist < 5. {
  170. if elapsed > 1000 {
  171. debug!(target: "ui::editbox", "TouchStateAction::StartSelect");
  172. self.state = TouchStateAction::StartSelect;
  173. }
  174. } else if x_dist.abs() > 5. {
  175. debug!(target: "ui::editbox", "TouchStateAction::ScrollHoriz");
  176. let scroll_start = self.scroll.get();
  177. self.state =
  178. TouchStateAction::ScrollHoriz { start_pos: *start_pos, scroll_start };
  179. }
  180. }
  181. _ => {}
  182. }
  183. }
  184. }
  185. pub type EditBoxPtr = Arc<EditBox>;
  186. pub struct EditBox {
  187. node: SceneNodeWeak,
  188. tasks: OnceLock<Vec<smol::Task<()>>>,
  189. render_api: RenderApi,
  190. text_shaper: TextShaperPtr,
  191. key_repeat: SyncMutex<PressedKeysSmoothRepeat>,
  192. glyphs: SyncMutex<Vec<Glyph>>,
  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. is_active: PropertyBool,
  199. is_focused: PropertyBool,
  200. rect: PropertyRect,
  201. baseline: PropertyFloat32,
  202. scroll: PropertyFloat32,
  203. scroll_speed: PropertyFloat32,
  204. cursor_pos: PropertyUint32,
  205. font_size: PropertyFloat32,
  206. text: PropertyStr,
  207. text_color: PropertyColor,
  208. text_hi_color: PropertyColor,
  209. cursor_color: PropertyColor,
  210. cursor_width: PropertyFloat32,
  211. cursor_ascent: PropertyFloat32,
  212. cursor_descent: PropertyFloat32,
  213. cursor_blink_time: PropertyUint32,
  214. cursor_idle_time: PropertyUint32,
  215. hi_bg_color: PropertyColor,
  216. select_ascent: PropertyFloat32,
  217. select_descent: PropertyFloat32,
  218. selected: PropertyPtr,
  219. z_index: PropertyUint32,
  220. debug: PropertyBool,
  221. editable: SyncMutex<Editable>,
  222. select: SyncMutex<Vec<Selection>>,
  223. mouse_btn_held: AtomicBool,
  224. cursor_is_visible: AtomicBool,
  225. blink_is_paused: AtomicBool,
  226. /// Used to explicitly hide the cursor. Must be manually re-enabled.
  227. hide_cursor: AtomicBool,
  228. touch_info: SyncMutex<TouchInfo>,
  229. is_phone_select: AtomicBool,
  230. old_window_scale: AtomicF32,
  231. window_scale: PropertyFloat32,
  232. parent_rect: SyncMutex<Option<Rectangle>>,
  233. is_mouse_hover: AtomicBool,
  234. }
  235. impl EditBox {
  236. pub async fn new(
  237. node: SceneNodeWeak,
  238. window_scale: PropertyFloat32,
  239. render_api: RenderApi,
  240. text_shaper: TextShaperPtr,
  241. ex: ExecutorPtr,
  242. ) -> Pimpl {
  243. debug!(target: "ui::editbox", "EditBox::new()");
  244. let node_ref = &node.upgrade().unwrap();
  245. let is_active = PropertyBool::wrap(node_ref, Role::Internal, "is_active", 0).unwrap();
  246. let is_focused = PropertyBool::wrap(node_ref, Role::Internal, "is_focused", 0).unwrap();
  247. let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap();
  248. let baseline = PropertyFloat32::wrap(node_ref, Role::Internal, "baseline", 0).unwrap();
  249. let scroll = PropertyFloat32::wrap(node_ref, Role::Internal, "scroll", 0).unwrap();
  250. let scroll_speed =
  251. PropertyFloat32::wrap(node_ref, Role::Internal, "scroll_speed", 0).unwrap();
  252. let cursor_pos = PropertyUint32::wrap(node_ref, Role::Internal, "cursor_pos", 0).unwrap();
  253. let font_size = PropertyFloat32::wrap(node_ref, Role::Internal, "font_size", 0).unwrap();
  254. let text = PropertyStr::wrap(node_ref, Role::Internal, "text", 0).unwrap();
  255. let text_color = PropertyColor::wrap(node_ref, Role::Internal, "text_color").unwrap();
  256. let text_hi_color = PropertyColor::wrap(node_ref, Role::Internal, "text_hi_color").unwrap();
  257. let cursor_color = PropertyColor::wrap(node_ref, Role::Internal, "cursor_color").unwrap();
  258. let cursor_width =
  259. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_width", 0).unwrap();
  260. let cursor_ascent =
  261. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_ascent", 0).unwrap();
  262. let cursor_descent =
  263. PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_descent", 0).unwrap();
  264. let hi_bg_color = PropertyColor::wrap(node_ref, Role::Internal, "hi_bg_color").unwrap();
  265. let select_ascent =
  266. PropertyFloat32::wrap(node_ref, Role::Internal, "select_ascent", 0).unwrap();
  267. let select_descent =
  268. PropertyFloat32::wrap(node_ref, Role::Internal, "select_descent", 0).unwrap();
  269. let selected = node_ref.get_property("selected").unwrap();
  270. let cursor_blink_time =
  271. PropertyUint32::wrap(node_ref, Role::Internal, "cursor_blink_time", 0).unwrap();
  272. let cursor_idle_time =
  273. PropertyUint32::wrap(node_ref, Role::Internal, "cursor_idle_time", 0).unwrap();
  274. let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
  275. let debug = PropertyBool::wrap(node_ref, Role::Internal, "debug", 0).unwrap();
  276. let node_name = node_ref.name.clone();
  277. let node_id = node_ref.id;
  278. // Must do this whenever the text changes
  279. let glyphs = text_shaper.shape(text.get(), font_size.get(), window_scale.get());
  280. let self_ = Arc::new(Self {
  281. node,
  282. tasks: OnceLock::new(),
  283. render_api,
  284. text_shaper: text_shaper.clone(),
  285. key_repeat: SyncMutex::new(PressedKeysSmoothRepeat::new(400, 50)),
  286. glyphs: SyncMutex::new(glyphs),
  287. text_dc_key: OsRng.gen(),
  288. cursor_mesh: SyncMutex::new(None),
  289. cursor_dc_key: OsRng.gen(),
  290. is_active,
  291. is_focused,
  292. rect,
  293. baseline: baseline.clone(),
  294. scroll: scroll.clone(),
  295. scroll_speed,
  296. cursor_pos,
  297. font_size: font_size.clone(),
  298. text,
  299. text_color,
  300. text_hi_color,
  301. cursor_color,
  302. cursor_width,
  303. cursor_ascent,
  304. cursor_descent,
  305. cursor_blink_time,
  306. cursor_idle_time,
  307. hi_bg_color,
  308. select_ascent,
  309. select_descent,
  310. selected,
  311. z_index,
  312. debug,
  313. editable: SyncMutex::new(Editable::new(
  314. text_shaper,
  315. font_size,
  316. window_scale.clone(),
  317. baseline,
  318. )),
  319. select: SyncMutex::new(vec![]),
  320. mouse_btn_held: AtomicBool::new(false),
  321. cursor_is_visible: AtomicBool::new(true),
  322. blink_is_paused: AtomicBool::new(false),
  323. hide_cursor: AtomicBool::new(false),
  324. touch_info: SyncMutex::new(TouchInfo::new(scroll)),
  325. is_phone_select: AtomicBool::new(false),
  326. old_window_scale: AtomicF32::new(window_scale.get()),
  327. window_scale,
  328. parent_rect: SyncMutex::new(None),
  329. is_mouse_hover: AtomicBool::new(false),
  330. });
  331. Pimpl::EditBox(self_)
  332. }
  333. fn node(&self) -> SceneNodePtr {
  334. self.node.upgrade().unwrap()
  335. }
  336. /// Called whenever the text or any text property changes.
  337. fn regen_text_mesh(&self, mut clip: Rectangle) -> GfxDrawMesh {
  338. clip.x = 0.;
  339. clip.y = 0.;
  340. let is_focused = self.is_focused.get();
  341. let text = self.text.get();
  342. let font_size = self.font_size.get();
  343. let window_scale = self.window_scale.get();
  344. let text_color = self.text_color.get();
  345. let text_hi_color = self.text_hi_color.get();
  346. let baseline = self.baseline.get();
  347. let scroll = self.scroll.get();
  348. let cursor_pos = self.cursor_pos.get() as usize;
  349. let cursor_color = self.cursor_color.get();
  350. let debug = self.debug.get();
  351. //debug!(target: "ui::editbox", "Rendering text '{text}' clip={clip:?}");
  352. //debug!(target: "ui::editbox", " cursor_pos={cursor_pos}, is_focused={is_focused}");
  353. let rendered = self.editable.lock().unwrap().render();
  354. let atlas = text::make_texture_atlas(&self.render_api, &rendered.glyphs);
  355. let mut mesh = MeshBuilder::with_clip(clip.clone());
  356. let selections = self.select.lock().unwrap().clone();
  357. // Just an assert
  358. if self.is_phone_select.load(Ordering::Relaxed) {
  359. assert!(selections.len() <= 1);
  360. }
  361. for select in &selections {
  362. self.draw_selected(&mut mesh, select, &rendered.glyphs, clip.h);
  363. }
  364. let select_marks = self.mark_selected_glyphs(&rendered.glyphs, selections);
  365. if rendered.has_underline() {
  366. self.draw_underline(
  367. &mut mesh,
  368. &rendered.glyphs,
  369. clip.h,
  370. rendered.under_start,
  371. rendered.under_end,
  372. );
  373. }
  374. let glyph_pos_iter =
  375. GlyphPositionIter::new(font_size, window_scale, &rendered.glyphs, baseline);
  376. for (glyph_idx, mut glyph_rect, glyph, is_selected) in
  377. zip3(glyph_pos_iter, rendered.glyphs.iter(), select_marks.iter())
  378. {
  379. let uv_rect = atlas.fetch_uv(glyph.glyph_id).expect("missing glyph UV rect");
  380. glyph_rect.x -= scroll;
  381. //mesh.draw_outline(&glyph_rect, COLOR_BLUE, 2.);
  382. let mut color = text_color.clone();
  383. if *is_selected {
  384. color = text_hi_color.clone();
  385. }
  386. if glyph.sprite.has_color {
  387. color = COLOR_WHITE;
  388. }
  389. mesh.draw_box(&glyph_rect, color, uv_rect);
  390. }
  391. if debug {
  392. mesh.draw_outline(&clip, COLOR_BLUE, 1.);
  393. }
  394. mesh.alloc(&self.render_api).draw_with_texture(atlas.texture)
  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 cursor_px_offset(&self) -> f32 {
  413. assert!(self.is_focused.get());
  414. let (cursor_pos, glyphs) = {
  415. let editable = self.editable.lock().unwrap();
  416. let rendered = editable.render();
  417. let cursor_pos = editable.get_cursor_pos(&rendered);
  418. (cursor_pos, rendered.glyphs)
  419. };
  420. let font_size = self.font_size.get();
  421. let window_scale = self.window_scale.get();
  422. let baseline = self.baseline.get();
  423. let scroll = self.scroll.get();
  424. // Add composer glyphs too
  425. let glyph_pos_iter = GlyphPositionIter::new(font_size, window_scale, &glyphs, baseline);
  426. // Used for drawing the cursor when it's at the end of the line.
  427. let mut rhs = 0.;
  428. if cursor_pos == 0 {
  429. return 0.;
  430. }
  431. for (glyph_idx, (mut glyph_rect, glyph)) in glyph_pos_iter.zip(glyphs.iter()).enumerate() {
  432. glyph_rect.x -= scroll;
  433. if cursor_pos == glyph_idx {
  434. return glyph_rect.x;
  435. }
  436. rhs = glyph_rect.rhs();
  437. }
  438. assert!(cursor_pos == glyphs.len());
  439. rhs += eol_nudge(font_size, &glyphs);
  440. rhs
  441. }
  442. fn mark_selected_glyphs(&self, glyphs: &Vec<Glyph>, selections: Vec<Selection>) -> Vec<bool> {
  443. let mut marks: Vec<_> = glyphs.iter().map(|_| false).collect();
  444. for select in selections {
  445. let start = std::cmp::min(select.start, select.end);
  446. let end = std::cmp::max(select.start, select.end);
  447. for i in start..end {
  448. marks[i] = true;
  449. }
  450. }
  451. marks
  452. }
  453. fn draw_selected(
  454. &self,
  455. mesh: &mut MeshBuilder,
  456. select: &Selection,
  457. glyphs: &Vec<Glyph>,
  458. clip_h: f32,
  459. ) {
  460. if select.start == select.end {
  461. return
  462. }
  463. let start = std::cmp::min(select.start, select.end);
  464. let end = std::cmp::max(select.start, select.end);
  465. let font_size = self.font_size.get();
  466. let window_scale = self.window_scale.get();
  467. let baseline = self.baseline.get();
  468. let scroll = self.scroll.get();
  469. let hi_bg_color = self.hi_bg_color.get();
  470. let glyph_pos_iter = GlyphPositionIter::new(font_size, window_scale, &glyphs, baseline);
  471. let is_phone_select = self.is_phone_select.load(Ordering::Relaxed);
  472. let mut start_x = 0.;
  473. let mut end_x = 0.;
  474. // When cursor lands at the end of the line
  475. let mut rhs = 0.;
  476. for (glyph_idx, mut glyph_rect) in glyph_pos_iter.enumerate() {
  477. glyph_rect.x -= scroll;
  478. if glyph_idx == start {
  479. start_x = glyph_rect.x;
  480. }
  481. if glyph_idx == end {
  482. end_x = glyph_rect.x;
  483. }
  484. rhs = glyph_rect.rhs();
  485. }
  486. if start == 0 {
  487. start_x = scroll;
  488. }
  489. if end == glyphs.len() {
  490. rhs += eol_nudge(font_size, &glyphs);
  491. end_x = rhs;
  492. }
  493. let select_ascent = self.select_ascent.get();
  494. let select_descent = self.select_descent.get();
  495. let select_rect = Rectangle {
  496. x: start_x,
  497. y: baseline - select_ascent,
  498. w: end_x - start_x,
  499. h: select_ascent + select_descent,
  500. };
  501. mesh.draw_box(&select_rect, hi_bg_color, &Rectangle::zero());
  502. if is_phone_select {
  503. self.draw_phone_select_handle(mesh, start_x, -1.);
  504. self.draw_phone_select_handle(mesh, end_x, 1.);
  505. }
  506. }
  507. fn draw_phone_select_handle(&self, mesh: &mut MeshBuilder, x: f32, side: f32) {
  508. debug!(target: "ui::editbox", "draw_phone_select_handle(..., {x}, {side})");
  509. let baseline = self.baseline.get();
  510. let select_ascent = self.select_ascent.get();
  511. let select_descent = self.select_descent.get();
  512. let color = self.text_hi_color.get();
  513. // Transparent for fade
  514. let mut color_trans = color.clone();
  515. color_trans[3] = 0.;
  516. // Vertical line downwards. We use this instead of draw_box() so we have a fade.
  517. let verts = vec![
  518. Vertex {
  519. pos: [x - side * 1., baseline - select_ascent],
  520. color: color_trans,
  521. uv: [0., 0.],
  522. },
  523. Vertex {
  524. pos: [x + side * 4., baseline - select_ascent],
  525. color: color_trans,
  526. uv: [0., 0.],
  527. },
  528. Vertex { pos: [x - side * 1., baseline + select_descent + 5.], color, uv: [0., 0.] },
  529. Vertex { pos: [x + side * 4., baseline + select_descent + 5.], color, uv: [0., 0.] },
  530. ];
  531. let indices = vec![0, 2, 1, 1, 2, 3];
  532. mesh.append(verts, indices);
  533. let y = baseline + select_descent;
  534. // The arrow itself.
  535. // Go anti-clockwise
  536. let verts = vec![
  537. Vertex { pos: [x, y], color, uv: [0., 0.] },
  538. Vertex { pos: [x, y + 50.], color, uv: [0., 0.] },
  539. Vertex { pos: [x + side * 30., y + 50.], color, uv: [0., 0.] },
  540. Vertex { pos: [x + side * 50., y + 25.], color, uv: [0., 0.] },
  541. ];
  542. let indices = vec![0, 1, 2, 0, 2, 3];
  543. mesh.append(verts, indices);
  544. }
  545. fn draw_underline(
  546. &self,
  547. mesh: &mut MeshBuilder,
  548. glyphs: &Vec<Glyph>,
  549. clip_h: f32,
  550. under_start: usize,
  551. under_end: usize,
  552. ) {
  553. assert!(under_start < under_end);
  554. let font_size = self.font_size.get();
  555. let window_scale = self.window_scale.get();
  556. let baseline = self.baseline.get();
  557. let scroll = self.scroll.get();
  558. let text_color = self.text_color.get();
  559. let glyph_pos_iter = GlyphPositionIter::new(font_size, window_scale, &glyphs, baseline);
  560. let mut start_x = 0.;
  561. let mut end_x = 0.;
  562. // When cursor lands at the end of the line
  563. let mut rhs = 0.;
  564. for (glyph_idx, mut glyph_rect) in glyph_pos_iter.enumerate() {
  565. glyph_rect.x -= scroll;
  566. if glyph_idx == under_start {
  567. start_x = glyph_rect.x;
  568. }
  569. if glyph_idx == under_end {
  570. end_x = glyph_rect.x;
  571. }
  572. rhs = glyph_rect.rhs();
  573. }
  574. if under_start == 0 {
  575. start_x = scroll;
  576. }
  577. if under_end == glyphs.len() {
  578. end_x = rhs;
  579. }
  580. // We don't need to do manual clipping since MeshBuilder should do that
  581. let underline_rect = Rectangle { x: start_x, y: baseline + 6., w: end_x - start_x, h: 4. };
  582. mesh.draw_box(&underline_rect, text_color, &Rectangle::zero());
  583. }
  584. async fn change_focus(self: Arc<Self>) {
  585. if !self.is_active.get() {
  586. return
  587. }
  588. debug!(target: "ui::editbox", "Focus changed");
  589. // Cursor visibility will change so just redraw everything lol
  590. self.redraw().await;
  591. }
  592. async fn insert_char(&self, key: char) {
  593. {
  594. let mut editable = self.editable.lock().unwrap();
  595. let mut tmp = [0; 4];
  596. let key_str = key.encode_utf8(&mut tmp);
  597. editable.compose(key_str, true);
  598. }
  599. self.pause_blinking();
  600. self.redraw().await;
  601. }
  602. async fn handle_shortcut(&self, key: char, mods: &KeyMods) {
  603. debug!(target: "ui::editbox", "handle_shortcut({:?}, {:?})", key, mods);
  604. match key {
  605. 'c' => {
  606. if mods.ctrl {
  607. self.copy_highlighted().unwrap();
  608. }
  609. }
  610. 'v' => {
  611. if mods.ctrl {
  612. if let Some(text) = window::clipboard_get() {
  613. self.paste_text(text).await;
  614. }
  615. }
  616. }
  617. _ => {}
  618. }
  619. }
  620. async fn handle_key(&self, key: &KeyCode, mods: &KeyMods) {
  621. debug!(target: "ui::editbox", "handle_key({:?}, {:?})", key, mods);
  622. match key {
  623. KeyCode::Left => {
  624. self.adjust_cursor(mods.shift, |editable| editable.move_cursor(-1));
  625. self.pause_blinking();
  626. //self.apply_cursor_scrolling();
  627. self.redraw().await;
  628. }
  629. KeyCode::Right => {
  630. self.adjust_cursor(mods.shift, |editable| editable.move_cursor(1));
  631. self.pause_blinking();
  632. //self.apply_cursor_scrolling();
  633. self.redraw().await;
  634. }
  635. KeyCode::Kp0 => self.insert_char('0').await,
  636. KeyCode::Kp1 => self.insert_char('1').await,
  637. KeyCode::Kp2 => self.insert_char('2').await,
  638. KeyCode::Kp3 => self.insert_char('3').await,
  639. KeyCode::Kp4 => self.insert_char('4').await,
  640. KeyCode::Kp5 => self.insert_char('5').await,
  641. KeyCode::Kp6 => self.insert_char('6').await,
  642. KeyCode::Kp7 => self.insert_char('7').await,
  643. KeyCode::Kp8 => self.insert_char('8').await,
  644. KeyCode::Kp9 => self.insert_char('9').await,
  645. KeyCode::KpDecimal => self.insert_char('.').await,
  646. KeyCode::Enter | KeyCode::KpEnter => {
  647. let node = self.node.upgrade().unwrap();
  648. node.trigger("enter_pressed", vec![]).await.unwrap();
  649. }
  650. KeyCode::Delete => {
  651. self.delete(0, 1);
  652. self.clamp_scroll();
  653. self.pause_blinking();
  654. self.redraw().await;
  655. }
  656. KeyCode::Backspace => {
  657. self.delete(1, 0);
  658. self.clamp_scroll();
  659. self.pause_blinking();
  660. self.redraw().await;
  661. }
  662. KeyCode::Home => {
  663. self.adjust_cursor(mods.shift, |editable| editable.move_start());
  664. self.pause_blinking();
  665. //self.apply_cursor_scrolling();
  666. self.redraw().await;
  667. }
  668. KeyCode::End => {
  669. self.adjust_cursor(mods.shift, |editable| editable.move_end());
  670. self.pause_blinking();
  671. //self.apply_cursor_scrolling();
  672. self.redraw().await;
  673. }
  674. _ => {}
  675. }
  676. }
  677. fn delete(&self, before: usize, after: usize) {
  678. let mut editable = self.editable.lock().unwrap();
  679. let selection = std::mem::take(&mut *self.select.lock().unwrap());
  680. if selection.is_empty() {
  681. editable.delete(before, after);
  682. return
  683. }
  684. let sel = selection.first().unwrap();
  685. let cursor_pos = std::cmp::min(sel.start, sel.end);
  686. let render = editable.render();
  687. let mut before_text = String::new();
  688. let mut after_text = String::new();
  689. 'next: for (pos, glyph) in render.glyphs.iter().enumerate() {
  690. for select in &selection {
  691. let start = std::cmp::min(select.start, select.end);
  692. let end = std::cmp::max(select.start, select.end);
  693. if start <= pos && pos < end {
  694. continue 'next
  695. }
  696. }
  697. if pos <= cursor_pos {
  698. before_text += &glyph.substr;
  699. } else {
  700. after_text += &glyph.substr;
  701. }
  702. }
  703. editable.set_text(before_text, after_text);
  704. self.is_phone_select.store(false, Ordering::Relaxed);
  705. // Reshow cursor (if hidden)
  706. self.hide_cursor.store(false, Ordering::Relaxed);
  707. }
  708. fn adjust_cursor(&self, has_shift: bool, move_cursor: impl Fn(&mut Editable)) {
  709. let mut editable = self.editable.lock().unwrap();
  710. let rendered = editable.render();
  711. let prev_cursor_pos = editable.get_cursor_pos(&rendered);
  712. move_cursor(&mut editable);
  713. let cursor_pos = editable.get_cursor_pos(&rendered);
  714. drop(editable);
  715. let mut select = self.select.lock().unwrap();
  716. // Start selection if shift is held
  717. if has_shift {
  718. // Create a new selection
  719. if select.is_empty() {
  720. select.push(Selection::new(prev_cursor_pos, cursor_pos));
  721. }
  722. // Update the selection
  723. select.last_mut().unwrap().end = cursor_pos;
  724. } else {
  725. select.clear();
  726. }
  727. }
  728. /// This will select the entire word rather than move the cursor to that location
  729. fn start_touch_select(&self, x: f32) {
  730. let rect = self.rect.get();
  731. let font_size = self.font_size.get();
  732. let window_scale = self.window_scale.get();
  733. let baseline = self.baseline.get();
  734. {
  735. let mut editable = self.editable.lock().unwrap();
  736. editable.end_compose();
  737. let rendered = editable.render();
  738. let x = x - rect.x + self.scroll.get();
  739. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  740. // Find word start
  741. let mut cpos_start = cpos;
  742. while cpos_start > 0 {
  743. // Is the glyph before this pos just whitespace?
  744. let glyph_str = &rendered.glyphs[cpos_start - 1].substr;
  745. if is_whitespace(glyph_str) {
  746. break
  747. }
  748. cpos_start -= 1;
  749. }
  750. // Find word end
  751. let mut cpos_end = cpos;
  752. while cpos_end < rendered.glyphs.len() {
  753. cpos_end += 1;
  754. let glyph_str = &rendered.glyphs[cpos_end].substr;
  755. if is_whitespace(glyph_str) {
  756. break
  757. }
  758. }
  759. let cidx_start = rendered.pos_to_idx(cpos_start);
  760. let cidx_end = rendered.pos_to_idx(cpos_end);
  761. // begin selection
  762. let mut select = self.select.lock().unwrap();
  763. select.clear();
  764. select.push(Selection::new(cpos_start, cpos_end));
  765. }
  766. self.is_phone_select.store(true, Ordering::Relaxed);
  767. // redraw() will now hide the cursor
  768. self.hide_cursor.store(true, Ordering::Relaxed);
  769. }
  770. fn glyphs_to_string(glyphs: &Vec<Glyph>) -> String {
  771. let mut text = String::new();
  772. for (i, glyph) in glyphs.iter().enumerate() {
  773. text.push_str(&glyph.substr);
  774. }
  775. text
  776. }
  777. fn delete_highlighted(&self) {
  778. assert!(!self.selected.is_null(0).unwrap());
  779. assert!(!self.selected.is_null(1).unwrap());
  780. let start = self.selected.get_u32(0).unwrap() as usize;
  781. let end = self.selected.get_u32(1).unwrap() as usize;
  782. let sel_start = std::cmp::min(start, end);
  783. let sel_end = std::cmp::max(start, end);
  784. let mut glyphs = self.glyphs.lock().unwrap().clone();
  785. glyphs.drain(sel_start..sel_end);
  786. let text = Self::glyphs_to_string(&glyphs);
  787. debug!(
  788. target: "ui::editbox",
  789. "delete_highlighted() text=\"{}\", cursor_pos={}",
  790. text, sel_start
  791. );
  792. self.text.set(text);
  793. self.selected.set_null(Role::Internal, 0).unwrap();
  794. self.selected.set_null(Role::Internal, 1).unwrap();
  795. self.cursor_pos.set(sel_start as u32);
  796. }
  797. fn copy_highlighted(&self) -> Result<()> {
  798. let start = self.selected.get_u32(0)? as usize;
  799. let end = self.selected.get_u32(1)? as usize;
  800. let sel_start = std::cmp::min(start, end);
  801. let sel_end = std::cmp::max(start, end);
  802. let mut text = String::new();
  803. let glyphs = self.glyphs.lock().unwrap().clone();
  804. for (glyph_idx, glyph) in glyphs.iter().enumerate() {
  805. if sel_start <= glyph_idx && glyph_idx < sel_end {
  806. text.push_str(&glyph.substr);
  807. }
  808. }
  809. info!(target: "ui::editbox", "Copied '{}'", text);
  810. window::clipboard_set(&text);
  811. Ok(())
  812. }
  813. async fn paste_text(&self, key: String) {
  814. let mut text = String::new();
  815. let cursor_pos = self.cursor_pos.get();
  816. if cursor_pos == 0 {
  817. text = key.clone();
  818. }
  819. let glyphs = self.glyphs.lock().unwrap().clone();
  820. for (glyph_idx, glyph) in glyphs.iter().enumerate() {
  821. text.push_str(&glyph.substr);
  822. if cursor_pos == glyph_idx as u32 + 1 {
  823. text.push_str(&key);
  824. }
  825. }
  826. self.text.set(text);
  827. // Not always true lol
  828. self.cursor_pos.set(cursor_pos + 1);
  829. self.apply_cursor_scrolling();
  830. self.redraw().await;
  831. }
  832. async fn handle_touch_start(&self, pos: Point) -> bool {
  833. debug!(target: "ui::editbox", "handle_touch_start({pos:?})");
  834. let mut touch_info = self.touch_info.lock().unwrap();
  835. if self.try_handle_drag(&mut touch_info, pos) {
  836. return true
  837. }
  838. let rect = self.rect.get();
  839. if !rect.contains(pos) {}
  840. touch_info.start(pos);
  841. true
  842. }
  843. fn try_handle_drag(&self, touch_info: &mut TouchInfo, pos: Point) -> bool {
  844. let selections = self.select.lock().unwrap().clone();
  845. if self.is_phone_select.load(Ordering::Relaxed) && selections.len() == 1 {
  846. let select = selections.first().unwrap();
  847. let rendered = {
  848. let editable = self.editable.lock().unwrap();
  849. editable.render()
  850. };
  851. let font_size = self.font_size.get();
  852. let window_scale = self.window_scale.get();
  853. let baseline = self.baseline.get();
  854. // Get left handle centerpoint
  855. let x1 = rendered.pos_to_xw(select.start, font_size, window_scale, baseline).0;
  856. // Get right handle centerpoint
  857. let x2 = {
  858. let (x, w) = rendered.pos_to_xw(select.end, font_size, window_scale, baseline);
  859. x + w
  860. };
  861. // Are we within range of either one?
  862. let select_descent = self.select_descent.get();
  863. let scroll = self.scroll.get();
  864. let y = baseline + select_descent + 25.;
  865. let p1 = Point::new(x1 - scroll, y);
  866. let p2 = Point::new(x2 - scroll, y);
  867. debug!(target: "ui::editbox", "handle center points = ({p1:?}, {p2:?})");
  868. const TOUCH_RADIUS_SQ: f32 = 10_000.;
  869. // Make pos relative to the rect
  870. let pos_rel = pos - self.rect.get().pos();
  871. if p1.dist_sq(&pos_rel) <= TOUCH_RADIUS_SQ {
  872. debug!(target: "ui::editbox", "TouchStateAction::DragSelectHandle [side=-1]");
  873. // Set touch_state status to enable begin dragging them
  874. touch_info.state = TouchStateAction::DragSelectHandle { side: -1 };
  875. return true;
  876. }
  877. if p2.dist_sq(&pos_rel) <= TOUCH_RADIUS_SQ {
  878. debug!(target: "ui::editbox", "TouchStateAction::DragSelectHandle [side=1]");
  879. // Set touch_state status to enable begin dragging them
  880. touch_info.state = TouchStateAction::DragSelectHandle { side: 1 };
  881. return true;
  882. }
  883. }
  884. false
  885. }
  886. async fn handle_touch_move(&self, pos: Point) -> bool {
  887. //debug!(target: "ui::editbox", "handle_touch_move({pos:?})");
  888. let touch_state = {
  889. let mut touch_info = self.touch_info.lock().unwrap();
  890. touch_info.update(&pos);
  891. touch_info.state.clone()
  892. };
  893. match &touch_state {
  894. TouchStateAction::Inactive => return false,
  895. TouchStateAction::StartSelect => {
  896. let x = pos.x;
  897. self.start_touch_select(x);
  898. self.redraw().await;
  899. debug!(target: "ui::editbox", "TouchStateAction::Select");
  900. self.touch_info.lock().unwrap().state = TouchStateAction::Select;
  901. }
  902. TouchStateAction::DragSelectHandle { side } => {
  903. {
  904. assert!(*side == -1 || *side == 1);
  905. assert!(self.is_phone_select.load(Ordering::Relaxed));
  906. let mut selections = self.select.lock().unwrap();
  907. assert_eq!(selections.len(), 1);
  908. let select = selections.first_mut().unwrap();
  909. let rendered = {
  910. let editable = self.editable.lock().unwrap();
  911. editable.render()
  912. };
  913. let font_size = self.font_size.get();
  914. let window_scale = self.window_scale.get();
  915. let baseline = self.baseline.get();
  916. let pos_x = pos.x + self.scroll.get();
  917. let mut pos = rendered.x_to_pos(pos_x, font_size, window_scale, baseline);
  918. if *side == -1 {
  919. let select_other_pos = &mut select.end;
  920. if pos >= *select_other_pos {
  921. pos = *select_other_pos - 1;
  922. }
  923. select.start = pos;
  924. } else {
  925. let select_other_pos = &mut select.start;
  926. if pos <= *select_other_pos {
  927. pos = *select_other_pos + 1;
  928. }
  929. select.end = pos;
  930. }
  931. }
  932. self.redraw().await;
  933. }
  934. TouchStateAction::ScrollHoriz { start_pos, scroll_start } => {
  935. let x_dist = start_pos.x - pos.x;
  936. let mut scroll = scroll_start + x_dist;
  937. scroll = scroll.clamp(0., self.max_cursor_scroll());
  938. self.scroll.set(scroll);
  939. self.redraw().await;
  940. }
  941. _ => {}
  942. }
  943. true
  944. }
  945. async fn handle_touch_end(&self, pos: Point) -> bool {
  946. debug!(target: "ui::editbox", "handle_touch_end({pos:?})");
  947. let state = self.touch_info.lock().unwrap().stop();
  948. match state {
  949. TouchStateAction::Inactive => return false,
  950. TouchStateAction::Started { pos: _, instant: _ } => {
  951. let rect = self.rect.get();
  952. let font_size = self.font_size.get();
  953. let window_scale = self.window_scale.get();
  954. let baseline = self.baseline.get();
  955. {
  956. let mut editable = self.editable.lock().unwrap();
  957. let rendered = editable.render();
  958. // Adjust with scroll here too
  959. let x = pos.x - rect.x;
  960. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  961. let cidx = rendered.pos_to_idx(cpos);
  962. editable.set_cursor_idx(cidx);
  963. // begin selection
  964. let mut select = self.select.lock().unwrap();
  965. select.clear();
  966. }
  967. self.is_phone_select.store(false, Ordering::Relaxed);
  968. // Reshow cursor (if hidden)
  969. self.hide_cursor.store(false, Ordering::Relaxed);
  970. self.redraw().await;
  971. }
  972. _ => {}
  973. }
  974. window::show_keyboard(true);
  975. true
  976. }
  977. /// Whenever the cursor property is modified this MUST be called
  978. /// to recalculate the scroll x property.
  979. fn apply_cursor_scrolling(&self) {
  980. let rect = self.rect.get();
  981. let cursor_pos = self.cursor_pos.get() as usize;
  982. let cursor_width = self.cursor_width.get();
  983. let mut scroll = self.scroll.get();
  984. let cursor_x = {
  985. let font_size = self.font_size.get();
  986. let window_scale = self.window_scale.get();
  987. let baseline = self.baseline.get();
  988. let glyphs = self.glyphs.lock().unwrap().clone();
  989. let mut glyph_pos_iter =
  990. GlyphPositionIter::new(font_size, window_scale, &glyphs, baseline);
  991. if cursor_pos == 0 {
  992. 0.
  993. } else if cursor_pos == glyphs.len() {
  994. let glyph_pos = glyph_pos_iter.last().unwrap();
  995. let rhs = glyph_pos.rhs() + eol_nudge(font_size, &glyphs);
  996. rhs
  997. } else {
  998. assert!(cursor_pos < glyphs.len());
  999. let glyph_pos = glyph_pos_iter.nth(cursor_pos).expect("glyph pos mismatch glyphs");
  1000. glyph_pos.x
  1001. }
  1002. };
  1003. // The LHS and RHS of the cursor box
  1004. let cursor_lhs = cursor_x - scroll;
  1005. let cursor_rhs = cursor_lhs + cursor_width;
  1006. // RHS is outside
  1007. if cursor_rhs > rect.w {
  1008. // We want a scroll so RHS = w
  1009. // cursor_x - scroll + CURSOR_WIDTH = rect.w
  1010. scroll = cursor_x + cursor_width - rect.w;
  1011. // LHS is negative
  1012. } else if cursor_lhs < 0. {
  1013. // We want scroll so LHS = 0
  1014. // cursor_x - scroll = 0
  1015. scroll = cursor_x;
  1016. }
  1017. self.scroll.set(scroll);
  1018. }
  1019. fn max_cursor_scroll(&self) -> f32 {
  1020. let font_size = self.font_size.get();
  1021. let window_scale = self.window_scale.get();
  1022. let baseline = self.baseline.get();
  1023. let rhs = {
  1024. let mut editable = self.editable.lock().unwrap();
  1025. let rendered = editable.render();
  1026. if rendered.glyphs.is_empty() {
  1027. return 0.
  1028. }
  1029. let glyph_pos_iter =
  1030. GlyphPositionIter::new(font_size, window_scale, &rendered.glyphs, baseline);
  1031. let last_rect = glyph_pos_iter.last().unwrap();
  1032. let mut rhs = last_rect.x + last_rect.w;
  1033. rhs += eol_nudge(font_size, &rendered.glyphs);
  1034. rhs
  1035. };
  1036. let rect_w = self.rect.get().w;
  1037. if rhs <= rect_w {
  1038. return 0.;
  1039. }
  1040. let max_scroll = rhs - rect_w;
  1041. max_scroll
  1042. }
  1043. fn clamp_scroll(&self) {
  1044. let mut scroll = self.scroll.get();
  1045. scroll = scroll.clamp(0., self.max_cursor_scroll());
  1046. self.scroll.set(scroll);
  1047. }
  1048. fn pause_blinking(&self) {
  1049. self.blink_is_paused.store(true, Ordering::Relaxed);
  1050. self.cursor_is_visible.store(true, Ordering::Relaxed);
  1051. }
  1052. async fn redraw(&self) {
  1053. debug!(target: "ui::editbox", "redraw()");
  1054. let parent_rect = self.parent_rect.lock().unwrap().unwrap().clone();
  1055. self.rect.eval(&parent_rect).expect("unable to eval rect");
  1056. let Some(draw_update) = self.make_draw_calls() else {
  1057. error!(target: "ui::editbox", "Text failed to draw");
  1058. return;
  1059. };
  1060. self.render_api.replace_draw_calls(draw_update.draw_calls);
  1061. }
  1062. fn redraw_cursor(&self) {
  1063. let cursor_instrs = self.get_cursor_instrs();
  1064. let draw_calls = vec![(
  1065. self.cursor_dc_key,
  1066. GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
  1067. )];
  1068. self.render_api.replace_draw_calls(draw_calls);
  1069. }
  1070. fn get_cursor_instrs(&self) -> Vec<GfxDrawInstruction> {
  1071. if !self.is_focused.get() ||
  1072. !self.cursor_is_visible.load(Ordering::Relaxed) ||
  1073. self.hide_cursor.load(Ordering::Relaxed)
  1074. {
  1075. return vec![]
  1076. }
  1077. let mut cursor_instrs = vec![];
  1078. let rect_w = self.rect.get().w;
  1079. let mut cursor_pos = Point::zero();
  1080. cursor_pos.x += self.cursor_px_offset();
  1081. if cursor_pos.x > rect_w {
  1082. return vec![]
  1083. }
  1084. cursor_instrs.push(GfxDrawInstruction::Move(cursor_pos));
  1085. let cursor_mesh = {
  1086. let mut cursor_mesh = self.cursor_mesh.lock().unwrap();
  1087. if cursor_mesh.is_none() {
  1088. *cursor_mesh = Some(self.regen_cursor_mesh());
  1089. }
  1090. cursor_mesh.clone().unwrap()
  1091. };
  1092. cursor_instrs.push(GfxDrawInstruction::Draw(cursor_mesh));
  1093. cursor_instrs
  1094. }
  1095. fn make_draw_calls(&self) -> Option<DrawUpdate> {
  1096. let rect = self.rect.get();
  1097. let text_mesh = self.regen_text_mesh(rect.clone());
  1098. let cursor_instrs = self.get_cursor_instrs();
  1099. Some(DrawUpdate {
  1100. key: self.text_dc_key,
  1101. draw_calls: vec![
  1102. (
  1103. self.text_dc_key,
  1104. GfxDrawCall {
  1105. instrs: vec![
  1106. GfxDrawInstruction::Move(rect.pos()),
  1107. GfxDrawInstruction::Draw(text_mesh),
  1108. ],
  1109. dcs: vec![self.cursor_dc_key],
  1110. z_index: self.z_index.get(),
  1111. },
  1112. ),
  1113. (
  1114. self.cursor_dc_key,
  1115. GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
  1116. ),
  1117. ],
  1118. })
  1119. }
  1120. }
  1121. impl Drop for EditBox {
  1122. fn drop(&mut self) {
  1123. self.render_api.replace_draw_calls(vec![(self.text_dc_key, Default::default())]);
  1124. }
  1125. }
  1126. #[async_trait]
  1127. impl UIObject for EditBox {
  1128. fn z_index(&self) -> u32 {
  1129. self.z_index.get()
  1130. }
  1131. async fn start(self: Arc<Self>, ex: ExecutorPtr) {
  1132. let me = Arc::downgrade(&self);
  1133. let node_ref = &self.node.upgrade().unwrap();
  1134. let node_name = node_ref.name.clone();
  1135. let node_id = node_ref.id;
  1136. let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone());
  1137. on_modify.when_change(self.is_focused.prop(), Self::change_focus);
  1138. // When text has been changed.
  1139. // Cursor and selection might be invalidated.
  1140. async fn reset(self_: Arc<EditBox>) {
  1141. self_.cursor_pos.set(0);
  1142. self_.selected.set_null(Role::Internal, 0).unwrap();
  1143. self_.selected.set_null(Role::Internal, 1).unwrap();
  1144. self_.scroll.set(0.);
  1145. self_.redraw();
  1146. }
  1147. async fn redraw(self_: Arc<EditBox>) {
  1148. self_.redraw().await;
  1149. }
  1150. on_modify.when_change(self.rect.prop(), redraw);
  1151. on_modify.when_change(self.baseline.prop(), redraw);
  1152. // The commented properties are modified on input events
  1153. // So then redraw() will get repeatedly triggered when these properties
  1154. // are changed. We should find a solution. For now the hooks are disabled.
  1155. //on_modify.when_change(scroll.prop(), redraw);
  1156. //on_modify.when_change(cursor_pos.prop(), redraw);
  1157. on_modify.when_change(self.font_size.prop(), redraw);
  1158. // We must also reshape text
  1159. on_modify.when_change(self.text.prop(), reset);
  1160. on_modify.when_change(self.text_color.prop(), redraw);
  1161. on_modify.when_change(self.hi_bg_color.prop(), redraw);
  1162. //on_modify.when_change(selected.clone(), redraw);
  1163. on_modify.when_change(self.z_index.prop(), redraw);
  1164. on_modify.when_change(self.debug.prop(), redraw);
  1165. async fn regen_cursor(self_: Arc<EditBox>) {
  1166. let mesh = std::mem::take(&mut *self_.cursor_mesh.lock().unwrap());
  1167. }
  1168. on_modify.when_change(self.cursor_color.prop(), regen_cursor);
  1169. on_modify.when_change(self.cursor_ascent.prop(), regen_cursor);
  1170. on_modify.when_change(self.cursor_descent.prop(), regen_cursor);
  1171. on_modify.when_change(self.cursor_width.prop(), regen_cursor);
  1172. let me2 = me.clone();
  1173. let cursor_blink_time = self.cursor_blink_time.clone();
  1174. let cursor_idle_time = self.cursor_idle_time.clone();
  1175. let blinking_cursor_task = ex.spawn(async move {
  1176. loop {
  1177. msleep(cursor_blink_time.get() as u64).await;
  1178. let self_ = me2.upgrade().unwrap();
  1179. if self_.blink_is_paused.swap(false, Ordering::Relaxed) {
  1180. msleep(cursor_idle_time.get() as u64).await;
  1181. continue
  1182. }
  1183. // Invert the bool
  1184. self_.cursor_is_visible.fetch_not(Ordering::Relaxed);
  1185. self_.redraw_cursor();
  1186. }
  1187. });
  1188. let mut tasks = on_modify.tasks;
  1189. tasks.push(blinking_cursor_task);
  1190. self.tasks.set(tasks);
  1191. }
  1192. async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
  1193. debug!(target: "ui::editbox", "EditBox::draw()");
  1194. *self.parent_rect.lock().unwrap() = Some(parent_rect);
  1195. self.rect.eval(&parent_rect).ok()?;
  1196. self.clamp_scroll();
  1197. self.make_draw_calls()
  1198. }
  1199. async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) -> bool {
  1200. // First filter for only single digit keys
  1201. if DISALLOWED_CHARS.contains(&key) {
  1202. return false
  1203. }
  1204. if !self.is_focused.get() {
  1205. return false
  1206. }
  1207. if mods.ctrl || mods.alt {
  1208. if repeat {
  1209. return false
  1210. }
  1211. self.handle_shortcut(key, &mods).await;
  1212. return true
  1213. }
  1214. let actions = {
  1215. let mut repeater = self.key_repeat.lock().unwrap();
  1216. repeater.key_down(PressedKey::Char(key), repeat)
  1217. };
  1218. //debug!(target: "ui::editbox", "Key {:?} has {} actions", key, actions);
  1219. for _ in 0..actions {
  1220. self.insert_char(key).await;
  1221. }
  1222. true
  1223. }
  1224. async fn handle_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
  1225. // First filter for only single digit keys
  1226. // Avoid processing events handled by insert_char()
  1227. if !ALLOWED_KEYCODES.contains(&key) {
  1228. return false
  1229. }
  1230. if !self.is_focused.get() {
  1231. return false
  1232. }
  1233. let actions = {
  1234. let mut repeater = self.key_repeat.lock().unwrap();
  1235. repeater.key_down(PressedKey::Key(key), repeat)
  1236. };
  1237. // Suppress noisy message
  1238. /*if actions > 0 {
  1239. debug!(target: "ui::editbox", "Key {:?} has {} actions", key, actions);
  1240. }*/
  1241. for _ in 0..actions {
  1242. self.handle_key(&key, &mods).await;
  1243. }
  1244. true
  1245. }
  1246. async fn handle_mouse_btn_down(&self, btn: MouseButton, mouse_pos: Point) -> bool {
  1247. if !self.is_active.get() {
  1248. return false
  1249. }
  1250. if btn != MouseButton::Left {
  1251. return false
  1252. }
  1253. let rect = self.rect.get();
  1254. // clicking inside box will:
  1255. // 1. make it active
  1256. // 2. begin selection
  1257. if !rect.contains(mouse_pos) {
  1258. if self.is_focused.get() {
  1259. debug!(target: "ui::editbox", "EditBox unfocused");
  1260. self.is_focused.set(false);
  1261. self.select.lock().unwrap().clear();
  1262. self.redraw().await;
  1263. }
  1264. return false
  1265. }
  1266. if self.is_focused.get() {
  1267. debug!(target: "ui::editbox", "EditBox clicked");
  1268. } else {
  1269. debug!(target: "ui::editbox", "EditBox focused");
  1270. self.is_focused.set(true);
  1271. }
  1272. let font_size = self.font_size.get();
  1273. let window_scale = self.window_scale.get();
  1274. let baseline = self.baseline.get();
  1275. {
  1276. let mut editable = self.editable.lock().unwrap();
  1277. let rendered = editable.render();
  1278. let x = mouse_pos.x - rect.x + self.scroll.get();
  1279. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  1280. let cidx = rendered.pos_to_idx(cpos);
  1281. editable.set_cursor_idx(cidx);
  1282. // begin selection
  1283. let mut select = self.select.lock().unwrap();
  1284. select.clear();
  1285. select.push(Selection::new(cpos, cpos));
  1286. self.mouse_btn_held.store(true, Ordering::Relaxed);
  1287. }
  1288. self.pause_blinking();
  1289. self.redraw().await;
  1290. true
  1291. }
  1292. async fn handle_mouse_btn_up(&self, btn: MouseButton, mouse_pos: Point) -> bool {
  1293. if !self.is_active.get() {
  1294. return false
  1295. }
  1296. // releasing mouse button will end selection
  1297. self.mouse_btn_held.store(false, Ordering::Relaxed);
  1298. false
  1299. }
  1300. async fn handle_mouse_move(&self, mouse_pos: Point) -> bool {
  1301. if !self.is_active.get() {
  1302. return false
  1303. }
  1304. let rect = self.rect.get();
  1305. self.is_mouse_hover.store(rect.contains(mouse_pos), Ordering::Relaxed);
  1306. if !self.mouse_btn_held.load(Ordering::Relaxed) {
  1307. return false;
  1308. }
  1309. // if active and selection_active, then use x to modify the selection.
  1310. // also implement scrolling when cursor is to the left or right
  1311. // just scroll to the end
  1312. // also set cursor_pos too
  1313. let font_size = self.font_size.get();
  1314. let window_scale = self.window_scale.get();
  1315. let baseline = self.baseline.get();
  1316. {
  1317. let mut editable = self.editable.lock().unwrap();
  1318. let rendered = editable.render();
  1319. let x = mouse_pos.x - rect.x + self.scroll.get();
  1320. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  1321. let cidx = rendered.pos_to_idx(cpos);
  1322. editable.set_cursor_idx(cidx);
  1323. // begin selection
  1324. let mut select = self.select.lock().unwrap();
  1325. select.last_mut().unwrap().end = cpos;
  1326. }
  1327. self.pause_blinking();
  1328. //self.apply_cursor_scrolling();
  1329. self.redraw().await;
  1330. true
  1331. }
  1332. async fn handle_mouse_wheel(&self, wheel_pos: Point) -> bool {
  1333. //debug!(target: "ui::editbox", "rect={rect:?}, wheel_pos={wheel_pos:?}");
  1334. if !self.is_mouse_hover.load(Ordering::Relaxed) {
  1335. return false
  1336. }
  1337. let mut scroll = self.scroll.get() + wheel_pos.y * self.scroll_speed.get();
  1338. scroll = scroll.clamp(0., self.max_cursor_scroll());
  1339. debug!(target: "ui::editbox", "handle_mouse_wheel({wheel_pos:?}) [scroll={scroll}]");
  1340. self.scroll.set(scroll);
  1341. self.redraw().await;
  1342. true
  1343. }
  1344. async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_pos: Point) -> bool {
  1345. if !self.is_active.get() {
  1346. return false
  1347. }
  1348. // Ignore multi-touch
  1349. if id != 0 {
  1350. return false
  1351. }
  1352. match phase {
  1353. TouchPhase::Started => self.handle_touch_start(touch_pos).await,
  1354. TouchPhase::Moved => self.handle_touch_move(touch_pos).await,
  1355. TouchPhase::Ended => self.handle_touch_end(touch_pos).await,
  1356. TouchPhase::Cancelled => false,
  1357. }
  1358. }
  1359. async fn handle_compose_text(&self, suggest_text: &str, is_commit: bool) -> bool {
  1360. debug!(target: "ui::editbox", "handle_compose_text({suggest_text}, {is_commit})");
  1361. if !self.is_active.get() {
  1362. return false
  1363. }
  1364. {
  1365. let mut editable = self.editable.lock().unwrap();
  1366. editable.compose(suggest_text, is_commit);
  1367. }
  1368. //self.apply_cursor_scrolling();
  1369. self.clamp_scroll();
  1370. self.redraw().await;
  1371. true
  1372. }
  1373. async fn handle_set_compose_region(&self, start: usize, end: usize) -> bool {
  1374. debug!(target: "ui::editbox", "handle_set_compose_region({start}, {end})");
  1375. if !self.is_active.get() {
  1376. return false
  1377. }
  1378. {
  1379. let mut editable = self.editable.lock().unwrap();
  1380. editable.set_compose_region(start, end);
  1381. }
  1382. self.redraw().await;
  1383. true
  1384. }
  1385. }
  1386. /// Filter these char events from being handled since we handle them
  1387. /// using the key_up/key_down events.
  1388. /// Avoids duplicate processing of keyboard input events.
  1389. static DISALLOWED_CHARS: &'static [char] = &['\r', '\u{8}', '\u{7f}', '\t', '\n'];
  1390. /// These keycodes are handled via normal key_up/key_down events.
  1391. /// Anything in this list must be disallowed char events.
  1392. static ALLOWED_KEYCODES: &'static [KeyCode] = &[
  1393. KeyCode::Left,
  1394. KeyCode::Right,
  1395. KeyCode::Up,
  1396. KeyCode::Down,
  1397. KeyCode::Enter,
  1398. KeyCode::Kp0,
  1399. KeyCode::Kp1,
  1400. KeyCode::Kp2,
  1401. KeyCode::Kp3,
  1402. KeyCode::Kp4,
  1403. KeyCode::Kp5,
  1404. KeyCode::Kp6,
  1405. KeyCode::Kp7,
  1406. KeyCode::Kp8,
  1407. KeyCode::Kp9,
  1408. KeyCode::KpDecimal,
  1409. KeyCode::KpEnter,
  1410. KeyCode::Delete,
  1411. KeyCode::Backspace,
  1412. KeyCode::Home,
  1413. KeyCode::End,
  1414. ];
  1415. /*
  1416. impl Stoppable for EditBox {
  1417. async fn stop(&self) {
  1418. // TODO: Delete own draw call
  1419. // Free buffers
  1420. // Should this be in drop?
  1421. //self.render_api.delete_buffer(self.vertex_buffer);
  1422. //self.render_api.delete_buffer(self.index_buffer);
  1423. }
  1424. }
  1425. */