mod.rs 54 KB

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