mod.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 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_trait::async_trait;
  19. use futures::stream::{FuturesUnordered, StreamExt};
  20. use miniquad::{KeyCode, KeyMods, MouseButton};
  21. use std::sync::{Arc, OnceLock, Weak};
  22. use crate::{
  23. gfx::{DrawCall, Point, Rectangle},
  24. prop::{BatchGuardPtr, ModifyAction, PropertyAtomicGuard, PropertyPtr, Role},
  25. scene::{Pimpl, SceneNode as SceneNode3, SceneNodePtr, SceneNodeWeak},
  26. util::i18n::I18nBabelFish,
  27. ExecutorPtr,
  28. };
  29. static LONG_PRESS_TIMEOUT: OnceLock<u32> = OnceLock::new();
  30. /// The system long-press timeout in milliseconds. Queried once from
  31. /// `ViewConfiguration.getLongPressTimeout()` on Android, defaults to 400
  32. /// on other platforms.
  33. pub fn long_press_timeout() -> u32 {
  34. *LONG_PRESS_TIMEOUT.get_or_init(|| {
  35. #[cfg(target_os = "android")]
  36. {
  37. crate::android::get_long_press_timeout()
  38. }
  39. #[cfg(not(target_os = "android"))]
  40. {
  41. 400
  42. }
  43. })
  44. }
  45. mod button;
  46. pub use button::{Button, ButtonPtr};
  47. pub mod chatview;
  48. pub use chatview::{ChatView, ChatViewPtr};
  49. pub mod tokentable;
  50. pub use tokentable::{TokenRow, TokenTable, TokenTablePtr};
  51. mod edit;
  52. pub use edit::{BaseEdit, BaseEditPtr, BaseEditType};
  53. pub mod emoji_picker;
  54. pub use emoji_picker::{EmojiPicker, EmojiPickerPtr};
  55. pub mod gesture;
  56. // The full config vocabulary is re-exported for widgets adopting
  57. // per-node recognizer configs (TapCfg/DragCfg axes + direction); the
  58. // crate is a binary so not every name has an in-crate use yet.
  59. #[allow(unused_imports)]
  60. pub use gesture::{
  61. Axes, Direction, DragCfg, GestureAction, GestureConstants, GestureSession, GestureSessionPtr,
  62. GestureSet, GestureTarget, LongPressCfg, TapCfg,
  63. };
  64. mod image;
  65. #[allow(unused_imports)]
  66. pub use image::{Image, ImagePtr};
  67. mod vid;
  68. #[allow(unused_imports)]
  69. pub use vid::{Video, VideoPtr};
  70. mod vector_art;
  71. pub use vector_art::{
  72. shape::{ShapeVertex, VectorShape},
  73. VectorArt, VectorArtPtr,
  74. };
  75. mod layer;
  76. pub use layer::{Layer, LayerPtr};
  77. mod scroll_layer;
  78. pub use scroll_layer::{ScrollLayer, ScrollLayerPtr};
  79. mod shortcut;
  80. pub use shortcut::{Shortcut, ShortcutPtr};
  81. mod menu;
  82. pub use menu::{Menu, MenuPtr};
  83. mod text;
  84. pub use text::{Text, TextPtr};
  85. mod text_scramble;
  86. pub use text_scramble::{TextScramble, TextScramblePtr};
  87. mod win;
  88. pub use win::{Window, WindowPtr};
  89. macro_rules! e { ($($arg:tt)*) => { error!(target: "scene::on_modify", $($arg)*); } }
  90. macro_rules! t { ($($arg:tt)*) => { trace!(target: "scene::on_modify", $($arg)*); } }
  91. /// Handle for requesting a redraw pass from the root window's draw loop.
  92. /// Cheap to clone. The underlying queue is bounded(1), so triggers sent
  93. /// while a pass is running or pending are coalesced into a single
  94. /// additional pass. Property mutations SHOULD be made through
  95. /// `make_guard()` so the trigger fires once, after the whole update
  96. /// chain has settled. State mutations must happen before calling
  97. /// `trigger()` so the resulting pass observes them.
  98. #[derive(Clone)]
  99. pub struct RedrawTrigger(async_channel::Sender<()>);
  100. impl RedrawTrigger {
  101. /// Create the trigger handle and the receiver consumed by the draw loop.
  102. pub fn new() -> (Self, async_channel::Receiver<()>) {
  103. let (tx, rx) = async_channel::bounded(1);
  104. (Self(tx), rx)
  105. }
  106. /// Request a draw pass without a batch scope. Only for mutations that
  107. /// need no `PropertyAtomicGuard` (plain fields, caches): the trigger is
  108. /// enqueued immediately, so all state must already be settled. For
  109. /// property updates use `make_guard()` instead, which defers the
  110. /// trigger to end-of-batch.
  111. ///
  112. /// Never blocks. A trigger is only dropped when another is already
  113. /// queued, which is equivalent: the queued token guarantees a pass
  114. /// that starts after this call, and since callers mutate state before
  115. /// triggering, that pass observes the mutation.
  116. ///
  117. /// Correctness relies on the draw loop draining exactly one token per
  118. /// iteration *before* drawing. Do not change the loop to recv after
  119. /// the draw or to drain multiple tokens per pass: a full channel means
  120. /// a pass is guaranteed, and that guarantee is what makes dropped
  121. /// triggers safe. Blocking here would also self-deadlock, since draws
  122. /// can trigger further passes.
  123. pub fn trigger(&self) {
  124. let _ = self.0.try_send(());
  125. }
  126. /// Open a property-update batch bound to this trigger. Property
  127. /// notifications are deferred until the batch — including any batches
  128. /// spawned from it by property-change reactions holding the batch
  129. /// guard — completes, and then exactly one redraw trigger is enqueued.
  130. /// Use this instead of manual `trigger()` calls around property
  131. /// mutations so a pass can never observe the intermediate state of a
  132. /// multi-step update.
  133. pub fn make_guard(&self, debug_str: Option<&'static str>) -> PropertyAtomicGuard {
  134. let redraw = self.0.clone();
  135. PropertyAtomicGuard::new(Box::new(move |_| {
  136. if let Some(tag) = debug_str {
  137. t!("Redraw batch ({tag}) ended, triggering redraw");
  138. }
  139. let _ = redraw.try_send(());
  140. }))
  141. }
  142. }
  143. #[async_trait]
  144. pub trait UIObject: Sync {
  145. fn priority(&self) -> u32;
  146. /// Called after schema and scenegraph is init but before miniquad starts.
  147. fn init(&self) {}
  148. /// Done after miniquad has started and the first window draw has been done.
  149. async fn start(self: Arc<Self>, _ex: ExecutorPtr) {}
  150. /// Clear all buffers and caches
  151. fn stop(&self) {}
  152. async fn draw(
  153. &self,
  154. _parent_rect: Rectangle,
  155. _atom: &mut PropertyAtomicGuard,
  156. ) -> Option<DrawUpdate> {
  157. None
  158. }
  159. async fn handle_char(&self, _key: char, _mods: KeyMods, _repeat: bool) -> bool {
  160. false
  161. }
  162. async fn handle_key_down(&self, _key: KeyCode, _mods: KeyMods, _repeat: bool) -> bool {
  163. false
  164. }
  165. async fn handle_key_up(&self, _key: KeyCode, _mods: KeyMods) -> bool {
  166. false
  167. }
  168. async fn handle_mouse_btn_down(&self, _btn: MouseButton, _mouse_pos: Point) -> bool {
  169. false
  170. }
  171. async fn handle_mouse_btn_up(&self, _btn: MouseButton, _mouse_pos: Point) -> bool {
  172. false
  173. }
  174. async fn handle_mouse_move(&self, _mouse_pos: Point) -> bool {
  175. false
  176. }
  177. async fn handle_mouse_wheel(&self, _wheel_pos: Point) -> bool {
  178. false
  179. }
  180. /// The gestures this widget accepts. Non-participating widgets
  181. /// return [`GestureSet::NONE`] and are inert.
  182. fn gesture_set(&self) -> GestureSet {
  183. GestureSet::NONE
  184. }
  185. /// Whether this widget is a gesture target at `pos` (given in the
  186. /// widget's parent coordinate space, like `handle_gesture`).
  187. fn gesture_hit_test(&self, _pos: Point) -> bool {
  188. false
  189. }
  190. /// Containers: descend the gesture chain under `pos` (the
  191. /// container's parent space), translating coordinates. The default
  192. /// is a no-op for leaf widgets.
  193. fn gesture_descend(&self, _pos: Point, _offset: Point, _chain: &mut Vec<GestureTarget>) {}
  194. async fn handle_gesture(&self, _gesture: GestureAction) -> bool {
  195. false
  196. }
  197. fn set_i18n(&self, _i18n_fish: &I18nBabelFish) {}
  198. }
  199. pub struct DrawUpdate {
  200. pub key: u64,
  201. pub draw_calls: Vec<(u64, DrawCall)>,
  202. }
  203. pub struct OnModify<T> {
  204. ex: ExecutorPtr,
  205. #[allow(dead_code)]
  206. node: SceneNodeWeak,
  207. me: Weak<T>,
  208. pub tasks: Vec<smol::Task<()>>,
  209. }
  210. impl<T: Send + Sync + 'static> OnModify<T> {
  211. pub fn new(ex: ExecutorPtr, node: SceneNodeWeak, me: Weak<T>) -> Self {
  212. Self { ex, node, me, tasks: vec![] }
  213. }
  214. pub fn when_change<F>(
  215. &mut self,
  216. prop: PropertyPtr,
  217. f: impl Fn(Arc<T>, BatchGuardPtr) -> F + Send + 'static,
  218. ) where
  219. F: std::future::Future<Output = ()> + Send + 'static,
  220. {
  221. self.when_change_impl(prop, false, f)
  222. }
  223. /// Like `when_change`, but also skips `Role::Internal` modifications of
  224. /// dependencies. Draw-pass-migrated widgets want this: internal sets are
  225. /// eval echoes (typically produced by the draw pass itself), so reacting
  226. /// to them would queue a pass for every pass, forever. External mutation
  227. /// sites (handlers, resize/insets tasks) trigger passes explicitly.
  228. pub fn when_change_external<F>(
  229. &mut self,
  230. prop: PropertyPtr,
  231. f: impl Fn(Arc<T>, BatchGuardPtr) -> F + Send + 'static,
  232. ) where
  233. F: std::future::Future<Output = ()> + Send + 'static,
  234. {
  235. self.when_change_impl(prop, true, f)
  236. }
  237. fn when_change_impl<F>(
  238. &mut self,
  239. prop: PropertyPtr,
  240. skip_internal: bool,
  241. f: impl Fn(Arc<T>, BatchGuardPtr) -> F + Send + 'static,
  242. ) where
  243. F: std::future::Future<Output = ()> + Send + 'static,
  244. {
  245. let mut on_modify_subs = vec![(Arc::downgrade(&prop), None, prop.subscribe_modify())];
  246. for dep in prop.get_depends() {
  247. let Some(dep_prop) = dep.prop.upgrade() else { continue };
  248. on_modify_subs.push((dep.prop, Some(dep.i), dep_prop.subscribe_modify()));
  249. }
  250. let me = self.me.clone();
  251. let task = self.ex.spawn(async move {
  252. loop {
  253. let mut poll_queues = FuturesUnordered::new();
  254. for (i, (prop_weak, prop_i, on_modify_sub)) in on_modify_subs.iter().enumerate() {
  255. let recv = on_modify_sub.receive();
  256. poll_queues.push(async move {
  257. let (role, action, batch_guard) = recv.await.ok()?;
  258. Some((i, prop_weak, prop_i, role, action, batch_guard))
  259. });
  260. }
  261. let Some(Some((idx, prop_weak, prop_i, role, action, batch_guard))) = poll_queues.next().await else {
  262. e!("Property {:?} on_modify pipe is broken", prop);
  263. return
  264. };
  265. // Skip internal messages from ourselves or explicitly marked ignored.
  266. // Draw-pass widgets also skip internal dependency echoes.
  267. if (idx == 0 && role == Role::Internal) ||
  268. (skip_internal && role == Role::Internal) ||
  269. role == Role::Ignored
  270. {
  271. continue
  272. }
  273. if let Some(prop_i) = prop_i {
  274. match action {
  275. ModifyAction::Set(i) => if *prop_i != i { continue },
  276. ModifyAction::SetCache(idxs) => if !idxs.contains(prop_i) { continue }
  277. _ => continue
  278. }
  279. }
  280. if idx == 0 {
  281. t!("Property {:?} modified [depend_idx={idx}, role={role:?}]", prop);
  282. } else {
  283. t!(
  284. "Property {:?} modified -> triggering {:?} [depend_idx={idx}, role={role:?}]",
  285. prop_weak.upgrade(),
  286. prop
  287. );
  288. }
  289. let Some(self_) = me.upgrade() else {
  290. // Normally unreachable: an owner is dropped only after
  291. // stop() cleared its modify tasks, so an alive task
  292. // implies an alive owner. Runtime node removal
  293. // (netdebug rmnode) breaks that: stop() merely drops
  294. // the Task handles, and a future that is mid-poll on
  295. // a worker thread keeps running until its next yield,
  296. // which can carry it past this upgrade after the last
  297. // Arc is gone (this future holds only weak refs).
  298. // With no owner there is nothing left to notify, so
  299. // exit quietly instead of panicking.
  300. warn!(
  301. target: "scene::on_modify",
  302. "Property {:?} owner destroyed before modify_task was stopped", prop
  303. );
  304. return
  305. };
  306. //debug!(target: "app", "property modified");
  307. f(self_, batch_guard).await;
  308. }
  309. });
  310. self.tasks.push(task);
  311. }
  312. }
  313. pub fn get_ui_object_ptr(node: &SceneNode3) -> Arc<dyn UIObject + Send> {
  314. match node.pimpl() {
  315. Pimpl::Layer(obj) => obj.clone(),
  316. Pimpl::ScrollLayer(obj) => obj.clone(),
  317. Pimpl::VectorArt(obj) => obj.clone(),
  318. Pimpl::Text(obj) => obj.clone(),
  319. Pimpl::TextScramble(obj) => obj.clone(),
  320. Pimpl::Edit(obj) => obj.clone(),
  321. Pimpl::Image(obj) => obj.clone(),
  322. Pimpl::Video(obj) => obj.clone(),
  323. Pimpl::Button(obj) => obj.clone(),
  324. Pimpl::EmojiPicker(obj) => obj.clone(),
  325. Pimpl::Shortcut(obj) => obj.clone(),
  326. Pimpl::Menu(obj) => obj.clone(),
  327. Pimpl::TokenTable(obj) => obj.clone(),
  328. Pimpl::ChatView(obj) => obj.clone(),
  329. Pimpl::PrivMsgNode(obj) => obj.clone(),
  330. Pimpl::DateMsgNode(obj) => obj.clone(),
  331. Pimpl::FileMsgNode(obj) => obj.clone(),
  332. _ => panic!("unhandled type for get_ui_object: {node:?}"),
  333. }
  334. }
  335. pub fn get_ui_object3<'a>(node: &'a SceneNode3) -> &'a dyn UIObject {
  336. match node.pimpl() {
  337. Pimpl::Layer(obj) => obj.as_ref(),
  338. Pimpl::ScrollLayer(obj) => obj.as_ref(),
  339. Pimpl::VectorArt(obj) => obj.as_ref(),
  340. Pimpl::Text(obj) => obj.as_ref(),
  341. Pimpl::TextScramble(obj) => obj.as_ref(),
  342. Pimpl::Edit(obj) => obj.as_ref(),
  343. Pimpl::Image(obj) => obj.as_ref(),
  344. Pimpl::Video(obj) => obj.as_ref(),
  345. Pimpl::Button(obj) => obj.as_ref(),
  346. Pimpl::EmojiPicker(obj) => obj.as_ref(),
  347. Pimpl::Shortcut(obj) => obj.as_ref(),
  348. Pimpl::Menu(obj) => obj.as_ref(),
  349. Pimpl::TokenTable(obj) => obj.as_ref(),
  350. Pimpl::ChatView(obj) => obj.as_ref(),
  351. Pimpl::PrivMsgNode(obj) => obj.as_ref(),
  352. Pimpl::DateMsgNode(obj) => obj.as_ref(),
  353. Pimpl::FileMsgNode(obj) => obj.as_ref(),
  354. _ => panic!("unhandled type for get_ui_object: {node:?}"),
  355. }
  356. }
  357. pub fn get_children_ordered(node: &SceneNode3) -> Vec<SceneNodePtr> {
  358. let mut child_infs = vec![];
  359. for child in node.get_children() {
  360. let obj = get_ui_object3(&child);
  361. let priority = obj.priority();
  362. child_infs.push((child, priority));
  363. }
  364. child_infs.sort_unstable_by_key(|(_, priority)| *priority);
  365. let nodes = child_infs.into_iter().rev().map(|(node, _)| node).collect();
  366. nodes
  367. }