ribir_core/
wrap_render.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use ribir_geom::{Point, Size, Transform};
use smallvec::SmallVec;
use widget_id::RenderQueryable;

use crate::prelude::*;

/// This trait is for a render widget that does not need to be an independent
/// node in the widget tree. It can serve as a wrapper for another render
/// widget.
///
/// # Which widgets should implement this trait?
///
/// If a render widget accepts a single child and its layout size matches its
/// child size, it can be implemented as a `WrapRender` instead of `Render`,
/// eliminating the need to allocate a node in the widget tree.
pub trait WrapRender {
  fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size {
    host.perform_layout(clamp, ctx)
  }

  fn paint(&self, host: &dyn Render, ctx: &mut PaintingCtx) { host.paint(ctx) }

  fn only_sized_by_parent(&self, host: &dyn Render) -> bool {
    // Detected by its host by default, so we return true here.
    host.only_sized_by_parent()
  }

  fn hit_test(&self, host: &dyn Render, ctx: &mut HitTestCtx, pos: Point) -> HitTest {
    host.hit_test(ctx, pos)
  }

  fn get_transform(&self, host: &dyn Render) -> Option<Transform> { host.get_transform() }

  fn combine_child(
    this: impl StateWriter<Value = Self>, mut child: Widget, dirty: DirtyPhase,
  ) -> Widget
  where
    Self: Sized + 'static,
  {
    let wrapper: Box<dyn WrapRender> = match this.try_into_value() {
      Ok(this) => Box::new(this),
      Err(this) => {
        let reader = match this.into_reader() {
          Ok(r) => r,
          Err(s) => {
            child = child.dirty_on(s.raw_modifies(), dirty);
            s.clone_reader()
          }
        };
        Box::new(reader)
      }
    };

    child.on_build(|id| {
      id.wrap_node(BuildCtx::get_mut().tree_mut(), move |r| {
        Box::new(RenderPair { wrapper, host: r })
      });
    })
  }
}

struct RenderPair {
  wrapper: Box<dyn WrapRender>,
  host: Box<dyn RenderQueryable>,
}

impl Query for RenderPair {
  fn query_all<'q>(&'q self, query_id: &QueryId, out: &mut SmallVec<[QueryHandle<'q>; 1]>) {
    self.host.query_all(query_id, out)
  }

  fn query_all_write<'q>(&'q self, query_id: &QueryId, out: &mut SmallVec<[QueryHandle<'q>; 1]>) {
    self.host.query_all_write(query_id, out)
  }

  fn query(&self, query_id: &QueryId) -> Option<QueryHandle> { self.host.query(query_id) }

  fn query_write(&self, query_id: &QueryId) -> Option<QueryHandle> {
    self.host.query_write(query_id)
  }

  fn queryable(&self) -> bool { self.host.queryable() }
}

impl Render for RenderPair {
  fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
    self
      .wrapper
      .perform_layout(clamp, self.host.as_render(), ctx)
  }

  fn paint(&self, ctx: &mut PaintingCtx) { self.wrapper.paint(self.host.as_render(), ctx); }

  fn only_sized_by_parent(&self) -> bool {
    self
      .wrapper
      .only_sized_by_parent(self.host.as_render())
  }

  fn hit_test(&self, ctx: &mut HitTestCtx, pos: Point) -> HitTest {
    self
      .wrapper
      .hit_test(self.host.as_render(), ctx, pos)
  }

  fn dirty_phase(&self) -> DirtyPhase { self.host.dirty_phase() }

  fn get_transform(&self) -> Option<Transform> { self.wrapper.get_transform(self.host.as_render()) }
}

impl<R> WrapRender for R
where
  R: StateReader,
  R::Value: WrapRender,
{
  fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size {
    self.read().perform_layout(clamp, host, ctx)
  }

  fn paint(&self, host: &dyn Render, ctx: &mut PaintingCtx) { self.read().paint(host, ctx) }

  fn only_sized_by_parent(&self, host: &dyn Render) -> bool {
    self.read().only_sized_by_parent(host)
  }

  fn hit_test(&self, host: &dyn Render, ctx: &mut HitTestCtx, pos: Point) -> HitTest {
    self.read().hit_test(host, ctx, pos)
  }

  fn get_transform(&self, host: &dyn Render) -> Option<Transform> {
    self.read().get_transform(host)
  }
}

#[macro_export]
macro_rules! impl_compose_child_for_wrap_render {
  ($name:ty, $dirty:expr) => {
    impl<'c> ComposeChild<'c> for $name {
      type Child = Widget<'c>;
      fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
        WrapRender::combine_child(this, child, $dirty)
      }
    }
  };
}

pub(crate) use impl_compose_child_for_wrap_render;