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