mod.rs 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634
  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. let text = {
  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. editable.get_text()
  541. };
  542. let atom = &mut PropertyAtomicGuard::new();
  543. self.text.set(atom, text);
  544. self.pause_blinking();
  545. self.redraw().await;
  546. }
  547. async fn handle_shortcut(&self, key: char, mods: &KeyMods, atom: &mut PropertyAtomicGuard) {
  548. t!("handle_shortcut({:?}, {:?})", key, mods);
  549. match key {
  550. 'c' => {
  551. if mods.ctrl {
  552. self.copy_highlighted().unwrap();
  553. }
  554. }
  555. 'v' => {
  556. if mods.ctrl {
  557. if let Some(text) = window::clipboard_get() {
  558. self.paste_text(text, atom).await;
  559. }
  560. }
  561. }
  562. _ => {}
  563. }
  564. }
  565. async fn handle_key(&self, key: &KeyCode, mods: &KeyMods, atom: &mut PropertyAtomicGuard) {
  566. t!("handle_key({:?}, {:?})", key, mods);
  567. match key {
  568. KeyCode::Left => {
  569. self.adjust_cursor(mods.shift, |editable| editable.move_cursor(-1));
  570. self.pause_blinking();
  571. //self.apply_cursor_scrolling();
  572. self.redraw().await;
  573. }
  574. KeyCode::Right => {
  575. self.adjust_cursor(mods.shift, |editable| editable.move_cursor(1));
  576. self.pause_blinking();
  577. //self.apply_cursor_scrolling();
  578. self.redraw().await;
  579. }
  580. KeyCode::Kp0 => self.insert_char('0').await,
  581. KeyCode::Kp1 => self.insert_char('1').await,
  582. KeyCode::Kp2 => self.insert_char('2').await,
  583. KeyCode::Kp3 => self.insert_char('3').await,
  584. KeyCode::Kp4 => self.insert_char('4').await,
  585. KeyCode::Kp5 => self.insert_char('5').await,
  586. KeyCode::Kp6 => self.insert_char('6').await,
  587. KeyCode::Kp7 => self.insert_char('7').await,
  588. KeyCode::Kp8 => self.insert_char('8').await,
  589. KeyCode::Kp9 => self.insert_char('9').await,
  590. KeyCode::KpDecimal => self.insert_char('.').await,
  591. KeyCode::Enter | KeyCode::KpEnter => {
  592. let node = self.node.upgrade().unwrap();
  593. node.trigger("enter_pressed", vec![]).await.unwrap();
  594. }
  595. KeyCode::Delete => {
  596. self.delete(0, 1);
  597. self.clamp_scroll(atom);
  598. self.pause_blinking();
  599. self.text.set(atom, self.editable.lock().unwrap().get_text());
  600. self.redraw().await;
  601. }
  602. KeyCode::Backspace => {
  603. self.delete(1, 0);
  604. self.clamp_scroll(atom);
  605. self.pause_blinking();
  606. self.text.set(atom, self.editable.lock().unwrap().get_text());
  607. self.redraw().await;
  608. }
  609. KeyCode::Home => {
  610. self.adjust_cursor(mods.shift, |editable| editable.move_start());
  611. self.pause_blinking();
  612. //self.apply_cursor_scrolling();
  613. self.redraw().await;
  614. }
  615. KeyCode::End => {
  616. self.adjust_cursor(mods.shift, |editable| editable.move_end());
  617. self.pause_blinking();
  618. //self.apply_cursor_scrolling();
  619. self.redraw().await;
  620. }
  621. _ => {}
  622. }
  623. }
  624. fn delete(&self, before: usize, after: usize) {
  625. let mut editable = self.editable.lock().unwrap();
  626. let selection = std::mem::take(&mut *self.select.lock().unwrap());
  627. if selection.is_empty() {
  628. editable.delete(before, after);
  629. return
  630. }
  631. let sel = selection.first().unwrap();
  632. let cursor_pos = std::cmp::min(sel.start, sel.end);
  633. let render = editable.render();
  634. // Same code in chatedit's TextWrap::delete_selected()
  635. let mut before_text = String::new();
  636. let mut after_text = String::new();
  637. 'next: for (pos, glyph) in render.glyphs.iter().enumerate() {
  638. for select in &selection {
  639. let start = std::cmp::min(select.start, select.end);
  640. let end = std::cmp::max(select.start, select.end);
  641. if start <= pos && pos < end {
  642. continue 'next
  643. }
  644. }
  645. if pos <= cursor_pos {
  646. before_text += &glyph.substr;
  647. } else {
  648. after_text += &glyph.substr;
  649. }
  650. }
  651. editable.set_text(before_text, after_text);
  652. self.is_phone_select.store(false, Ordering::Relaxed);
  653. // Reshow cursor (if hidden)
  654. self.hide_cursor.store(false, Ordering::Relaxed);
  655. }
  656. fn adjust_cursor(&self, has_shift: bool, move_cursor: impl Fn(&mut Editable)) {
  657. let mut editable = self.editable.lock().unwrap();
  658. let rendered = editable.render();
  659. let prev_cursor_pos = editable.get_cursor_pos(&rendered);
  660. move_cursor(&mut editable);
  661. let cursor_pos = editable.get_cursor_pos(&rendered);
  662. drop(editable);
  663. let mut select = self.select.lock().unwrap();
  664. // Start selection if shift is held
  665. if has_shift {
  666. // Create a new selection
  667. if select.is_empty() {
  668. select.push(Selection::new(prev_cursor_pos, cursor_pos));
  669. }
  670. // Update the selection
  671. select.last_mut().unwrap().end = cursor_pos;
  672. } else {
  673. select.clear();
  674. }
  675. }
  676. /// This will select the entire word rather than move the cursor to that location
  677. fn start_touch_select(&self, x: f32) {
  678. let rect = self.rect.get();
  679. let font_size = self.font_size.get();
  680. let window_scale = self.window_scale.get();
  681. let baseline = self.baseline.get();
  682. {
  683. let mut editable = self.editable.lock().unwrap();
  684. editable.end_compose();
  685. let rendered = editable.render();
  686. let x = x - rect.x + self.scroll.get();
  687. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  688. // Find word start
  689. let mut cpos_start = cpos;
  690. while cpos_start > 0 {
  691. // Is the glyph before this pos just whitespace?
  692. let glyph_str = &rendered.glyphs[cpos_start - 1].substr;
  693. if is_whitespace(glyph_str) {
  694. break
  695. }
  696. cpos_start -= 1;
  697. }
  698. // Find word end
  699. let mut cpos_end = cpos;
  700. while cpos_end < rendered.glyphs.len() {
  701. cpos_end += 1;
  702. let glyph_str = &rendered.glyphs[cpos_end].substr;
  703. if is_whitespace(glyph_str) {
  704. break
  705. }
  706. }
  707. let cidx_start = rendered.pos_to_idx(cpos_start);
  708. let cidx_end = rendered.pos_to_idx(cpos_end);
  709. // begin selection
  710. let mut select = self.select.lock().unwrap();
  711. select.clear();
  712. select.push(Selection::new(cpos_start, cpos_end));
  713. }
  714. self.is_phone_select.store(true, Ordering::Relaxed);
  715. // redraw() will now hide the cursor
  716. self.hide_cursor.store(true, Ordering::Relaxed);
  717. }
  718. fn glyphs_to_string(glyphs: &Vec<Glyph>) -> String {
  719. let mut text = String::new();
  720. for (i, glyph) in glyphs.iter().enumerate() {
  721. text.push_str(&glyph.substr);
  722. }
  723. text
  724. }
  725. fn delete_highlighted(&self, atom: &mut PropertyAtomicGuard) {
  726. assert!(!self.selected.is_null(0).unwrap());
  727. assert!(!self.selected.is_null(1).unwrap());
  728. let start = self.selected.get_u32(0).unwrap() as usize;
  729. let end = self.selected.get_u32(1).unwrap() as usize;
  730. let sel_start = std::cmp::min(start, end);
  731. let sel_end = std::cmp::max(start, end);
  732. let mut glyphs = self.glyphs.lock().unwrap().clone();
  733. glyphs.drain(sel_start..sel_end);
  734. let text = Self::glyphs_to_string(&glyphs);
  735. t!("delete_highlighted() text='{text}', cursor_pos={sel_start}");
  736. self.text.set(atom, text);
  737. self.selected.clone().set_null(atom, Role::Internal, 0).unwrap();
  738. self.selected.clone().set_null(atom, Role::Internal, 1).unwrap();
  739. self.cursor_pos.set(atom, sel_start as u32);
  740. }
  741. fn copy_highlighted(&self) -> Result<()> {
  742. let start = self.selected.get_u32(0)? as usize;
  743. let end = self.selected.get_u32(1)? as usize;
  744. let sel_start = std::cmp::min(start, end);
  745. let sel_end = std::cmp::max(start, end);
  746. let mut text = String::new();
  747. let glyphs = self.glyphs.lock().unwrap().clone();
  748. for (glyph_idx, glyph) in glyphs.iter().enumerate() {
  749. if sel_start <= glyph_idx && glyph_idx < sel_end {
  750. text.push_str(&glyph.substr);
  751. }
  752. }
  753. info!(target: "ui::editbox", "Copied '{}'", text);
  754. window::clipboard_set(&text);
  755. Ok(())
  756. }
  757. async fn paste_text(&self, key: String, atom: &mut PropertyAtomicGuard) {
  758. let mut text = String::new();
  759. let cursor_pos = self.cursor_pos.get();
  760. if cursor_pos == 0 {
  761. text = key.clone();
  762. }
  763. let glyphs = self.glyphs.lock().unwrap().clone();
  764. for (glyph_idx, glyph) in glyphs.iter().enumerate() {
  765. text.push_str(&glyph.substr);
  766. if cursor_pos == glyph_idx as u32 + 1 {
  767. text.push_str(&key);
  768. }
  769. }
  770. self.text.set(atom, text);
  771. // Not always true lol
  772. self.cursor_pos.set(atom, cursor_pos + 1);
  773. self.apply_cursor_scrolling(atom);
  774. self.redraw().await;
  775. }
  776. async fn handle_touch_start(&self, pos: Point) -> bool {
  777. t!("handle_touch_start({pos:?})");
  778. let mut touch_info = self.touch_info.lock().unwrap();
  779. if self.try_handle_drag(&mut touch_info, pos) {
  780. return true
  781. }
  782. let rect = self.rect.get();
  783. if !rect.contains(pos) {}
  784. touch_info.start(pos);
  785. true
  786. }
  787. fn try_handle_drag(&self, touch_info: &mut TouchInfo, pos: Point) -> bool {
  788. let selections = self.select.lock().unwrap().clone();
  789. if self.is_phone_select.load(Ordering::Relaxed) && selections.len() == 1 {
  790. let select = selections.first().unwrap();
  791. let rendered = {
  792. let editable = self.editable.lock().unwrap();
  793. editable.render()
  794. };
  795. let font_size = self.font_size.get();
  796. let window_scale = self.window_scale.get();
  797. let baseline = self.baseline.get();
  798. // Get left handle centerpoint
  799. let x1 = rendered.pos_to_xw(select.start, font_size, window_scale, baseline).0;
  800. // Get right handle centerpoint
  801. let x2 = {
  802. let (x, w) = rendered.pos_to_xw(select.end, font_size, window_scale, baseline);
  803. x + w
  804. };
  805. // Are we within range of either one?
  806. let select_descent = self.select_descent.get();
  807. let scroll = self.scroll.get();
  808. let y = baseline + select_descent + 25.;
  809. let p1 = Point::new(x1 - scroll, y);
  810. let p2 = Point::new(x2 - scroll, y);
  811. t!("handle center points = ({p1:?}, {p2:?})");
  812. const TOUCH_RADIUS_SQ: f32 = 10_000.;
  813. // Make pos relative to the rect
  814. let pos_rel = pos - self.rect.get().pos();
  815. if p1.dist_sq(pos_rel) <= TOUCH_RADIUS_SQ {
  816. d!("TouchStateAction::DragSelectHandle [side=-1]");
  817. // Set touch_state status to enable begin dragging them
  818. touch_info.state = TouchStateAction::DragSelectHandle { side: -1 };
  819. return true
  820. }
  821. if p2.dist_sq(pos_rel) <= TOUCH_RADIUS_SQ {
  822. d!("TouchStateAction::DragSelectHandle [side=1]");
  823. // Set touch_state status to enable begin dragging them
  824. touch_info.state = TouchStateAction::DragSelectHandle { side: 1 };
  825. return true
  826. }
  827. }
  828. false
  829. }
  830. async fn handle_touch_move(&self, pos: Point) -> bool {
  831. t!("handle_touch_move({pos:?})");
  832. let atom = &mut PropertyAtomicGuard::new();
  833. let touch_state = {
  834. let mut touch_info = self.touch_info.lock().unwrap();
  835. touch_info.update(&pos);
  836. touch_info.state.clone()
  837. };
  838. match &touch_state {
  839. TouchStateAction::Inactive => return false,
  840. TouchStateAction::StartSelect => {
  841. let x = pos.x;
  842. self.start_touch_select(x);
  843. self.redraw().await;
  844. d!("TouchStateAction::Select");
  845. self.touch_info.lock().unwrap().state = TouchStateAction::Select;
  846. }
  847. TouchStateAction::DragSelectHandle { side } => {
  848. {
  849. assert!(*side == -1 || *side == 1);
  850. assert!(self.is_phone_select.load(Ordering::Relaxed));
  851. let mut selections = self.select.lock().unwrap();
  852. assert_eq!(selections.len(), 1);
  853. let select = selections.first_mut().unwrap();
  854. let rendered = {
  855. let editable = self.editable.lock().unwrap();
  856. editable.render()
  857. };
  858. let font_size = self.font_size.get();
  859. let window_scale = self.window_scale.get();
  860. let baseline = self.baseline.get();
  861. let pos_x = pos.x + self.scroll.get();
  862. let mut pos = rendered.x_to_pos(pos_x, font_size, window_scale, baseline);
  863. if *side == -1 {
  864. let select_other_pos = &mut select.end;
  865. if pos >= *select_other_pos {
  866. pos = *select_other_pos - 1;
  867. }
  868. select.start = pos;
  869. } else {
  870. let select_other_pos = &mut select.start;
  871. if pos <= *select_other_pos {
  872. pos = *select_other_pos + 1;
  873. }
  874. select.end = pos;
  875. }
  876. }
  877. self.redraw().await;
  878. }
  879. TouchStateAction::ScrollHoriz { start_pos, scroll_start } => {
  880. let x_dist = start_pos.x - pos.x;
  881. let mut scroll = scroll_start + x_dist;
  882. scroll = scroll.clamp(0., self.max_cursor_scroll());
  883. self.scroll.set(atom, scroll);
  884. self.redraw().await;
  885. }
  886. _ => {}
  887. }
  888. true
  889. }
  890. async fn handle_touch_end(&self, pos: Point) -> bool {
  891. t!("handle_touch_end({pos:?})");
  892. let state = self.touch_info.lock().unwrap().stop();
  893. match state {
  894. TouchStateAction::Inactive => return false,
  895. TouchStateAction::Started { pos: _, instant: _ } => {
  896. let rect = self.rect.get();
  897. let font_size = self.font_size.get();
  898. let window_scale = self.window_scale.get();
  899. let baseline = self.baseline.get();
  900. {
  901. let mut editable = self.editable.lock().unwrap();
  902. let rendered = editable.render();
  903. // Adjust with scroll here too
  904. let x = pos.x - rect.x;
  905. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  906. let cidx = rendered.pos_to_idx(cpos);
  907. editable.set_cursor_idx(cidx);
  908. // begin selection
  909. let mut select = self.select.lock().unwrap();
  910. select.clear();
  911. }
  912. self.is_phone_select.store(false, Ordering::Relaxed);
  913. // Reshow cursor (if hidden)
  914. self.hide_cursor.store(false, Ordering::Relaxed);
  915. self.redraw().await;
  916. }
  917. _ => {}
  918. }
  919. window::show_keyboard(true);
  920. true
  921. }
  922. /// Whenever the cursor property is modified this MUST be called
  923. /// to recalculate the scroll x property.
  924. fn apply_cursor_scrolling(&self, atom: &mut PropertyAtomicGuard) {
  925. let rect = self.rect.get();
  926. let cursor_pos = self.cursor_pos.get() as usize;
  927. let cursor_width = self.cursor_width.get();
  928. let mut scroll = self.scroll.get();
  929. let cursor_x = {
  930. let font_size = self.font_size.get();
  931. let window_scale = self.window_scale.get();
  932. let baseline = self.baseline.get();
  933. let glyphs = self.glyphs.lock().unwrap().clone();
  934. let mut glyph_pos_iter =
  935. GlyphPositionIter::new(font_size, window_scale, &glyphs, baseline);
  936. if cursor_pos == 0 {
  937. 0.
  938. } else if cursor_pos == glyphs.len() {
  939. let glyph_pos = glyph_pos_iter.last().unwrap();
  940. let rhs = glyph_pos.rhs() + eol_nudge(font_size, &glyphs);
  941. rhs
  942. } else {
  943. assert!(cursor_pos < glyphs.len());
  944. let glyph_pos = glyph_pos_iter.nth(cursor_pos).expect("glyph pos mismatch glyphs");
  945. glyph_pos.x
  946. }
  947. };
  948. // The LHS and RHS of the cursor box
  949. let cursor_lhs = cursor_x - scroll;
  950. let cursor_rhs = cursor_lhs + cursor_width;
  951. // RHS is outside
  952. if cursor_rhs > rect.w {
  953. // We want a scroll so RHS = w
  954. // cursor_x - scroll + CURSOR_WIDTH = rect.w
  955. scroll = cursor_x + cursor_width - rect.w;
  956. // LHS is negative
  957. } else if cursor_lhs < 0. {
  958. // We want scroll so LHS = 0
  959. // cursor_x - scroll = 0
  960. scroll = cursor_x;
  961. }
  962. self.scroll.set(atom, scroll);
  963. }
  964. fn max_cursor_scroll(&self) -> f32 {
  965. let font_size = self.font_size.get();
  966. let window_scale = self.window_scale.get();
  967. let baseline = self.baseline.get();
  968. let rhs = {
  969. let mut editable = self.editable.lock().unwrap();
  970. let rendered = editable.render();
  971. if rendered.glyphs.is_empty() {
  972. return 0.
  973. }
  974. let glyph_pos_iter =
  975. GlyphPositionIter::new(font_size, window_scale, &rendered.glyphs, baseline);
  976. let last_rect = glyph_pos_iter.last().unwrap();
  977. let mut rhs = last_rect.x + last_rect.w;
  978. rhs += eol_nudge(font_size, &rendered.glyphs);
  979. rhs
  980. };
  981. let rect_w = self.rect.get().w;
  982. if rhs <= rect_w {
  983. return 0.
  984. }
  985. let max_scroll = rhs - rect_w;
  986. max_scroll
  987. }
  988. fn clamp_scroll(&self, atom: &mut PropertyAtomicGuard) {
  989. let mut scroll = self.scroll.get();
  990. scroll = scroll.clamp(0., self.max_cursor_scroll());
  991. self.scroll.set(atom, scroll);
  992. }
  993. fn pause_blinking(&self) {
  994. self.blink_is_paused.store(true, Ordering::Relaxed);
  995. self.cursor_is_visible.store(true, Ordering::Relaxed);
  996. }
  997. async fn redraw(&self) {
  998. let timest = unixtime();
  999. t!("redraw()");
  1000. let parent_rect = self.parent_rect.lock().unwrap().unwrap().clone();
  1001. self.rect.eval(&parent_rect).expect("unable to eval rect");
  1002. let Some(draw_update) = self.make_draw_calls() else {
  1003. error!(target: "ui::editbox", "Text failed to draw");
  1004. return
  1005. };
  1006. self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
  1007. }
  1008. fn redraw_cursor(&self) {
  1009. let timest = unixtime();
  1010. let cursor_instrs = self.get_cursor_instrs();
  1011. let draw_calls = vec![(
  1012. self.cursor_dc_key,
  1013. GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
  1014. )];
  1015. self.render_api.replace_draw_calls(timest, draw_calls);
  1016. }
  1017. fn get_cursor_instrs(&self) -> Vec<GfxDrawInstruction> {
  1018. if !self.is_focused.get() ||
  1019. !self.cursor_is_visible.load(Ordering::Relaxed) ||
  1020. self.hide_cursor.load(Ordering::Relaxed)
  1021. {
  1022. return vec![]
  1023. }
  1024. let mut cursor_instrs = vec![];
  1025. let rect_w = self.rect.get().w;
  1026. let mut cursor_pos = Point::zero();
  1027. cursor_pos.x += self.cursor_px_offset();
  1028. if cursor_pos.x > rect_w {
  1029. return vec![]
  1030. }
  1031. cursor_instrs.push(GfxDrawInstruction::Move(cursor_pos));
  1032. let cursor_mesh = {
  1033. let mut cursor_mesh = self.cursor_mesh.lock().unwrap();
  1034. if cursor_mesh.is_none() {
  1035. *cursor_mesh = Some(self.regen_cursor_mesh());
  1036. }
  1037. cursor_mesh.clone().unwrap()
  1038. };
  1039. cursor_instrs.push(GfxDrawInstruction::Draw(cursor_mesh));
  1040. cursor_instrs
  1041. }
  1042. fn make_draw_calls(&self) -> Option<DrawUpdate> {
  1043. let rect = self.rect.get();
  1044. let text_mesh = self.regen_text_mesh(rect.clone());
  1045. let cursor_instrs = self.get_cursor_instrs();
  1046. Some(DrawUpdate {
  1047. key: self.text_dc_key,
  1048. draw_calls: vec![
  1049. (
  1050. self.text_dc_key,
  1051. GfxDrawCall {
  1052. instrs: vec![
  1053. GfxDrawInstruction::Move(rect.pos()),
  1054. GfxDrawInstruction::Draw(text_mesh),
  1055. ],
  1056. dcs: vec![self.cursor_dc_key],
  1057. z_index: self.z_index.get(),
  1058. },
  1059. ),
  1060. (
  1061. self.cursor_dc_key,
  1062. GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
  1063. ),
  1064. ],
  1065. })
  1066. }
  1067. }
  1068. impl Drop for EditBox {
  1069. fn drop(&mut self) {
  1070. self.render_api
  1071. .replace_draw_calls(unixtime(), vec![(self.text_dc_key, Default::default())]);
  1072. }
  1073. }
  1074. #[async_trait]
  1075. impl UIObject for EditBox {
  1076. fn priority(&self) -> u32 {
  1077. self.priority.get()
  1078. }
  1079. async fn start(self: Arc<Self>, ex: ExecutorPtr) {
  1080. let me = Arc::downgrade(&self);
  1081. let mut on_modify = OnModify::new(ex.clone(), self.node.clone(), me.clone());
  1082. on_modify.when_change(self.is_focused.prop(), Self::change_focus);
  1083. // When text has been changed.
  1084. // Cursor and selection might be invalidated.
  1085. async fn reset(self_: Arc<EditBox>) {
  1086. self_.editable.lock().unwrap().end_compose();
  1087. self_.editable.lock().unwrap().set_text(self_.text.get(), String::new());
  1088. let atom = &mut PropertyAtomicGuard::new();
  1089. self_.cursor_pos.set(atom, 0);
  1090. self_.selected.clone().set_null(atom, Role::Internal, 0).unwrap();
  1091. self_.selected.clone().set_null(atom, Role::Internal, 1).unwrap();
  1092. self_.scroll.set(atom, 0.);
  1093. self_.redraw();
  1094. }
  1095. async fn redraw(self_: Arc<EditBox>) {
  1096. self_.redraw().await;
  1097. }
  1098. on_modify.when_change(self.rect.prop(), redraw);
  1099. on_modify.when_change(self.baseline.prop(), redraw);
  1100. // The commented properties are modified on input events
  1101. // So then redraw() will get repeatedly triggered when these properties
  1102. // are changed. We should find a solution. For now the hooks are disabled.
  1103. //on_modify.when_change(scroll.prop(), redraw);
  1104. //on_modify.when_change(cursor_pos.prop(), redraw);
  1105. on_modify.when_change(self.font_size.prop(), redraw);
  1106. // We must also reshape text
  1107. on_modify.when_change(self.text.prop(), reset);
  1108. on_modify.when_change(self.text_color.prop(), redraw);
  1109. on_modify.when_change(self.hi_bg_color.prop(), redraw);
  1110. //on_modify.when_change(selected.clone(), redraw);
  1111. on_modify.when_change(self.z_index.prop(), redraw);
  1112. on_modify.when_change(self.debug.prop(), redraw);
  1113. async fn regen_cursor(self_: Arc<EditBox>) {
  1114. let mesh = std::mem::take(&mut *self_.cursor_mesh.lock().unwrap());
  1115. }
  1116. on_modify.when_change(self.cursor_color.prop(), regen_cursor);
  1117. on_modify.when_change(self.cursor_ascent.prop(), regen_cursor);
  1118. on_modify.when_change(self.cursor_descent.prop(), regen_cursor);
  1119. on_modify.when_change(self.cursor_width.prop(), regen_cursor);
  1120. let me2 = me.clone();
  1121. let cursor_blink_time = self.cursor_blink_time.clone();
  1122. let cursor_idle_time = self.cursor_idle_time.clone();
  1123. let blinking_cursor_task = ex.spawn(async move {
  1124. loop {
  1125. msleep(cursor_blink_time.get() as u64).await;
  1126. let self_ = me2.upgrade().unwrap();
  1127. if self_.blink_is_paused.swap(false, Ordering::Relaxed) {
  1128. msleep(cursor_idle_time.get() as u64).await;
  1129. continue
  1130. }
  1131. // Invert the bool
  1132. self_.cursor_is_visible.fetch_not(Ordering::Relaxed);
  1133. self_.redraw_cursor();
  1134. }
  1135. });
  1136. let mut tasks = on_modify.tasks;
  1137. tasks.push(blinking_cursor_task);
  1138. self.tasks.set(tasks);
  1139. }
  1140. async fn draw(
  1141. &self,
  1142. parent_rect: Rectangle,
  1143. trace_id: u32,
  1144. atom: &mut PropertyAtomicGuard,
  1145. ) -> Option<DrawUpdate> {
  1146. t!("EditBox::draw() [trace_id={trace_id}]");
  1147. *self.parent_rect.lock().unwrap() = Some(parent_rect);
  1148. self.rect.eval(&parent_rect).ok()?;
  1149. self.clamp_scroll(atom);
  1150. self.make_draw_calls()
  1151. }
  1152. async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) -> bool {
  1153. // First filter for only single digit keys
  1154. if DISALLOWED_CHARS.contains(&key) {
  1155. return false
  1156. }
  1157. if !self.is_focused.get() {
  1158. return false
  1159. }
  1160. let atom = &mut PropertyAtomicGuard::new();
  1161. if mods.ctrl || mods.alt {
  1162. if repeat {
  1163. return false
  1164. }
  1165. self.handle_shortcut(key, &mods, atom).await;
  1166. return true
  1167. }
  1168. let actions = {
  1169. let mut repeater = self.key_repeat.lock().unwrap();
  1170. repeater.key_down(PressedKey::Char(key), repeat)
  1171. };
  1172. t!("Key {key:?} has {actions} actions");
  1173. for _ in 0..actions {
  1174. self.insert_char(key).await;
  1175. }
  1176. true
  1177. }
  1178. async fn handle_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
  1179. // First filter for only single digit keys
  1180. // Avoid processing events handled by insert_char()
  1181. if !ALLOWED_KEYCODES.contains(&key) {
  1182. return false
  1183. }
  1184. if !self.is_focused.get() {
  1185. return false
  1186. }
  1187. let actions = {
  1188. let mut repeater = self.key_repeat.lock().unwrap();
  1189. repeater.key_down(PressedKey::Key(key), repeat)
  1190. };
  1191. let atom = &mut PropertyAtomicGuard::new();
  1192. // Suppress noisy message
  1193. if actions > 0 {
  1194. t!("Key {key:?} has {actions} actions");
  1195. }
  1196. for _ in 0..actions {
  1197. self.handle_key(&key, &mods, atom).await;
  1198. }
  1199. true
  1200. }
  1201. async fn handle_mouse_btn_down(&self, btn: MouseButton, mouse_pos: Point) -> bool {
  1202. if !self.is_active.get() {
  1203. return false
  1204. }
  1205. if btn != MouseButton::Left {
  1206. return false
  1207. }
  1208. let rect = self.rect.get();
  1209. let atom = &mut PropertyAtomicGuard::new();
  1210. // clicking inside box will:
  1211. // 1. make it active
  1212. // 2. begin selection
  1213. if !rect.contains(mouse_pos) {
  1214. if self.is_focused.get() {
  1215. d!("EditBox unfocused");
  1216. self.is_focused.set(atom, false);
  1217. self.select.lock().unwrap().clear();
  1218. self.redraw().await;
  1219. }
  1220. return false
  1221. }
  1222. if self.is_focused.get() {
  1223. d!("EditBox clicked");
  1224. } else {
  1225. d!("EditBox focused");
  1226. self.is_focused.set(atom, true);
  1227. }
  1228. let font_size = self.font_size.get();
  1229. let window_scale = self.window_scale.get();
  1230. let baseline = self.baseline.get();
  1231. {
  1232. let mut editable = self.editable.lock().unwrap();
  1233. let rendered = editable.render();
  1234. let x = mouse_pos.x - rect.x + self.scroll.get();
  1235. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  1236. let cidx = rendered.pos_to_idx(cpos);
  1237. editable.set_cursor_idx(cidx);
  1238. // begin selection
  1239. let mut select = self.select.lock().unwrap();
  1240. select.clear();
  1241. select.push(Selection::new(cpos, cpos));
  1242. self.mouse_btn_held.store(true, Ordering::Relaxed);
  1243. }
  1244. self.pause_blinking();
  1245. self.redraw().await;
  1246. true
  1247. }
  1248. async fn handle_mouse_btn_up(&self, btn: MouseButton, mouse_pos: Point) -> bool {
  1249. if !self.is_active.get() {
  1250. return false
  1251. }
  1252. // releasing mouse button will end selection
  1253. self.mouse_btn_held.store(false, Ordering::Relaxed);
  1254. false
  1255. }
  1256. async fn handle_mouse_move(&self, mouse_pos: Point) -> bool {
  1257. if !self.is_active.get() {
  1258. return false
  1259. }
  1260. let rect = self.rect.get();
  1261. self.is_mouse_hover.store(rect.contains(mouse_pos), Ordering::Relaxed);
  1262. if !self.mouse_btn_held.load(Ordering::Relaxed) {
  1263. return false
  1264. }
  1265. // if active and selection_active, then use x to modify the selection.
  1266. // also implement scrolling when cursor is to the left or right
  1267. // just scroll to the end
  1268. // also set cursor_pos too
  1269. let font_size = self.font_size.get();
  1270. let window_scale = self.window_scale.get();
  1271. let baseline = self.baseline.get();
  1272. {
  1273. let mut editable = self.editable.lock().unwrap();
  1274. let rendered = editable.render();
  1275. let x = mouse_pos.x - rect.x + self.scroll.get();
  1276. let cpos = rendered.x_to_pos(x, font_size, window_scale, baseline);
  1277. let cidx = rendered.pos_to_idx(cpos);
  1278. editable.set_cursor_idx(cidx);
  1279. // begin selection
  1280. let mut select = self.select.lock().unwrap();
  1281. select.last_mut().unwrap().end = cpos;
  1282. }
  1283. self.pause_blinking();
  1284. //self.apply_cursor_scrolling();
  1285. self.redraw().await;
  1286. true
  1287. }
  1288. async fn handle_mouse_wheel(&self, wheel_pos: Point) -> bool {
  1289. if !self.is_mouse_hover.load(Ordering::Relaxed) {
  1290. return false
  1291. }
  1292. let atom = &mut PropertyAtomicGuard::new();
  1293. let mut scroll = self.scroll.get() + wheel_pos.y * self.scroll_speed.get();
  1294. scroll = scroll.clamp(0., self.max_cursor_scroll());
  1295. t!("handle_mouse_wheel({wheel_pos:?}) [scroll={scroll}]");
  1296. self.scroll.set(atom, scroll);
  1297. self.redraw().await;
  1298. true
  1299. }
  1300. async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_pos: Point) -> bool {
  1301. if !self.is_active.get() {
  1302. return false
  1303. }
  1304. // Ignore multi-touch
  1305. if id != 0 {
  1306. return false
  1307. }
  1308. match phase {
  1309. TouchPhase::Started => self.handle_touch_start(touch_pos).await,
  1310. TouchPhase::Moved => self.handle_touch_move(touch_pos).await,
  1311. TouchPhase::Ended => self.handle_touch_end(touch_pos).await,
  1312. TouchPhase::Cancelled => false,
  1313. }
  1314. }
  1315. /*
  1316. async fn handle_compose_text(
  1317. &self,
  1318. suggest_text: &str,
  1319. cursor_pos: i32,
  1320. is_commit: bool,
  1321. ) -> bool {
  1322. t!("handle_compose_text({suggest_text}, {is_commit})");
  1323. if !self.is_active.get() {
  1324. return false
  1325. }
  1326. {
  1327. let mut editable = self.editable.lock().unwrap();
  1328. editable.compose(suggest_text, is_commit);
  1329. }
  1330. let atom = &mut PropertyAtomicGuard::new();
  1331. //self.apply_cursor_scrolling();
  1332. self.clamp_scroll(atom);
  1333. self.redraw().await;
  1334. true
  1335. }
  1336. async fn handle_set_compose_region(&self, start: usize, end: usize) -> bool {
  1337. t!("handle_set_compose_region({start}, {end})");
  1338. if !self.is_active.get() {
  1339. return false
  1340. }
  1341. {
  1342. let mut editable = self.editable.lock().unwrap();
  1343. editable.set_compose_region(start, end);
  1344. }
  1345. self.redraw().await;
  1346. true
  1347. }
  1348. */
  1349. }
  1350. /// Filter these char events from being handled since we handle them
  1351. /// using the key_up/key_down events.
  1352. /// Avoids duplicate processing of keyboard input events.
  1353. pub static DISALLOWED_CHARS: &'static [char] = &['\r', '\u{8}', '\u{7f}', '\t', '\n'];
  1354. /// These keycodes are handled via normal key_up/key_down events.
  1355. /// Anything in this list must be disallowed char events.
  1356. pub static ALLOWED_KEYCODES: &'static [KeyCode] = &[
  1357. KeyCode::Left,
  1358. KeyCode::Right,
  1359. KeyCode::Up,
  1360. KeyCode::Down,
  1361. KeyCode::Enter,
  1362. KeyCode::Kp0,
  1363. KeyCode::Kp1,
  1364. KeyCode::Kp2,
  1365. KeyCode::Kp3,
  1366. KeyCode::Kp4,
  1367. KeyCode::Kp5,
  1368. KeyCode::Kp6,
  1369. KeyCode::Kp7,
  1370. KeyCode::Kp8,
  1371. KeyCode::Kp9,
  1372. KeyCode::KpDecimal,
  1373. KeyCode::KpEnter,
  1374. KeyCode::Delete,
  1375. KeyCode::Backspace,
  1376. KeyCode::Home,
  1377. KeyCode::End,
  1378. ];