Skip to main content

winio_ui_app_kit/widgets/
scroll_view.rs

1use inherit_methods_macro::inherit_methods;
2use objc2::{MainThreadOnly, rc::Retained};
3use objc2_app_kit::{NSControl, NSScrollView, NSView};
4use objc2_foundation::NSSize;
5use winio_handle::{AsContainer, BorrowedContainer};
6use winio_primitive::{Point, Size};
7
8use crate::{Result, catch, from_cgsize, transform_cgrect, transform_rect, widgets::Widget};
9
10#[derive(Debug)]
11pub struct ScrollView {
12    handle: Widget,
13    view: Retained<NSScrollView>,
14    inner_view: Retained<NSView>,
15}
16
17#[inherit_methods(from = "self.handle")]
18impl ScrollView {
19    pub fn new(parent: impl AsContainer) -> Result<Self> {
20        let parent = parent.as_container();
21        let mtm = parent.as_app_kit().mtm();
22
23        catch(|| unsafe {
24            let view = NSScrollView::new(mtm);
25            let inner_view = NSView::new(mtm);
26            let handle = Widget::from_nsview(parent, Retained::cast_unchecked(view.clone()))?;
27
28            view.setHasVerticalScroller(true);
29            view.setHasHorizontalScroller(true);
30            view.setDocumentView(Some(&inner_view));
31
32            Ok(Self {
33                handle,
34                view,
35                inner_view,
36            })
37        })
38        .flatten()
39    }
40
41    pub fn is_visible(&self) -> Result<bool>;
42
43    pub fn set_visible(&mut self, v: bool) -> Result<()>;
44
45    pub fn is_enabled(&self) -> Result<bool>;
46
47    pub fn set_enabled(&mut self, v: bool) -> Result<()>;
48
49    pub fn loc(&self) -> Result<Point>;
50
51    pub fn set_loc(&mut self, p: Point) -> Result<()>;
52
53    pub fn size(&self) -> Result<Size>;
54
55    pub fn set_size(&mut self, v: Size) -> Result<()> {
56        self.handle.set_size(v)?;
57        catch(|| {
58            let inner_size = from_cgsize(self.inner_view.frame().size);
59            let subviews = self.inner_view.subviews();
60            let frames = subviews
61                .iter()
62                .map(|c| {
63                    let mut frame = c.frame();
64                    let size = {
65                        let size = c.fittingSize();
66                        if size != NSSize::ZERO {
67                            size
68                        } else if let Ok(c) = c.downcast::<NSControl>() {
69                            c.sizeThatFits(NSSize::ZERO)
70                        } else {
71                            NSSize::ZERO
72                        }
73                    };
74                    frame.size.width = frame.size.width.max(size.width);
75                    frame.size.height = frame.size.height.max(size.height);
76                    transform_cgrect(inner_size, frame)
77                })
78                .collect::<Vec<_>>();
79            let rect = frames
80                .iter()
81                .copied()
82                .reduce(|a, b| a.union(&b))
83                .unwrap_or_default();
84            let mut rect = rect.to_box2d();
85            rect.min = rect.min.min(Point::zero());
86            let mut rect = rect.to_rect();
87            if rect.height() < v.height {
88                rect.size.height = v.height;
89            }
90            let frame = transform_rect(v, rect);
91            self.inner_view.setFrame(frame);
92            let inner_size = from_cgsize(frame.size);
93            for (c, rect) in subviews.into_iter().zip(frames) {
94                c.setFrame(transform_rect(inner_size, rect));
95            }
96        })
97    }
98
99    pub fn hscroll(&self) -> Result<bool> {
100        catch(|| self.view.hasHorizontalScroller())
101    }
102
103    pub fn set_hscroll(&mut self, v: bool) -> Result<()> {
104        catch(|| self.view.setHasHorizontalScroller(v))
105    }
106
107    pub fn vscroll(&self) -> Result<bool> {
108        catch(|| self.view.hasVerticalScroller())
109    }
110
111    pub fn set_vscroll(&mut self, v: bool) -> Result<()> {
112        catch(|| self.view.setHasVerticalScroller(v))
113    }
114
115    pub async fn start(&self) -> ! {
116        std::future::pending().await
117    }
118}
119
120winio_handle::impl_as_widget!(ScrollView, handle);
121
122impl AsContainer for ScrollView {
123    fn as_container(&self) -> BorrowedContainer<'_> {
124        BorrowedContainer::app_kit(&self.inner_view)
125    }
126}