winio_ui_app_kit/widgets/
scroll_view.rs1use 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}