query.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard};
  2. use dioxus_native_core::{real_dom::NodeImmutable, NodeId, RealDom};
  3. use taffy::{
  4. geometry::Point,
  5. prelude::{Layout, Size},
  6. Taffy,
  7. };
  8. use crate::{layout::TaffyLayout, layout_to_screen_space};
  9. /// Allows querying the layout of nodes after rendering. It will only provide a correct value after a node is rendered.
  10. /// Provided as a root context for all tui applictions.
  11. /// # Example
  12. /// ```rust, ignore
  13. /// use dioxus::prelude::*;
  14. /// use dioxus_tui::query::Query;
  15. /// use dioxus_tui::Size;
  16. ///
  17. /// fn main() {
  18. /// dioxus_tui::launch(app);
  19. /// }
  20. ///
  21. /// fn app(cx: Scope) -> Element {
  22. /// let hue = use_state(cx, || 0.0);
  23. /// let brightness = use_state(cx, || 0.0);
  24. /// let tui_query: Query = cx.consume_context().unwrap();
  25. /// cx.render(rsx! {
  26. /// div{
  27. /// width: "100%",
  28. /// background_color: "hsl({hue}, 70%, {brightness}%)",
  29. /// onmousemove: move |evt| {
  30. /// let node = tui_query.get(cx.root_node().mounted_id());
  31. /// let Size{width, height} = node.size().unwrap();
  32. /// hue.set((evt.data.offset_x as f32/width as f32)*255.0);
  33. /// brightness.set((evt.data.offset_y as f32/height as f32)*100.0);
  34. /// },
  35. /// "hsl({hue}, 70%, {brightness}%)",
  36. /// }
  37. /// })
  38. /// }
  39. /// ```
  40. #[derive(Clone)]
  41. pub struct Query {
  42. pub(crate) rdom: Arc<RwLock<RealDom>>,
  43. pub(crate) stretch: Arc<Mutex<Taffy>>,
  44. }
  45. impl Query {
  46. pub fn new(rdom: Arc<RwLock<RealDom>>, stretch: Arc<Mutex<Taffy>>) -> Self {
  47. Self { rdom, stretch }
  48. }
  49. pub fn get(&self, id: NodeId) -> ElementRef {
  50. let rdom = self.rdom.read();
  51. let stretch = self.stretch.lock();
  52. ElementRef::new(
  53. rdom.expect("rdom lock poisoned"),
  54. stretch.expect("taffy lock poisoned"),
  55. id,
  56. )
  57. }
  58. }
  59. pub struct ElementRef<'a> {
  60. inner: RwLockReadGuard<'a, RealDom>,
  61. stretch: MutexGuard<'a, Taffy>,
  62. id: NodeId,
  63. }
  64. impl<'a> ElementRef<'a> {
  65. fn new(
  66. inner: RwLockReadGuard<'a, RealDom>,
  67. stretch: MutexGuard<'a, Taffy>,
  68. id: NodeId,
  69. ) -> Self {
  70. Self { inner, stretch, id }
  71. }
  72. pub fn size(&self) -> Option<Size<u32>> {
  73. self.layout().map(|l| l.size.map(|v| v.round() as u32))
  74. }
  75. pub fn pos(&self) -> Option<Point<u32>> {
  76. self.layout().map(|l| Point {
  77. x: l.location.x.round() as u32,
  78. y: l.location.y.round() as u32,
  79. })
  80. }
  81. pub fn layout(&self) -> Option<Layout> {
  82. let layout = self
  83. .stretch
  84. .layout(
  85. self.inner
  86. .get(self.id)
  87. .unwrap()
  88. .get::<TaffyLayout>()
  89. .unwrap()
  90. .node
  91. .ok()?,
  92. )
  93. .ok();
  94. layout.map(|layout| Layout {
  95. order: layout.order,
  96. size: layout.size.map(layout_to_screen_space),
  97. location: Point {
  98. x: layout_to_screen_space(layout.location.x),
  99. y: layout_to_screen_space(layout.location.y),
  100. },
  101. })
  102. }
  103. }