mod.rs 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680
  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. // Adjust with scroll here too
  739. let x = x - rect.x;
  740. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  741. // Find word start
  742. let mut cpos_start = cpos;
  743. while cpos_start > 0 {
  744. // Is the glyph before this pos just whitespace?
  745. let glyph_str = &rendered.glyphs[cpos_start - 1].substr;
  746. if is_whitespace(glyph_str) {
  747. break
  748. }
  749. cpos_start -= 1;
  750. }
  751. // Find word end
  752. let mut cpos_end = cpos;
  753. while cpos_end < rendered.glyphs.len() {
  754. cpos_end += 1;
  755. let glyph_str = &rendered.glyphs[cpos_end].substr;
  756. if is_whitespace(glyph_str) {
  757. break
  758. }
  759. }
  760. let cidx_start = rendered.pos_to_idx(cpos_start);
  761. let cidx_end = rendered.pos_to_idx(cpos_end);
  762. // begin selection
  763. let mut select = self.select.lock().unwrap();
  764. select.clear();
  765. select.push(Selection::new(cpos_start, cpos_end));
  766. }
  767. self.is_phone_select.store(true, Ordering::Relaxed);
  768. // redraw() will now hide the cursor
  769. self.hide_cursor.store(true, Ordering::Relaxed);
  770. }
  771. fn glyphs_to_string(glyphs: &Vec<Glyph>) -> String {
  772. let mut text = String::new();
  773. for (i, glyph) in glyphs.iter().enumerate() {
  774. text.push_str(&glyph.substr);
  775. }
  776. text
  777. }
  778. fn delete_highlighted(&self) {
  779. assert!(!self.selected.is_null(0).unwrap());
  780. assert!(!self.selected.is_null(1).unwrap());
  781. let start = self.selected.get_u32(0).unwrap() as usize;
  782. let end = self.selected.get_u32(1).unwrap() as usize;
  783. let sel_start = std::cmp::min(start, end);
  784. let sel_end = std::cmp::max(start, end);
  785. let mut glyphs = self.glyphs.lock().unwrap().clone();
  786. glyphs.drain(sel_start..sel_end);
  787. let text = Self::glyphs_to_string(&glyphs);
  788. debug!(
  789. target: "ui::editbox",
  790. "delete_highlighted() text=\"{}\", cursor_pos={}",
  791. text, sel_start
  792. );
  793. self.text.set(text);
  794. self.selected.set_null(Role::Internal, 0).unwrap();
  795. self.selected.set_null(Role::Internal, 1).unwrap();
  796. self.cursor_pos.set(sel_start as u32);
  797. }
  798. fn copy_highlighted(&self) -> Result<()> {
  799. let start = self.selected.get_u32(0)? as usize;
  800. let end = self.selected.get_u32(1)? as usize;
  801. let sel_start = std::cmp::min(start, end);
  802. let sel_end = std::cmp::max(start, end);
  803. let mut text = String::new();
  804. let glyphs = self.glyphs.lock().unwrap().clone();
  805. for (glyph_idx, glyph) in glyphs.iter().enumerate() {
  806. if sel_start <= glyph_idx && glyph_idx < sel_end {
  807. text.push_str(&glyph.substr);
  808. }
  809. }
  810. info!(target: "ui::editbox", "Copied '{}'", text);
  811. window::clipboard_set(&text);
  812. Ok(())
  813. }
  814. async fn paste_text(&self, key: String) {
  815. let mut text = String::new();
  816. let cursor_pos = self.cursor_pos.get();
  817. if cursor_pos == 0 {
  818. text = key.clone();
  819. }
  820. let glyphs = self.glyphs.lock().unwrap().clone();
  821. for (glyph_idx, glyph) in glyphs.iter().enumerate() {
  822. text.push_str(&glyph.substr);
  823. if cursor_pos == glyph_idx as u32 + 1 {
  824. text.push_str(&key);
  825. }
  826. }
  827. self.text.set(text);
  828. // Not always true lol
  829. self.cursor_pos.set(cursor_pos + 1);
  830. self.apply_cursor_scrolling();
  831. self.redraw().await;
  832. }
  833. async fn handle_touch_start(&self, pos: Point) -> bool {
  834. debug!(target: "ui::editbox", "handle_touch_start({pos:?})");
  835. let mut touch_info = self.touch_info.lock().unwrap();
  836. if self.try_handle_drag(&mut touch_info, pos) {
  837. return true
  838. }
  839. let rect = self.rect.get();
  840. if !rect.contains(pos) {}
  841. touch_info.start(pos);
  842. true
  843. }
  844. fn try_handle_drag(&self, touch_info: &mut TouchInfo, pos: Point) -> bool {
  845. let selections = self.select.lock().unwrap().clone();
  846. if self.is_phone_select.load(Ordering::Relaxed) && selections.len() == 1 {
  847. let select = selections.first().unwrap();
  848. let rendered = {
  849. let editable = self.editable.lock().unwrap();
  850. editable.render()
  851. };
  852. let font_size = self.font_size.get();
  853. let window_scale = self.window_scale.get();
  854. let baseline = self.baseline.get();
  855. // Get left handle centerpoint
  856. let x1 = rendered.pos_to_xw(select.start, font_size, window_scale, baseline).0;
  857. // Get right handle centerpoint
  858. let x2 = {
  859. let (x, w) = rendered.pos_to_xw(select.end, font_size, window_scale, baseline);
  860. x + w
  861. };
  862. // Are we within range of either one?
  863. let select_descent = self.select_descent.get();
  864. let y = baseline + select_descent + 25.;
  865. let p1 = Point::new(x1, y);
  866. let p2 = Point::new(x2, 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 mut pos = rendered.x_to_pos(pos.x, font_size, window_scale, baseline);
  917. if *side == -1 {
  918. let select_other_pos = &mut select.end;
  919. if pos >= *select_other_pos {
  920. pos = *select_other_pos - 1;
  921. }
  922. select.start = pos;
  923. } else {
  924. let select_other_pos = &mut select.start;
  925. if pos <= *select_other_pos {
  926. pos = *select_other_pos + 1;
  927. }
  928. select.end = pos;
  929. }
  930. }
  931. self.redraw().await;
  932. }
  933. TouchStateAction::ScrollHoriz { start_pos, scroll_start } => {
  934. let x_dist = start_pos.x - pos.x;
  935. let mut scroll = scroll_start + x_dist;
  936. scroll = scroll.clamp(0., self.max_cursor_scroll());
  937. self.scroll.set(scroll);
  938. self.redraw().await;
  939. }
  940. _ => {}
  941. }
  942. true
  943. }
  944. async fn handle_touch_end(&self, pos: Point) -> bool {
  945. debug!(target: "ui::editbox", "handle_touch_end({pos:?})");
  946. let state = self.touch_info.lock().unwrap().stop();
  947. match state {
  948. TouchStateAction::Inactive => return false,
  949. TouchStateAction::Started { pos: _, instant: _ } => {
  950. let rect = self.rect.get();
  951. let font_size = self.font_size.get();
  952. let window_scale = self.window_scale.get();
  953. let baseline = self.baseline.get();
  954. {
  955. let mut editable = self.editable.lock().unwrap();
  956. let rendered = editable.render();
  957. // Adjust with scroll here too
  958. let x = pos.x - rect.x;
  959. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  960. let cidx = rendered.pos_to_idx(cpos);
  961. editable.set_cursor_idx(cidx);
  962. // begin selection
  963. let mut select = self.select.lock().unwrap();
  964. select.clear();
  965. }
  966. self.is_phone_select.store(false, Ordering::Relaxed);
  967. // Reshow cursor (if hidden)
  968. self.hide_cursor.store(false, Ordering::Relaxed);
  969. self.redraw().await;
  970. }
  971. _ => {}
  972. }
  973. window::show_keyboard(true);
  974. true
  975. }
  976. /// Whenever the cursor property is modified this MUST be called
  977. /// to recalculate the scroll x property.
  978. fn apply_cursor_scrolling(&self) {
  979. let rect = self.rect.get();
  980. let cursor_pos = self.cursor_pos.get() as usize;
  981. let cursor_width = self.cursor_width.get();
  982. let mut scroll = self.scroll.get();
  983. let cursor_x = {
  984. let font_size = self.font_size.get();
  985. let window_scale = self.window_scale.get();
  986. let baseline = self.baseline.get();
  987. let glyphs = self.glyphs.lock().unwrap().clone();
  988. let mut glyph_pos_iter =
  989. GlyphPositionIter::new(font_size, window_scale, &glyphs, baseline);
  990. if cursor_pos == 0 {
  991. 0.
  992. } else if cursor_pos == glyphs.len() {
  993. let glyph_pos = glyph_pos_iter.last().unwrap();
  994. let rhs = glyph_pos.rhs() + eol_nudge(font_size, &glyphs);
  995. rhs
  996. } else {
  997. assert!(cursor_pos < glyphs.len());
  998. let glyph_pos = glyph_pos_iter.nth(cursor_pos).expect("glyph pos mismatch glyphs");
  999. glyph_pos.x
  1000. }
  1001. };
  1002. // The LHS and RHS of the cursor box
  1003. let cursor_lhs = cursor_x - scroll;
  1004. let cursor_rhs = cursor_lhs + cursor_width;
  1005. // RHS is outside
  1006. if cursor_rhs > rect.w {
  1007. // We want a scroll so RHS = w
  1008. // cursor_x - scroll + CURSOR_WIDTH = rect.w
  1009. scroll = cursor_x + cursor_width - rect.w;
  1010. // LHS is negative
  1011. } else if cursor_lhs < 0. {
  1012. // We want scroll so LHS = 0
  1013. // cursor_x - scroll = 0
  1014. scroll = cursor_x;
  1015. }
  1016. self.scroll.set(scroll);
  1017. }
  1018. fn max_cursor_scroll(&self) -> f32 {
  1019. let font_size = self.font_size.get();
  1020. let window_scale = self.window_scale.get();
  1021. let baseline = self.baseline.get();
  1022. let rhs = {
  1023. let mut editable = self.editable.lock().unwrap();
  1024. let rendered = editable.render();
  1025. if rendered.glyphs.is_empty() {
  1026. return 0.
  1027. }
  1028. let glyph_pos_iter =
  1029. GlyphPositionIter::new(font_size, window_scale, &rendered.glyphs, baseline);
  1030. let last_rect = glyph_pos_iter.last().unwrap();
  1031. last_rect.x + last_rect.w
  1032. };
  1033. let rect_w = self.rect.get().w;
  1034. if rhs <= rect_w {
  1035. return 0.;
  1036. }
  1037. let max_scroll = rhs - rect_w;
  1038. max_scroll
  1039. }
  1040. fn clamp_scroll(&self) {
  1041. let mut scroll = self.scroll.get();
  1042. scroll = scroll.clamp(0., self.max_cursor_scroll());
  1043. self.scroll.set(scroll);
  1044. }
  1045. fn pause_blinking(&self) {
  1046. self.blink_is_paused.store(true, Ordering::Relaxed);
  1047. self.cursor_is_visible.store(true, Ordering::Relaxed);
  1048. }
  1049. async fn redraw(&self) {
  1050. let Some(draw_update) = self.make_draw_calls() else {
  1051. error!(target: "ui::editbox", "Text failed to draw");
  1052. return;
  1053. };
  1054. self.render_api.replace_draw_calls(draw_update.draw_calls);
  1055. }
  1056. fn redraw_cursor(&self) {
  1057. let cursor_instrs = self.get_cursor_instrs();
  1058. let draw_calls = vec![(
  1059. self.cursor_dc_key,
  1060. GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
  1061. )];
  1062. self.render_api.replace_draw_calls(draw_calls);
  1063. }
  1064. fn get_cursor_instrs(&self) -> Vec<GfxDrawInstruction> {
  1065. if !self.is_focused.get() ||
  1066. !self.cursor_is_visible.load(Ordering::Relaxed) ||
  1067. self.hide_cursor.load(Ordering::Relaxed)
  1068. {
  1069. return vec![]
  1070. }
  1071. let mut cursor_instrs = vec![];
  1072. let rect_w = self.rect.get().w;
  1073. let mut cursor_pos = Point::zero();
  1074. cursor_pos.x += self.cursor_px_offset();
  1075. if cursor_pos.x > rect_w {
  1076. return vec![]
  1077. }
  1078. cursor_instrs.push(GfxDrawInstruction::Move(cursor_pos));
  1079. let cursor_mesh = {
  1080. let mut cursor_mesh = self.cursor_mesh.lock().unwrap();
  1081. if cursor_mesh.is_none() {
  1082. *cursor_mesh = Some(self.regen_cursor_mesh());
  1083. }
  1084. cursor_mesh.clone().unwrap()
  1085. };
  1086. cursor_instrs.push(GfxDrawInstruction::Draw(cursor_mesh));
  1087. cursor_instrs
  1088. }
  1089. fn make_draw_calls(&self) -> Option<DrawUpdate> {
  1090. let rect = self.rect.get();
  1091. let text_mesh = self.regen_text_mesh(rect.clone());
  1092. let cursor_instrs = self.get_cursor_instrs();
  1093. Some(DrawUpdate {
  1094. key: self.text_dc_key,
  1095. draw_calls: vec![
  1096. (
  1097. self.text_dc_key,
  1098. GfxDrawCall {
  1099. instrs: vec![
  1100. GfxDrawInstruction::Move(rect.pos()),
  1101. GfxDrawInstruction::Draw(text_mesh),
  1102. ],
  1103. dcs: vec![self.cursor_dc_key],
  1104. z_index: self.z_index.get(),
  1105. },
  1106. ),
  1107. (
  1108. self.cursor_dc_key,
  1109. GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
  1110. ),
  1111. ],
  1112. })
  1113. }
  1114. }
  1115. impl Drop for EditBox {
  1116. fn drop(&mut self) {
  1117. self.render_api.replace_draw_calls(vec![(self.text_dc_key, Default::default())]);
  1118. }
  1119. }
  1120. #[async_trait]
  1121. impl UIObject for EditBox {
  1122. fn z_index(&self) -> u32 {
  1123. self.z_index.get()
  1124. }
  1125. async fn start(self: Arc<Self>, ex: ExecutorPtr) {
  1126. let me = Arc::downgrade(&self);
  1127. let node_ref = &self.node.upgrade().unwrap();
  1128. let node_name = node_ref.name.clone();
  1129. let node_id = node_ref.id;
  1130. let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone());
  1131. on_modify.when_change(self.is_focused.prop(), Self::change_focus);
  1132. // When text has been changed.
  1133. // Cursor and selection might be invalidated.
  1134. async fn reset(self_: Arc<EditBox>) {
  1135. self_.cursor_pos.set(0);
  1136. self_.selected.set_null(Role::Internal, 0).unwrap();
  1137. self_.selected.set_null(Role::Internal, 1).unwrap();
  1138. self_.scroll.set(0.);
  1139. self_.redraw();
  1140. }
  1141. async fn redraw(self_: Arc<EditBox>) {
  1142. self_.redraw();
  1143. }
  1144. on_modify.when_change(self.rect.prop(), redraw);
  1145. on_modify.when_change(self.baseline.prop(), redraw);
  1146. // The commented properties are modified on input events
  1147. // So then redraw() will get repeatedly triggered when these properties
  1148. // are changed. We should find a solution. For now the hooks are disabled.
  1149. //on_modify.when_change(scroll.prop(), redraw);
  1150. //on_modify.when_change(cursor_pos.prop(), redraw);
  1151. on_modify.when_change(self.font_size.prop(), redraw);
  1152. // We must also reshape text
  1153. on_modify.when_change(self.text.prop(), reset);
  1154. on_modify.when_change(self.text_color.prop(), redraw);
  1155. on_modify.when_change(self.hi_bg_color.prop(), redraw);
  1156. //on_modify.when_change(selected.clone(), redraw);
  1157. on_modify.when_change(self.z_index.prop(), redraw);
  1158. on_modify.when_change(self.debug.prop(), redraw);
  1159. async fn regen_cursor(self_: Arc<EditBox>) {
  1160. let mesh = std::mem::take(&mut *self_.cursor_mesh.lock().unwrap());
  1161. }
  1162. on_modify.when_change(self.cursor_color.prop(), regen_cursor);
  1163. on_modify.when_change(self.cursor_ascent.prop(), regen_cursor);
  1164. on_modify.when_change(self.cursor_descent.prop(), regen_cursor);
  1165. on_modify.when_change(self.cursor_width.prop(), regen_cursor);
  1166. let me2 = me.clone();
  1167. let cursor_blink_time = self.cursor_blink_time.clone();
  1168. let cursor_idle_time = self.cursor_idle_time.clone();
  1169. let blinking_cursor_task = ex.spawn(async move {
  1170. loop {
  1171. msleep(cursor_blink_time.get() as u64).await;
  1172. let self_ = me2.upgrade().unwrap();
  1173. if self_.blink_is_paused.swap(false, Ordering::Relaxed) {
  1174. msleep(cursor_idle_time.get() as u64).await;
  1175. continue
  1176. }
  1177. // Invert the bool
  1178. self_.cursor_is_visible.fetch_not(Ordering::Relaxed);
  1179. self_.redraw_cursor();
  1180. }
  1181. });
  1182. let mut tasks = on_modify.tasks;
  1183. tasks.push(blinking_cursor_task);
  1184. self.tasks.set(tasks);
  1185. }
  1186. async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
  1187. debug!(target: "ui::editbox", "EditBox::draw()");
  1188. *self.parent_rect.lock().unwrap() = Some(parent_rect);
  1189. self.rect.eval(&parent_rect).ok()?;
  1190. self.clamp_scroll();
  1191. self.make_draw_calls()
  1192. }
  1193. async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) -> bool {
  1194. // First filter for only single digit keys
  1195. if DISALLOWED_CHARS.contains(&key) {
  1196. return false
  1197. }
  1198. if !self.is_focused.get() {
  1199. return false
  1200. }
  1201. if mods.ctrl || mods.alt {
  1202. if repeat {
  1203. return false
  1204. }
  1205. self.handle_shortcut(key, &mods).await;
  1206. return true
  1207. }
  1208. let actions = {
  1209. let mut repeater = self.key_repeat.lock().unwrap();
  1210. repeater.key_down(PressedKey::Char(key), repeat)
  1211. };
  1212. //debug!(target: "ui::editbox", "Key {:?} has {} actions", key, actions);
  1213. for _ in 0..actions {
  1214. self.insert_char(key).await;
  1215. }
  1216. true
  1217. }
  1218. async fn handle_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
  1219. // First filter for only single digit keys
  1220. // Avoid processing events handled by insert_char()
  1221. if !ALLOWED_KEYCODES.contains(&key) {
  1222. return false
  1223. }
  1224. if !self.is_focused.get() {
  1225. return false
  1226. }
  1227. let actions = {
  1228. let mut repeater = self.key_repeat.lock().unwrap();
  1229. repeater.key_down(PressedKey::Key(key), repeat)
  1230. };
  1231. // Suppress noisy message
  1232. /*if actions > 0 {
  1233. debug!(target: "ui::editbox", "Key {:?} has {} actions", key, actions);
  1234. }*/
  1235. for _ in 0..actions {
  1236. self.handle_key(&key, &mods).await;
  1237. }
  1238. true
  1239. }
  1240. async fn handle_mouse_btn_down(&self, btn: MouseButton, mouse_pos: Point) -> bool {
  1241. if !self.is_active.get() {
  1242. return false
  1243. }
  1244. if btn != MouseButton::Left {
  1245. return false
  1246. }
  1247. let rect = self.rect.get();
  1248. // clicking inside box will:
  1249. // 1. make it active
  1250. // 2. begin selection
  1251. if !rect.contains(mouse_pos) {
  1252. if self.is_focused.get() {
  1253. debug!(target: "ui::editbox", "EditBox unfocused");
  1254. self.is_focused.set(false);
  1255. self.select.lock().unwrap().clear();
  1256. self.redraw().await;
  1257. }
  1258. return false
  1259. }
  1260. if self.is_focused.get() {
  1261. debug!(target: "ui::editbox", "EditBox clicked");
  1262. } else {
  1263. debug!(target: "ui::editbox", "EditBox focused");
  1264. self.is_focused.set(true);
  1265. }
  1266. let font_size = self.font_size.get();
  1267. let window_scale = self.window_scale.get();
  1268. let baseline = self.baseline.get();
  1269. {
  1270. let mut editable = self.editable.lock().unwrap();
  1271. let rendered = editable.render();
  1272. // Adjust with scroll here too
  1273. let x = mouse_pos.x - rect.x;
  1274. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  1275. let cidx = rendered.pos_to_idx(cpos);
  1276. editable.set_cursor_idx(cidx);
  1277. // begin selection
  1278. let mut select = self.select.lock().unwrap();
  1279. select.clear();
  1280. select.push(Selection::new(cpos, cpos));
  1281. self.mouse_btn_held.store(true, Ordering::Relaxed);
  1282. }
  1283. self.pause_blinking();
  1284. self.redraw().await;
  1285. true
  1286. }
  1287. async fn handle_mouse_btn_up(&self, btn: MouseButton, mouse_pos: Point) -> bool {
  1288. if !self.is_active.get() {
  1289. return false
  1290. }
  1291. // releasing mouse button will end selection
  1292. self.mouse_btn_held.store(false, Ordering::Relaxed);
  1293. false
  1294. }
  1295. async fn handle_mouse_move(&self, mouse_pos: Point) -> bool {
  1296. if !self.is_active.get() {
  1297. return false
  1298. }
  1299. let rect = self.rect.get();
  1300. self.is_mouse_hover.store(rect.contains(mouse_pos), Ordering::Relaxed);
  1301. if !self.mouse_btn_held.load(Ordering::Relaxed) {
  1302. return false;
  1303. }
  1304. // if active and selection_active, then use x to modify the selection.
  1305. // also implement scrolling when cursor is to the left or right
  1306. // just scroll to the end
  1307. // also set cursor_pos too
  1308. let font_size = self.font_size.get();
  1309. let window_scale = self.window_scale.get();
  1310. let baseline = self.baseline.get();
  1311. {
  1312. let mut editable = self.editable.lock().unwrap();
  1313. let rendered = editable.render();
  1314. // Adjust with scroll here too
  1315. let x = mouse_pos.x - rect.x;
  1316. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  1317. let cidx = rendered.pos_to_idx(cpos);
  1318. editable.set_cursor_idx(cidx);
  1319. // begin selection
  1320. let mut select = self.select.lock().unwrap();
  1321. select.last_mut().unwrap().end = cpos;
  1322. }
  1323. self.pause_blinking();
  1324. //self.apply_cursor_scrolling();
  1325. self.redraw().await;
  1326. true
  1327. }
  1328. async fn handle_mouse_wheel(&self, wheel_pos: Point) -> bool {
  1329. //debug!(target: "ui::editbox", "rect={rect:?}, wheel_pos={wheel_pos:?}");
  1330. if !self.is_mouse_hover.load(Ordering::Relaxed) {
  1331. return false
  1332. }
  1333. let mut scroll = self.scroll.get() + wheel_pos.y * self.scroll_speed.get();
  1334. scroll = scroll.clamp(0., self.max_cursor_scroll());
  1335. debug!(target: "ui::editbox", "handle_mouse_wheel({wheel_pos:?}) [scroll={scroll}]");
  1336. self.scroll.set(scroll);
  1337. self.redraw().await;
  1338. true
  1339. }
  1340. async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_pos: Point) -> bool {
  1341. if !self.is_active.get() {
  1342. return false
  1343. }
  1344. // Ignore multi-touch
  1345. if id != 0 {
  1346. return false
  1347. }
  1348. match phase {
  1349. TouchPhase::Started => self.handle_touch_start(touch_pos).await,
  1350. TouchPhase::Moved => self.handle_touch_move(touch_pos).await,
  1351. TouchPhase::Ended => self.handle_touch_end(touch_pos).await,
  1352. TouchPhase::Cancelled => false,
  1353. }
  1354. }
  1355. async fn handle_compose_text(&self, suggest_text: &str, is_commit: bool) -> bool {
  1356. debug!(target: "ui::editbox", "handle_compose_text({suggest_text}, {is_commit})");
  1357. if !self.is_active.get() {
  1358. return false
  1359. }
  1360. {
  1361. let mut editable = self.editable.lock().unwrap();
  1362. editable.compose(suggest_text, is_commit);
  1363. }
  1364. //self.apply_cursor_scrolling();
  1365. self.clamp_scroll();
  1366. self.redraw().await;
  1367. true
  1368. }
  1369. async fn handle_set_compose_region(&self, start: usize, end: usize) -> bool {
  1370. debug!(target: "ui::editbox", "handle_set_compose_region({start}, {end})");
  1371. if !self.is_active.get() {
  1372. return false
  1373. }
  1374. {
  1375. let mut editable = self.editable.lock().unwrap();
  1376. editable.set_compose_region(start, end);
  1377. }
  1378. self.redraw().await;
  1379. true
  1380. }
  1381. }
  1382. /// Filter these char events from being handled since we handle them
  1383. /// using the key_up/key_down events.
  1384. /// Avoids duplicate processing of keyboard input events.
  1385. static DISALLOWED_CHARS: &'static [char] = &['\r', '\u{8}', '\u{7f}', '\t', '\n'];
  1386. /// These keycodes are handled via normal key_up/key_down events.
  1387. /// Anything in this list must be disallowed char events.
  1388. static ALLOWED_KEYCODES: &'static [KeyCode] = &[
  1389. KeyCode::Left,
  1390. KeyCode::Right,
  1391. KeyCode::Up,
  1392. KeyCode::Down,
  1393. KeyCode::Enter,
  1394. KeyCode::Kp0,
  1395. KeyCode::Kp1,
  1396. KeyCode::Kp2,
  1397. KeyCode::Kp3,
  1398. KeyCode::Kp4,
  1399. KeyCode::Kp5,
  1400. KeyCode::Kp6,
  1401. KeyCode::Kp7,
  1402. KeyCode::Kp8,
  1403. KeyCode::Kp9,
  1404. KeyCode::KpDecimal,
  1405. KeyCode::KpEnter,
  1406. KeyCode::Delete,
  1407. KeyCode::Backspace,
  1408. KeyCode::Home,
  1409. KeyCode::End,
  1410. ];
  1411. /*
  1412. impl Stoppable for EditBox {
  1413. async fn stop(&self) {
  1414. // TODO: Delete own draw call
  1415. // Free buffers
  1416. // Should this be in drop?
  1417. //self.render_api.delete_buffer(self.vertex_buffer);
  1418. //self.render_api.delete_buffer(self.index_buffer);
  1419. }
  1420. }
  1421. */