Skip to main content

winio_ui_app_kit/widgets/
tab_view.rs

1use inherit_methods_macro::inherit_methods;
2use objc2::{
3    DefinedClass, MainThreadOnly, define_class, msg_send,
4    rc::{Allocated, Retained},
5    runtime::ProtocolObject,
6};
7use objc2_app_kit::{NSTabView, NSTabViewDelegate, NSTabViewItem, NSView};
8use objc2_foundation::{MainThreadMarker, NSObject, NSObjectProtocol, NSString};
9use winio_callback::Callback;
10use winio_handle::{AsContainer, BorrowedContainer};
11use winio_primitive::{Point, Size};
12
13use crate::{Error, GlobalRuntime, Result, catch, from_cgsize, from_nsstring, widgets::Widget};
14
15#[derive(Debug)]
16pub struct TabView {
17    handle: Widget,
18    view: Retained<NSTabView>,
19    delegate: Retained<TabViewDelegate>,
20}
21
22#[inherit_methods(from = "self.handle")]
23impl TabView {
24    pub fn new(parent: impl AsContainer) -> Result<Self> {
25        let parent = parent.as_container();
26        let mtm = parent.as_app_kit().mtm();
27
28        catch(|| unsafe {
29            let view = NSTabView::new(mtm);
30            let handle = Widget::from_nsview(parent, Retained::cast_unchecked(view.clone()))?;
31
32            let delegate = TabViewDelegate::new(mtm);
33            let del_obj = ProtocolObject::from_ref(&*delegate);
34            view.setDelegate(Some(del_obj));
35
36            Ok(Self {
37                handle,
38                view,
39                delegate,
40            })
41        })
42        .flatten()
43    }
44
45    pub fn is_visible(&self) -> Result<bool>;
46
47    pub fn set_visible(&mut self, v: bool) -> Result<()>;
48
49    pub fn is_enabled(&self) -> Result<bool>;
50
51    pub fn set_enabled(&mut self, v: bool) -> Result<()>;
52
53    pub fn loc(&self) -> Result<Point>;
54
55    pub fn set_loc(&mut self, p: Point) -> Result<()>;
56
57    pub fn size(&self) -> Result<Size>;
58
59    pub fn set_size(&mut self, v: Size) -> Result<()>;
60
61    pub fn selection(&self) -> Result<Option<usize>> {
62        catch(|| {
63            self.view
64                .selectedTabViewItem()
65                .map(|item| self.view.indexOfTabViewItem(&item) as _)
66        })
67    }
68
69    pub fn set_selection(&mut self, i: usize) -> Result<()> {
70        catch(|| self.view.selectTabViewItemAtIndex(i as _))
71    }
72
73    pub async fn wait_select(&self) {
74        self.delegate.ivars().did_select.wait().await
75    }
76
77    pub fn insert(&mut self, i: usize, item: &TabViewItem) -> Result<()> {
78        catch(|| self.view.insertTabViewItem_atIndex(&item.item, i as _))
79    }
80
81    pub fn remove(&mut self, i: usize) -> Result<()> {
82        catch(|| {
83            self.view
84                .removeTabViewItem(&self.view.tabViewItemAtIndex(i as _))
85        })
86    }
87
88    pub fn len(&self) -> Result<usize> {
89        catch(|| self.view.numberOfTabViewItems() as _)
90    }
91
92    pub fn is_empty(&self) -> Result<bool> {
93        Ok(self.len()? == 0)
94    }
95
96    pub fn clear(&mut self) -> Result<()> {
97        catch(|| {
98            while self.view.numberOfTabViewItems() > 0 {
99                self.view
100                    .removeTabViewItem(&self.view.tabViewItemAtIndex(0));
101            }
102        })
103    }
104}
105
106winio_handle::impl_as_widget!(TabView, handle);
107
108#[derive(Debug, Default)]
109struct TabViewDelegateIvars {
110    did_select: Callback,
111}
112
113define_class! {
114    #[unsafe(super(NSObject))]
115    #[name = "WinioTabViewDelegate"]
116    #[ivars = TabViewDelegateIvars]
117    #[thread_kind = MainThreadOnly]
118    #[derive(Debug)]
119    struct TabViewDelegate;
120
121    impl TabViewDelegate {
122        #[unsafe(method_id(init))]
123        fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
124            let this = this.set_ivars(TabViewDelegateIvars::default());
125            unsafe { msg_send![super(this), init] }
126        }
127    }
128
129    unsafe impl NSObjectProtocol for TabViewDelegate {}
130
131    #[allow(non_snake_case)]
132    unsafe impl NSTabViewDelegate for TabViewDelegate {
133        #[unsafe(method(tabView:didSelectTabViewItem:))]
134        unsafe fn tabView_didSelectTabViewItem(
135            &self,
136            _tab_view: &NSTabView,
137            _tab_view_item: Option<&NSTabViewItem>,
138        ) {
139            self.ivars().did_select.signal::<GlobalRuntime>(());
140        }
141    }
142}
143
144impl TabViewDelegate {
145    pub fn new(mtm: MainThreadMarker) -> Retained<Self> {
146        unsafe { msg_send![mtm.alloc::<Self>(), init] }
147    }
148}
149
150#[derive(Debug)]
151pub struct TabViewItem {
152    item: Retained<NSTabViewItem>,
153    view: Retained<NSView>,
154}
155
156impl TabViewItem {
157    pub fn new() -> Result<Self> {
158        let mtm = MainThreadMarker::new().ok_or(Error::NotMainThread)?;
159        catch(|| {
160            let item = NSTabViewItem::new();
161            let view = NSView::new(mtm);
162            item.setView(Some(&view));
163            Self { item, view }
164        })
165    }
166
167    pub fn text(&self) -> Result<String> {
168        catch(|| from_nsstring(&self.item.label()))
169    }
170
171    pub fn set_text(&mut self, s: impl AsRef<str>) -> Result<()> {
172        catch(|| self.item.setLabel(&NSString::from_str(s.as_ref())))
173    }
174
175    pub fn size(&self) -> Result<Size> {
176        catch(|| {
177            let frame = self.view.frame().size;
178            Ok(from_cgsize(frame))
179        })
180        .flatten()
181    }
182}
183
184impl AsContainer for TabViewItem {
185    fn as_container(&self) -> BorrowedContainer<'_> {
186        BorrowedContainer::app_kit(&self.view)
187    }
188}