Skip to main content

ferroui_controls/
control_extensions.rs

1use crate::{Control, RequestBringIntoViewEventArgs};
2use ferroui_base::controls::NameScopeExtensions;
3use ferroui_base::layout::{BringIntoViewRequest, Layoutable};
4use ferroui_base::reactive::{AnonymousObserver, IDisposable, IObservable};
5use ferroui_base::{ObjectType, Rect, Ref};
6use std::rc::Rc;
7
8impl Control {
9    /// Tries to bring the control into view.
10    pub fn bring_into_view(&self) {
11        self.bring_into_view_core(None);
12    }
13
14    /// Tries to bring the control into view.
15    ///
16    /// `rect` is the area of the control to being into view.
17    pub fn bring_into_view_rect(&self, rect: Rect) {
18        self.bring_into_view_core(Some(rect));
19    }
20
21    fn bring_into_view_core(&self, rect: Option<Rect>) {
22        if self.try_execute_bring_into_view(rect) {
23            return;
24        }
25
26        if let Some(layout_root) = self.get_layout_root() {
27            let layout_manager = layout_root.layout_manager();
28            if let Some(layout_manager) = layout_manager.as_bring_into_view_layout_manager() {
29                layout_manager
30                    .enqueue_bring_into_view(Rc::new(ControlBringIntoViewRequest { target: self.to_ref(), rect }));
31            }
32        }
33    }
34
35    fn try_execute_bring_into_view(&self, rect: Option<Rect>) -> bool {
36        if !self.is_effectively_visible() {
37            return true; // BringIntoView on an invisible control just does nothing.
38        }
39
40        if !self.is_measure_valid() || !self.is_arrange_valid() {
41            return false;
42        }
43
44        let mut ev = RequestBringIntoViewEventArgs::with_event(Control::request_bring_into_view_event());
45        ev.target_object = Some(self.to_ref().upcast());
46        ev.set_target_rect(rect.unwrap_or_else(|| Rect::from_size(self.bounds().size())));
47
48        self.raise_event(&ev);
49        true
50    }
51
52    /// Finds the named control in the scope of the specified control.
53    ///
54    /// Returns the control or `None` if not found; panics if the control has
55    /// no parent name scope or the named control is of a different class.
56    pub fn find_control<T: ObjectType>(&self, name: &str) -> Option<Ref<T>> {
57        match NameScopeExtensions::find_name_scope(self) {
58            Some(name_scope) => name_scope.find_as::<T>(name),
59            None => panic!("Could not find parent name scope."),
60        }
61    }
62
63    /// Gets the named control in the scope of the specified control.
64    ///
65    /// Panics if the control has no parent name scope or the named control
66    /// is not found.
67    pub fn get_control<T: ObjectType>(&self, name: &str) -> Ref<T> {
68        match self.find_control::<T>(name) {
69            Some(control) => control,
70            None => panic!("Could not find control named '{name}'."),
71        }
72    }
73
74    /// Sets a pseudoclass of the control depending on an observable trigger.
75    ///
76    /// Returns a disposable used to cancel the subscription.
77    pub fn set_pseudo_class_from(&self, name: &str, trigger: &dyn IObservable<bool>) -> Rc<dyn IDisposable> {
78        let name = name.to_string();
79        let weak = self.to_ref().downgrade();
80        trigger.subscribe(Rc::new(AnonymousObserver::new(move |x: bool| {
81            if let Some(this) = weak.upgrade() {
82                this.pseudo_classes().set(&name, x);
83            }
84        })))
85    }
86}
87
88struct ControlBringIntoViewRequest {
89    target: Ref<Control>,
90    rect: Option<Rect>,
91}
92
93impl BringIntoViewRequest for ControlBringIntoViewRequest {
94    fn target(&self) -> Ref<Layoutable> {
95        self.target.clone().upcast()
96    }
97
98    fn try_execute(&self) -> bool {
99        self.target.try_execute_bring_into_view(self.rect)
100    }
101}