Skip to main content

winio_ui_app_kit/widgets/
slider.rs

1use inherit_methods_macro::inherit_methods;
2use objc2::{
3    DefinedClass, MainThreadMarker, MainThreadOnly, define_class, msg_send,
4    rc::{Allocated, Retained},
5    sel,
6};
7use objc2_app_kit::{NSSlider, NSTickMarkPosition};
8use objc2_foundation::NSObject;
9use winio_callback::Callback;
10use winio_handle::AsContainer;
11use winio_primitive::{Orient, Point, Size, TickPosition};
12
13use crate::{GlobalRuntime, Result, Widget, catch};
14
15#[derive(Debug)]
16pub struct Slider {
17    handle: Widget,
18    view: Retained<NSSlider>,
19    delegate: Retained<SliderDelegate>,
20}
21
22#[inherit_methods(from = "self.handle")]
23impl Slider {
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 = NSSlider::new(mtm);
30            let handle = Widget::from_nsview(parent, Retained::cast_unchecked(view.clone()))?;
31
32            let delegate = SliderDelegate::new(mtm);
33            view.setTarget(Some(&delegate));
34            view.setAction(Some(sel!(onAction)));
35
36            view.setEnabled(true);
37
38            Ok(Self {
39                handle,
40                view,
41                delegate,
42            })
43        })
44        .flatten()
45    }
46
47    pub fn is_visible(&self) -> Result<bool>;
48
49    pub fn set_visible(&mut self, v: bool) -> Result<()>;
50
51    pub fn is_enabled(&self) -> Result<bool>;
52
53    pub fn set_enabled(&mut self, v: bool) -> Result<()>;
54
55    pub fn preferred_size(&self) -> Result<Size>;
56
57    pub fn loc(&self) -> Result<Point>;
58
59    pub fn set_loc(&mut self, p: Point) -> Result<()>;
60
61    pub fn size(&self) -> Result<Size>;
62
63    pub fn set_size(&mut self, v: Size) -> Result<()>;
64
65    pub fn tooltip(&self) -> Result<String>;
66
67    pub fn set_tooltip(&mut self, s: impl AsRef<str>) -> Result<()>;
68
69    pub fn tick_pos(&self) -> Result<TickPosition> {
70        let tpos = catch(|| self.view.tickMarkPosition())?;
71        let tpos = match tpos {
72            NSTickMarkPosition::Below => TickPosition::BottomRight,
73            NSTickMarkPosition::Above => TickPosition::TopLeft,
74            _ => TickPosition::None,
75        };
76        Ok(tpos)
77    }
78
79    pub fn set_tick_pos(&mut self, v: TickPosition) -> Result<()> {
80        let tpos = match v {
81            TickPosition::BottomRight => NSTickMarkPosition::Below,
82            _ => NSTickMarkPosition::Above,
83        };
84        catch(|| {
85            self.view.setTickMarkPosition(tpos);
86        })
87    }
88
89    pub fn orient(&self) -> Result<Orient> {
90        let vertical = catch(|| self.view.isVertical())?;
91        if vertical {
92            Ok(Orient::Vertical)
93        } else {
94            Ok(Orient::Horizontal)
95        }
96    }
97
98    pub fn set_orient(&mut self, v: Orient) -> Result<()> {
99        catch(|| self.view.setVertical(matches!(v, Orient::Vertical)))
100    }
101
102    pub fn minimum(&self) -> Result<usize> {
103        catch(|| self.view.minValue() as _)
104    }
105
106    pub fn set_minimum(&mut self, v: usize) -> Result<()> {
107        catch(|| self.view.setMinValue(v as _))
108    }
109
110    pub fn maximum(&self) -> Result<usize> {
111        catch(|| self.view.maxValue() as _)
112    }
113
114    pub fn set_maximum(&mut self, v: usize) -> Result<()> {
115        catch(|| self.view.setMaxValue(v as _))
116    }
117
118    pub fn freq(&self) -> Result<usize> {
119        let nmarks = catch(|| self.view.numberOfTickMarks() as usize)?;
120        let range = self.maximum()? - self.minimum()?;
121        Ok(range / nmarks)
122    }
123
124    pub fn set_freq(&mut self, v: usize) -> Result<()> {
125        let range = self.maximum()? - self.minimum()?;
126        catch(|| self.view.setNumberOfTickMarks((range / v) as _))
127    }
128
129    pub fn pos(&self) -> Result<usize> {
130        catch(|| self.view.doubleValue() as _)
131    }
132
133    pub fn set_pos(&mut self, pos: usize) -> Result<()> {
134        catch(|| self.view.setDoubleValue(pos as _))
135    }
136
137    pub async fn wait_change(&self) {
138        self.delegate.ivars().action.wait().await;
139    }
140}
141
142winio_handle::impl_as_widget!(Slider, handle);
143
144#[derive(Debug, Default)]
145struct SliderDelegateIvars {
146    action: Callback,
147}
148
149define_class! {
150    #[unsafe(super(NSObject))]
151    #[name = "WinioSliderDelegate"]
152    #[ivars = SliderDelegateIvars]
153    #[thread_kind = MainThreadOnly]
154    #[derive(Debug)]
155    struct SliderDelegate;
156
157    #[allow(non_snake_case)]
158    impl SliderDelegate {
159        #[unsafe(method_id(init))]
160        fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
161            let this = this.set_ivars(SliderDelegateIvars::default());
162            unsafe { msg_send![super(this), init] }
163        }
164
165        #[unsafe(method(onAction))]
166        unsafe fn onAction(&self) {
167            self.ivars().action.signal::<GlobalRuntime>(());
168        }
169    }
170}
171
172impl SliderDelegate {
173    pub fn new(mtm: MainThreadMarker) -> Retained<Self> {
174        unsafe { msg_send![mtm.alloc::<Self>(), init] }
175    }
176}