query.rs 3.1 KB

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