mod.rs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use async_lock::Mutex as AsyncMutex;
  19. use async_trait::async_trait;
  20. use atomic_float::AtomicF32;
  21. use chrono::{Local, TimeZone};
  22. use darkfi::system::{msleep, CondVar};
  23. use darkfi_serial::{deserialize, Decodable, Encodable, SerialDecodable, SerialEncodable};
  24. use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
  25. use rand::{rngs::OsRng, Rng};
  26. use sled_overlay::sled;
  27. use std::{
  28. collections::VecDeque,
  29. hash::{DefaultHasher, Hash, Hasher},
  30. io::Cursor,
  31. sync::{
  32. atomic::{AtomicBool, Ordering},
  33. Arc, Mutex as SyncMutex, Weak,
  34. },
  35. };
  36. mod page;
  37. use page::{FreedData, MessageBuffer};
  38. use crate::{
  39. gfx::{
  40. GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GraphicsEventPublisherPtr, Point, Rectangle,
  41. RenderApi, RenderApiPtr,
  42. },
  43. mesh::{Color, MeshBuilder, COLOR_BLUE, COLOR_GREEN},
  44. prop::{
  45. PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyRect, PropertyUint32,
  46. Role,
  47. },
  48. pubsub::Subscription,
  49. scene::{Pimpl, SceneNodeWeak},
  50. text::{self, Glyph, GlyphPositionIter, TextShaperPtr},
  51. util::{enumerate, is_whitespace},
  52. ExecutorPtr,
  53. };
  54. use super::{DrawUpdate, OnModify, UIObject};
  55. const EPSILON: f32 = 0.001;
  56. const BIG_EPSILON: f32 = 0.05;
  57. fn is_zero(x: f32) -> bool {
  58. x.abs() < EPSILON
  59. }
  60. /// std::cmp::max() doesn't work on f32
  61. fn max(a: f32, b: f32) -> f32 {
  62. if a > b {
  63. a
  64. } else {
  65. b
  66. }
  67. }
  68. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
  69. pub struct ChatMsg {
  70. pub nick: String,
  71. pub text: String,
  72. }
  73. type Timestamp = u64;
  74. type MessageId = [u8; 32];
  75. const PRELOAD_PAGES: usize = 1;
  76. #[derive(Clone)]
  77. struct TouchInfo {
  78. start_scroll: f32,
  79. start_y: f32,
  80. start_instant: std::time::Instant,
  81. /// Used for flick scrolling
  82. samples: VecDeque<(std::time::Instant, f32)>,
  83. last_instant: std::time::Instant,
  84. last_y: f32,
  85. /// Selection started?
  86. is_select_mode: Option<bool>,
  87. }
  88. impl TouchInfo {
  89. fn new(start_scroll: f32, y: f32) -> Self {
  90. Self {
  91. start_scroll,
  92. start_y: y,
  93. start_instant: std::time::Instant::now(),
  94. samples: VecDeque::from([(std::time::Instant::now(), y)]),
  95. last_instant: std::time::Instant::now(),
  96. last_y: y,
  97. is_select_mode: None,
  98. }
  99. }
  100. fn push_sample(&mut self, y: f32) {
  101. self.samples.push_back((std::time::Instant::now(), y));
  102. // Now drop all old samples older than 40ms
  103. while let Some((instant, _)) = self.samples.front() {
  104. if instant.elapsed().as_millis_f32() <= 40. {
  105. break
  106. }
  107. self.samples.pop_front().unwrap();
  108. }
  109. }
  110. fn first_sample(&self) -> Option<(f32, f32)> {
  111. self.samples.front().map(|(t, s)| (t.elapsed().as_millis_f32(), *s))
  112. }
  113. }
  114. pub type ChatViewPtr = Arc<ChatView>;
  115. pub struct ChatView {
  116. node: SceneNodeWeak,
  117. #[allow(dead_code)]
  118. tasks: Vec<smol::Task<()>>,
  119. render_api: RenderApiPtr,
  120. text_shaper: TextShaperPtr,
  121. tree: sled::Tree,
  122. msgbuf: AsyncMutex<MessageBuffer>,
  123. dc_key: u64,
  124. /// Used for detecting when scrolling view
  125. mouse_pos: SyncMutex<Point>,
  126. /// Touch scrolling
  127. touch_info: SyncMutex<Option<TouchInfo>>,
  128. touch_is_active: AtomicBool,
  129. rect: PropertyRect,
  130. scroll: PropertyFloat32,
  131. font_size: PropertyFloat32,
  132. line_height: PropertyFloat32,
  133. baseline: PropertyFloat32,
  134. timestamp_color: PropertyColor,
  135. text_color: PropertyColor,
  136. nick_colors: PropertyPtr,
  137. hi_bg_color: PropertyColor,
  138. z_index: PropertyUint32,
  139. debug: PropertyBool,
  140. scroll_start_accel: PropertyFloat32,
  141. scroll_resist: PropertyFloat32,
  142. select_hold_time: PropertyFloat32,
  143. /// Scroll accel
  144. motion_cv: Arc<CondVar>,
  145. speed: AtomicF32,
  146. mouse_btn_held: AtomicBool,
  147. /// Triggers the background loading task to wake up
  148. bgload_cv: Arc<CondVar>,
  149. /// We use it when we re-eval rect when its changed via property.
  150. parent_rect: SyncMutex<Option<Rectangle>>,
  151. }
  152. impl ChatView {
  153. pub async fn new(
  154. node: SceneNodeWeak,
  155. tree: sled::Tree,
  156. window_scale: PropertyFloat32,
  157. render_api: RenderApiPtr,
  158. text_shaper: TextShaperPtr,
  159. ex: ExecutorPtr,
  160. ) -> Pimpl {
  161. debug!(target: "ui::chatview", "ChatView::new()");
  162. let node_ref = &node.upgrade().unwrap();
  163. let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap();
  164. let scroll = PropertyFloat32::wrap(node_ref, Role::Internal, "scroll", 0).unwrap();
  165. let font_size = PropertyFloat32::wrap(node_ref, Role::Internal, "font_size", 0).unwrap();
  166. let line_height =
  167. PropertyFloat32::wrap(node_ref, Role::Internal, "line_height", 0).unwrap();
  168. let baseline = PropertyFloat32::wrap(node_ref, Role::Internal, "baseline", 0).unwrap();
  169. let timestamp_color =
  170. PropertyColor::wrap(node_ref, Role::Internal, "timestamp_color").unwrap();
  171. let text_color = PropertyColor::wrap(node_ref, Role::Internal, "text_color").unwrap();
  172. let nick_colors = node_ref.get_property("nick_colors").expect("ChatView::nick_colors");
  173. let hi_bg_color = PropertyColor::wrap(node_ref, Role::Internal, "hi_bg_color").unwrap();
  174. let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
  175. let debug = PropertyBool::wrap(node_ref, Role::Internal, "debug", 0).unwrap();
  176. let scroll_start_accel =
  177. PropertyFloat32::wrap(node_ref, Role::Internal, "scroll_start_accel", 0).unwrap();
  178. let scroll_resist =
  179. PropertyFloat32::wrap(node_ref, Role::Internal, "scroll_resist", 0).unwrap();
  180. let select_hold_time =
  181. PropertyFloat32::wrap(node_ref, Role::Internal, "select_hold_time", 0).unwrap();
  182. let node_name = node_ref.name.clone();
  183. let node_id = node_ref.id;
  184. let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
  185. /*
  186. let me2 = me.clone();
  187. let insert_line_method_task =
  188. ex.spawn(
  189. async move { while Self::process_insert_line_method(&me2, &recvr).await {} },
  190. );
  191. */
  192. let me2 = me.clone();
  193. let motion_cv = Arc::new(CondVar::new());
  194. let cv = motion_cv.clone();
  195. let motion_task = ex.spawn(async move {
  196. loop {
  197. cv.wait().await;
  198. let Some(self_) = me2.upgrade() else {
  199. // Should not happen
  200. panic!("self destroyed before motion_task was stopped!");
  201. };
  202. self_.handle_movement().await;
  203. cv.reset();
  204. }
  205. });
  206. let me2 = me.clone();
  207. let bgload_cv = Arc::new(CondVar::new());
  208. let cv = bgload_cv.clone();
  209. let bgload_task = ex.spawn(async move {
  210. loop {
  211. cv.wait().await;
  212. let Some(self_) = me2.upgrade() else {
  213. // Should not happen
  214. panic!("self destroyed before bgload_task was stopped!");
  215. };
  216. self_.handle_bgload().await;
  217. cv.reset();
  218. }
  219. });
  220. let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
  221. async fn reload_view(self_: Arc<ChatView>) {
  222. self_.scrollview(self_.scroll.get()).await;
  223. }
  224. on_modify.when_change(scroll.prop(), reload_view);
  225. //async fn redraw(self_: Arc<ChatView>) {
  226. // self_.redraw().await;
  227. //}
  228. //on_modify.when_change(rect.clone(), redraw);
  229. //on_modify.when_change(debug.prop(), redraw);
  230. let mut tasks = vec![/*insert_line_method_task,*/ motion_task, bgload_task];
  231. tasks.append(&mut on_modify.tasks);
  232. Self {
  233. node,
  234. tasks,
  235. render_api: render_api.clone(),
  236. text_shaper: text_shaper.clone(),
  237. tree,
  238. msgbuf: AsyncMutex::new(MessageBuffer::new(
  239. font_size.clone(),
  240. line_height.clone(),
  241. baseline.clone(),
  242. timestamp_color.clone(),
  243. text_color.clone(),
  244. nick_colors.clone(),
  245. hi_bg_color.clone(),
  246. debug.clone(),
  247. window_scale,
  248. render_api,
  249. text_shaper,
  250. )),
  251. dc_key: OsRng.gen(),
  252. mouse_pos: SyncMutex::new(Point::from([0., 0.])),
  253. touch_info: SyncMutex::new(None),
  254. touch_is_active: AtomicBool::new(false),
  255. rect,
  256. scroll,
  257. font_size,
  258. line_height,
  259. baseline,
  260. timestamp_color,
  261. text_color,
  262. nick_colors,
  263. hi_bg_color,
  264. z_index,
  265. debug,
  266. scroll_start_accel,
  267. scroll_resist,
  268. select_hold_time,
  269. motion_cv,
  270. speed: AtomicF32::new(0.),
  271. mouse_btn_held: AtomicBool::new(false),
  272. bgload_cv,
  273. parent_rect: SyncMutex::new(None),
  274. }
  275. });
  276. Pimpl::ChatView(self_)
  277. }
  278. async fn process_insert_line_method(
  279. me: &Weak<Self>,
  280. recvr: &async_channel::Receiver<Vec<u8>>,
  281. ) -> bool {
  282. let Ok(data) = recvr.recv().await else {
  283. debug!(target: "ui::chatview", "Event relayer closed");
  284. return false
  285. };
  286. fn decode_data(data: &[u8]) -> std::io::Result<(Timestamp, MessageId, String, String)> {
  287. let mut cur = Cursor::new(&data);
  288. let timestamp = Timestamp::decode(&mut cur)?;
  289. let message_id = MessageId::decode(&mut cur)?;
  290. let nick = String::decode(&mut cur)?;
  291. let text = String::decode(&mut cur)?;
  292. Ok((timestamp, message_id, nick, text))
  293. }
  294. let Ok((timestamp, message_id, nick, text)) = decode_data(&data) else {
  295. error!(target: "ui::chatview", "insert_line() method invalid arg data");
  296. return true
  297. };
  298. let Some(self_) = me.upgrade() else {
  299. // Should not happen
  300. panic!("self destroyed before touch_task was stopped!");
  301. };
  302. self_.handle_insert_line(timestamp, message_id, nick, text).await;
  303. true
  304. }
  305. /// Mark line as selected
  306. async fn select_line(&self, mut y: f32) {
  307. // The cursor is inside the rect. We just have to find which line it clicked.
  308. let scroll = self.scroll.get();
  309. let rect = self.rect.get();
  310. let bottom = scroll + rect.y + rect.h;
  311. assert!(bottom >= y);
  312. y = bottom - y;
  313. let mut msgbuf = self.msgbuf.lock().await;
  314. msgbuf.select_line(y).await;
  315. self.redraw_cached(&mut msgbuf).await;
  316. }
  317. fn end_touch_phase(&self, touch_y: f32) {
  318. // Now calculate scroll acceleration
  319. let touch_info = std::mem::replace(&mut *self.touch_info.lock().unwrap(), None);
  320. let Some(touch_info) = &touch_info else { return };
  321. self.touch_is_active.store(false, Ordering::Relaxed);
  322. // No scroll accel with selection mode
  323. if touch_info.is_select_mode.is_some() {
  324. return
  325. }
  326. let Some((time, sample_y)) = touch_info.first_sample() else { return };
  327. let dist = touch_y - sample_y;
  328. // Ignore sub-ms events
  329. if time < 1. {
  330. error!(target: "ui::chatview", "Received a sub-ms touch event!");
  331. return
  332. }
  333. //let speed = dist / time;
  334. //self.speed.fetch_add(speed, Ordering::Relaxed);
  335. //debug!(target: "ui::chatview", "speed = {dist} / {time} = {speed}");
  336. let accel = self.scroll_start_accel.get() * dist / time;
  337. let touch_time = touch_info.start_instant.elapsed();
  338. debug!(target: "ui::chatview", "accel = {dist} / {time} = {accel}, touch = {touch_time:?}");
  339. self.speed.fetch_add(accel, Ordering::Relaxed);
  340. self.motion_cv.notify();
  341. }
  342. async fn add_line_to_db(
  343. &self,
  344. timest: Timestamp,
  345. message_id: &MessageId,
  346. nick: &str,
  347. text: &str,
  348. ) -> bool {
  349. let timest = timest.to_be_bytes();
  350. assert_eq!(timest.len(), 8);
  351. let mut key = [0u8; 8 + 32];
  352. key[..8].clone_from_slice(&timest);
  353. key[8..].clone_from_slice(message_id);
  354. // When does this return Err?
  355. let contains_key = self.tree.contains_key(&key);
  356. if contains_key.is_err() || contains_key.unwrap() {
  357. // Already exists
  358. return false
  359. }
  360. let msg = ChatMsg { nick: nick.to_string(), text: text.to_string() };
  361. let mut val = vec![];
  362. msg.encode(&mut val).unwrap();
  363. self.tree.insert(&key, val).unwrap();
  364. let _ = self.tree.flush_async().await;
  365. true
  366. }
  367. async fn handle_insert_line(
  368. &self,
  369. timest: Timestamp,
  370. message_id: MessageId,
  371. nick: String,
  372. text: String,
  373. ) {
  374. debug!(target: "ui::chatview", "handle_insert_line({timest}, {message_id:?}, {nick}, {text})");
  375. if !self.add_line_to_db(timest, &message_id, &nick, &text).await {
  376. // Already exists so bail
  377. debug!(target: "ui::chatview", "duplicate msg so bailing");
  378. return
  379. }
  380. // Add message to page
  381. let mut msgbuf = self.msgbuf.lock().await;
  382. msgbuf.insert_privmsg(timest, message_id, nick, text).await;
  383. self.redraw_cached(&mut msgbuf).await;
  384. self.bgload_cv.notify();
  385. }
  386. async fn handle_movement(&self) {
  387. // We need to fix this impl because it depends very much on the speed of the device
  388. // that it's running on.
  389. // Look into optimizing scrollview() so scrolling is smooth.
  390. // We could use skiplists to avoid looping from the very bottom.
  391. // So index 1 in the skiplist advances to 100px up... (or however much multiplier)
  392. loop {
  393. msleep(10).await;
  394. if self.touch_is_active.load(Ordering::Relaxed) {
  395. return
  396. }
  397. let mut speed = self.speed.load(Ordering::Relaxed);
  398. // Apply constant decel to speed
  399. speed *= self.scroll_resist.get();
  400. if speed.abs() < BIG_EPSILON {
  401. speed = 0.;
  402. }
  403. self.speed.store(speed, Ordering::Relaxed);
  404. // Finished
  405. if is_zero(speed) {
  406. return
  407. }
  408. let scroll = self.scroll.get() + speed;
  409. let dist = self.scrollview(scroll).await;
  410. // We reached the end so just stop
  411. if is_zero(dist) {
  412. self.speed.store(0., Ordering::Relaxed);
  413. return
  414. }
  415. }
  416. }
  417. async fn handle_bgload(&self) {
  418. //debug!(target: "ui::chatview", "ChatView::handle_bgload()");
  419. // Do we need to load some more?
  420. let scroll = self.scroll.get();
  421. let rect = self.rect.get();
  422. let top = scroll + rect.h;
  423. let preload_height = PRELOAD_PAGES as f32 * rect.h;
  424. let mut msgbuf = self.msgbuf.lock().await;
  425. let total_height = msgbuf.calc_total_height().await;
  426. if total_height > top + preload_height {
  427. // Nothing to do here
  428. //debug!(target: "ui::chatview", "bgloader: buffer is sufficient");
  429. return
  430. }
  431. // Keep loading until this is below 0
  432. let mut remaining_load_height = top + preload_height - total_height;
  433. //debug!(target: "ui::chatview", "bgloader: remaining px = {remaining_load_height}");
  434. let mut remaining_visible = top - total_height;
  435. // Get the current earliest timestamp
  436. let iter = match msgbuf.oldest_timestamp() {
  437. Some(oldest_timest) => {
  438. // iterate from there
  439. //debug!(target: "ui::chatview", "preloading from {oldest_timest}");
  440. let timest = (oldest_timest - 1).to_be_bytes();
  441. let mut key = [0u8; 8 + 32];
  442. key[..8].clone_from_slice(&timest);
  443. let iter = self.tree.range(..key).rev();
  444. iter
  445. }
  446. None => {
  447. //debug!(target: "ui::chatview", "initial load");
  448. self.tree.iter().rev()
  449. }
  450. };
  451. for entry in iter {
  452. let Ok((k, v)) = entry else { break };
  453. assert_eq!(k.len(), 8 + 32);
  454. let timest_bytes: [u8; 8] = k[..8].try_into().unwrap();
  455. let message_id: MessageId = k[8..].try_into().unwrap();
  456. let timest = Timestamp::from_be_bytes(timest_bytes);
  457. let chatmsg: ChatMsg = deserialize(&v).unwrap();
  458. debug!(target: "ui::chatview", "{timest:?} {chatmsg:?}");
  459. let msg_height =
  460. msgbuf.push_privmsg(timest, message_id, chatmsg.nick, chatmsg.text).await;
  461. remaining_load_height -= msg_height;
  462. if remaining_load_height <= 0. {
  463. break
  464. }
  465. if remaining_visible > 0. {
  466. self.redraw_cached(&mut msgbuf).await;
  467. }
  468. remaining_visible -= msg_height;
  469. }
  470. }
  471. /// Descent = line height - baseline
  472. fn descent(&self) -> f32 {
  473. self.line_height.get() - self.baseline.get()
  474. }
  475. async fn scrollview(&self, mut scroll: f32) -> f32 {
  476. //debug!(target: "ui::chatview", "scrollview()");
  477. let old_scroll = self.scroll.get();
  478. let rect = self.rect.get();
  479. let mut msgbuf = self.msgbuf.lock().await;
  480. // 1/3 of time spent here ~1.5ms
  481. if let Some(new_scroll) = self.adjust_scroll(&mut msgbuf, scroll, rect.h).await {
  482. scroll = new_scroll;
  483. }
  484. // 2/3 of time spent here ~3.3ms
  485. self.redraw_cached(&mut msgbuf).await;
  486. self.scroll.set(scroll);
  487. self.bgload_cv.notify();
  488. scroll - old_scroll
  489. }
  490. /// Adjusts a proposed scroll value to clamp it within range. It will load pages until we
  491. /// either run out or we have enough, then checks scroll is within range.
  492. /// Returns None if the value is within range.
  493. async fn adjust_scroll(
  494. &self,
  495. msgbuf: &mut MessageBuffer,
  496. mut scroll: f32,
  497. rect_h: f32,
  498. ) -> Option<f32> {
  499. // We still wish to preload pages to fill the screen, so we just adjust it up to 0.
  500. let nonneg_scroll = max(scroll, 0.);
  501. if scroll < 0. {
  502. return Some(0.)
  503. }
  504. let total_height = msgbuf.calc_total_height().await;
  505. let max_allowed_scroll = if total_height > rect_h { total_height - rect_h } else { 0. };
  506. if scroll > max_allowed_scroll {
  507. scroll = max_allowed_scroll;
  508. assert!(scroll >= 0.);
  509. return Some(scroll)
  510. }
  511. // Unchanged
  512. None
  513. }
  514. /// Returns draw calls for drawing
  515. async fn get_meshes(
  516. &self,
  517. msgbuf: &mut MessageBuffer,
  518. rect: &Rectangle,
  519. ) -> (Vec<GfxDrawInstruction>, FreedData) {
  520. let scroll = self.scroll.get();
  521. let total_height = msgbuf.calc_total_height().await;
  522. // Use this to start from the top
  523. //let start_pos = if total_height < rect.h { total_height } else { rect.h };
  524. // We start from the bottom though
  525. let start_pos = rect.h;
  526. let mut instrs = vec![];
  527. //let mut old_drawmesh = vec![];
  528. let meshes = msgbuf.gen_meshes(rect, scroll).await;
  529. for (i, (y_pos, mesh)) in enumerate(meshes) {
  530. // Apply scroll and scissor
  531. // We use the scissor for scrolling
  532. // Because we use the scissor, our actual rect is now rect instead of parent_rect
  533. let off_x = 0.;
  534. // This calc decides whether scroll is in terms of pages or pixels
  535. let off_y = (scroll + start_pos - y_pos);
  536. let pos = Point::from([0., off_y]);
  537. instrs.push(GfxDrawInstruction::Move(pos));
  538. instrs.push(GfxDrawInstruction::Draw(mesh));
  539. }
  540. let freed = std::mem::take(&mut msgbuf.freed);
  541. (instrs, freed)
  542. }
  543. async fn redraw_cached(&self, msgbuf: &mut MessageBuffer) {
  544. let rect = self.rect.get();
  545. let (mut mesh_instrs, freed) = self.get_meshes(msgbuf, &rect).await;
  546. let mut instrs = vec![GfxDrawInstruction::ApplyView(rect)];
  547. instrs.append(&mut mesh_instrs);
  548. let draw_calls =
  549. vec![(self.dc_key, GfxDrawCall { instrs, dcs: vec![], z_index: self.z_index.get() })];
  550. self.render_api.replace_draw_calls(draw_calls);
  551. for buffer_id in freed.buffers {
  552. self.render_api.delete_buffer(buffer_id);
  553. }
  554. for texture_id in freed.textures {
  555. self.render_api.delete_texture(texture_id);
  556. }
  557. }
  558. /// Invalidates cache and redraws everything
  559. async fn redraw_all(&self) {
  560. debug!(target: "ui::chatview", "redraw()");
  561. // ... todo fin
  562. }
  563. }
  564. #[async_trait]
  565. impl UIObject for ChatView {
  566. fn z_index(&self) -> u32 {
  567. self.z_index.get()
  568. }
  569. async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
  570. debug!(target: "ui::chatview", "ChatView::draw()");
  571. *self.parent_rect.lock().unwrap() = Some(parent_rect.clone());
  572. self.rect.eval(&parent_rect).ok()?;
  573. let rect = self.rect.get();
  574. let mut msgbuf = self.msgbuf.lock().await;
  575. msgbuf.adjust_window_scale().await;
  576. msgbuf.adjust_width(rect.w);
  577. msgbuf.clear_meshes();
  578. let mut scroll = self.scroll.get();
  579. if let Some(scroll) = self.adjust_scroll(&mut msgbuf, scroll, rect.h).await {
  580. self.scroll.set(scroll);
  581. }
  582. // We may need to load more messages since the screen size has changed.
  583. // Now we have updated all the values so it's safe to wake up here.
  584. self.bgload_cv.notify();
  585. let (mut mesh_instrs, freed) = self.get_meshes(&mut msgbuf, &rect).await;
  586. drop(msgbuf);
  587. let mut instrs = vec![GfxDrawInstruction::ApplyView(rect)];
  588. instrs.append(&mut mesh_instrs);
  589. Some(DrawUpdate {
  590. key: self.dc_key,
  591. draw_calls: vec![(
  592. self.dc_key,
  593. GfxDrawCall { instrs, dcs: vec![], z_index: self.z_index.get() },
  594. )],
  595. freed_textures: freed.textures,
  596. freed_buffers: freed.buffers,
  597. })
  598. }
  599. async fn handle_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
  600. if repeat {
  601. return false
  602. }
  603. match key {
  604. KeyCode::PageUp => {
  605. let scroll = self.scroll.get() + 200.;
  606. self.scrollview(scroll).await;
  607. }
  608. KeyCode::PageDown => {
  609. let scroll = self.scroll.get() - 200.;
  610. self.scrollview(scroll).await;
  611. }
  612. _ => {}
  613. }
  614. true
  615. }
  616. async fn handle_mouse_btn_down(&self, btn: MouseButton, mouse_pos: Point) -> bool {
  617. if btn != MouseButton::Left {
  618. return false
  619. }
  620. let rect = self.rect.get();
  621. if !rect.contains(mouse_pos) {
  622. return false
  623. }
  624. self.select_line(mouse_pos.y).await;
  625. self.mouse_btn_held.store(true, Ordering::Relaxed);
  626. true
  627. }
  628. async fn handle_mouse_btn_up(&self, btn: MouseButton, mouse_pos: Point) -> bool {
  629. if btn != MouseButton::Left {
  630. return false
  631. }
  632. self.mouse_btn_held.store(false, Ordering::Relaxed);
  633. false
  634. }
  635. async fn handle_mouse_move(&self, mouse_pos: Point) -> bool {
  636. //debug!(target: "ui::chatview", "handle_mouse_move({mouse_x}, {mouse_y})");
  637. // We store the mouse pos for use in handle_mouse_wheel()
  638. *self.mouse_pos.lock().unwrap() = mouse_pos.clone();
  639. if !self.mouse_btn_held.load(Ordering::Relaxed) {
  640. return false
  641. }
  642. let rect = self.rect.get();
  643. if !rect.contains(mouse_pos) {
  644. return false
  645. }
  646. self.select_line(mouse_pos.y).await;
  647. false
  648. }
  649. async fn handle_mouse_wheel(&self, wheel_pos: Point) -> bool {
  650. //debug!(target: "ui::chatview", "handle_mouse_wheel({wheel_x}, {wheel_y})");
  651. let rect = self.rect.get();
  652. let mouse_pos = self.mouse_pos.lock().unwrap().clone();
  653. if !rect.contains(mouse_pos) {
  654. //debug!(target: "ui::chatview", "not inside rect");
  655. return false
  656. }
  657. self.speed.fetch_add(wheel_pos.y * self.scroll_start_accel.get(), Ordering::Relaxed);
  658. self.motion_cv.notify();
  659. true
  660. }
  661. async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_pos: Point) -> bool {
  662. // Ignore multi-touch
  663. if id != 0 {
  664. return false
  665. }
  666. let rect = self.rect.get();
  667. //debug!(target: "ui::chatview", "handle_touch({phase:?}, {touch_x}, {touch_y})");
  668. let touch_y = touch_pos.y;
  669. if !rect.contains(touch_pos) {
  670. match phase {
  671. TouchPhase::Started => *self.touch_info.lock().unwrap() = None,
  672. _ => self.end_touch_phase(touch_y),
  673. }
  674. return false
  675. }
  676. let select_hold_time = self.select_hold_time.get();
  677. // Simulate mouse events
  678. match phase {
  679. TouchPhase::Started => {
  680. self.touch_is_active.store(true, Ordering::Relaxed);
  681. let mut touch_info = self.touch_info.lock().unwrap();
  682. *touch_info = Some(TouchInfo::new(self.scroll.get(), touch_y));
  683. }
  684. TouchPhase::Moved => {
  685. let (start_scroll, start_y, start_elapsed, do_update, is_select_mode) = {
  686. let mut touch_info = self.touch_info.lock().unwrap();
  687. let Some(touch_info) = &mut *touch_info else { return false };
  688. touch_info.last_y = touch_y;
  689. let start_scroll = touch_info.start_scroll;
  690. let start_y = touch_info.start_y;
  691. let start_elapsed = touch_info.start_instant.elapsed().as_millis_f32();
  692. if start_elapsed > select_hold_time && touch_info.is_select_mode.is_none() {
  693. // Did we move?
  694. if (touch_y - start_y).abs() < BIG_EPSILON {
  695. touch_info.is_select_mode = Some(true);
  696. } else {
  697. touch_info.is_select_mode = Some(false);
  698. }
  699. }
  700. let is_select_mode = touch_info.is_select_mode.clone();
  701. touch_info.push_sample(touch_y);
  702. // Only update screen every 20ms. Avoid wasting cycles.
  703. let last_elapsed = touch_info.last_instant.elapsed().as_millis_f32();
  704. let do_update = last_elapsed > 20.;
  705. if do_update {
  706. touch_info.last_instant = std::time::Instant::now();
  707. }
  708. (start_scroll, start_y, start_elapsed, do_update, is_select_mode)
  709. };
  710. debug!(target: "ui::chatview", "touch phase moved, is_select_mode={is_select_mode:?}");
  711. // When scrolling if we suddenly grab the screen for more than a brief period
  712. // of time then stop the scrolling completely.
  713. if start_elapsed > 200. {
  714. //debug!(target: "ui::chatview", "Stopping scroll accel");
  715. self.speed.store(0., Ordering::Relaxed);
  716. }
  717. // Only update every so often to prevent wasting resources.
  718. if !do_update {
  719. return true
  720. }
  721. // We are in selection mode so don't scroll the screen until touch phase ends.
  722. if let Some(is_select_mode) = is_select_mode &&
  723. is_select_mode
  724. {
  725. self.select_line(touch_y).await;
  726. return true
  727. }
  728. let dist = touch_y - start_y;
  729. // No movement so just return
  730. if dist.abs() < BIG_EPSILON {
  731. return true
  732. }
  733. let scroll = start_scroll + dist;
  734. // Redraws the screen from the cache
  735. self.scrollview(scroll).await;
  736. }
  737. TouchPhase::Ended | TouchPhase::Cancelled => {
  738. self.end_touch_phase(touch_y);
  739. }
  740. }
  741. true
  742. }
  743. }