yeehaw/elements/
pane_parent.rs

1use {
2    crate::{
3        Color, Context, DrawCh, DrawChs2D, DrawRegion, DrawUpdate, DynLocation, DynLocationSet,
4        DynVal, Element, ElementID, ElementOrganizer, Event, EventResponses, Pane, Parent,
5        ReceivableEvents, Size, Style, ZIndex,
6    },
7    std::collections::HashMap,
8    std::{
9        ops::Deref,
10        {
11            cell::{Ref, RefCell, RefMut},
12            rc::Rc,
13        },
14    },
15};
16
17/// ParentPane is a pane element which other objects can embed and build off
18/// of. It is a pane which can have children panes.
19///
20/// NOTE the ParentPane does not itself fulfill the Element trait however
21/// it provides much of the boilerplate required to do so.
22///
23/// the element store (el_store) is a store for sub-elements. Any of the sub-elements can be
24/// accessed any of the contents and share it with other elements of this parent pane.
25#[derive(Clone)]
26pub struct ParentPane {
27    pub pane: Pane,
28    pub eo: ElementOrganizer,
29    pub el_store: Rc<RefCell<HashMap<String, Vec<u8>>>>,
30}
31
32#[yeehaw_derive::impl_pane_basics_from(pane)]
33impl ParentPane {
34    pub fn new(ctx: &Context, kind: &'static str) -> Self {
35        let pane = Pane::new(ctx, kind).with_focused(true);
36        ParentPane {
37            pane,
38            eo: ElementOrganizer::default(),
39            el_store: Rc::new(RefCell::new(HashMap::new())),
40        }
41    }
42
43    pub fn at<D: Into<DynVal>, D2: Into<DynVal>>(self, x: D, y: D2) -> Self {
44        self.set_at(x.into(), y.into());
45        self
46    }
47
48    pub fn with_kind(self, kind: &'static str) -> Self {
49        self.pane.set_kind(kind);
50        self
51    }
52
53    pub fn focused(self) -> Self {
54        *self.pane.focused.borrow_mut() = true;
55        self
56    }
57
58    pub fn unfocused(self) -> Self {
59        *self.pane.focused.borrow_mut() = false;
60        self
61    }
62
63    pub fn with_element(self, el: Box<dyn Element>) -> Self {
64        self.add_element(el); // ignore the response as this is used during initialization
65        self
66    }
67
68    pub fn add_element(&self, el: Box<dyn Element>) {
69        self.eo.add_element(el, Some(Box::new(self.clone())))
70    }
71
72    pub fn remove_element(&self, el_id: &ElementID) {
73        self.eo.remove_element(el_id)
74    }
75
76    pub fn clear_elements(&self) {
77        self.eo.clear_elements()
78    }
79
80    pub fn has_elements(&self) -> bool {
81        !self.eo.els.borrow().is_empty()
82    }
83
84    // -------------------------------------
85    // Element functions
86
87    pub fn get_element(&self, el_id: &ElementID) -> Option<Box<dyn Element>> {
88        self.eo.get_element(el_id)
89    }
90
91    pub fn get_element_attribute(&self, el_id: &ElementID, key: &str) -> Option<Vec<u8>> {
92        self.eo.get_element_attribute(el_id, key)
93    }
94
95    pub fn update_el_z_index(&self, el_id: &ElementID, z: ZIndex) {
96        self.eo.update_el_z_index(el_id, z);
97    }
98
99    /// NOTE this name was chosen to distinguish itself from propagate_responses_upward
100    pub fn send_responses_upward(&self, ctx: &Context, resps: EventResponses) {
101        self.pane.send_responses_upward(ctx, resps);
102    }
103
104    pub fn focus(&self) {
105        self.set_focused(true);
106    }
107
108    pub fn unfocus(&self) {
109        self.set_focused(false);
110    }
111
112    /// sends an event to a specific element
113    #[must_use]
114    pub fn send_event_to_el(&self, ctx: &Context, el_id: &ElementID, ev: Event) -> EventResponses {
115        self.eo
116            .send_event_to_el(ctx, el_id, ev, Box::new(self.clone()))
117    }
118}
119
120#[yeehaw_derive::impl_element_from(pane)]
121impl Element for ParentPane {
122    fn can_receive(&self, ev: &Event) -> bool {
123        self.get_focused() && (self.pane.can_receive(ev) || self.eo.can_receive(ev))
124    }
125
126    fn receivable(&self) -> Vec<Rc<RefCell<ReceivableEvents>>> {
127        let mut rec = self.eo.receivable();
128        rec.extend(self.pane.receivable());
129        rec
130    }
131
132    //                                               (captured, resp          )
133    fn receive_event(&self, ctx: &Context, ev: Event) -> (bool, EventResponses) {
134        self.eo.event_process(ctx, ev, Box::new(self.clone()))
135    }
136
137    fn drawing(&self, ctx: &Context, dr: &DrawRegion, force_update: bool) -> Vec<DrawUpdate> {
138        if !self.get_visible() {
139            return Vec::with_capacity(0);
140        }
141        let mut out = self.pane.drawing(ctx, dr, force_update);
142        out.extend(self.eo.all_drawing_updates(ctx, dr, force_update));
143        out
144    }
145}
146
147impl Parent for ParentPane {
148    /// DO NOT CALL THIS FUNCTION DIRECTLY
149    /// This function is intended for internal propogation ONLY if you need to propogate changes
150    /// use the function: send_responses_upward
151    ///
152    /// Passes changes to inputability to this element's parent element. If
153    /// updateThisElementsPrioritizers is TRUE then this element's prioritizers should be updated
154    /// using the given IC. This should be set to false when an upwards propagation is being
155    /// initiated as all of the changes to the prioritzers should have already been handled. The
156    /// boolean should be set to true on all further calls as the changes are propagated upstream so
157    /// as to update the ancestors' prioritizers.
158    ///
159    /// childEl is the element which is invoking the propagation from BELOW this parent pane. This
160    /// is used by the parent to determine which events/cmds to update the prioritizers for.
161    ///
162    /// The propagateEl is the element to send further upward propagation to. Typically this means
163    /// the Element which is inheriting THIS parent pane.
164    ///
165    /// NOTE: propagateEl is necessary as the parent pane will usually have registered an element
166    /// that extends ParentPane. If this ParentPane sent itself, it would not match the child
167    /// registered in the parent's EO.
168    ///
169    /// NOTE this function should be extended from if the parent pane is used as a base for a more
170    /// complex element. As the developer you should be fulfilling the
171    /// propagate_responses_upward function directly.
172    ///
173    /// NOTE the parent_ctx is the correct context for THIS parent pane.
174    fn propagate_responses_upward(
175        &self, ctx: &Context, child_el_id: &ElementID, mut resps: EventResponses,
176    ) {
177        let b: Box<dyn Parent> = Box::new(self.clone());
178        self.eo
179            .partially_process_ev_resps(ctx, child_el_id, &mut resps, &b);
180        if let Some(parent) = self.pane.parent.borrow_mut().deref() {
181            parent.propagate_responses_upward(ctx, &self.id(), resps);
182        }
183    }
184
185    fn get_store_item(&self, key: &str) -> Option<Vec<u8>> {
186        self.el_store.borrow().get(key).cloned()
187    }
188
189    fn set_store_item(&self, key: &str, value: Vec<u8>) {
190        self.el_store.borrow_mut().insert(key.to_string(), value);
191    }
192
193    fn get_parent_focused(&self) -> bool {
194        self.pane.get_focused()
195    }
196
197    fn get_id(&self) -> ElementID {
198        self.pane.id()
199    }
200}