ferroui_native/
window_impl_base.rs1use crate::helpers::*;
4use crate::interop::*;
5use crate::top_level_impl::{callback_property, MacOSTopLevelHandle, TopLevelEvents, TopLevelImpl, TopLevelParent};
6use ferroui_base::platform::IPlatformSettings;
7use ferroui_base::{FerroLocator, LocatorExtensions, PixelPoint, Size};
8use ferroui_controls::platform::PlatformThemeVariant;
9use ferroui_controls::{WindowEdge, WindowResizeReason};
10use ferroui_microcom::ComPtr;
11use std::cell::RefCell;
12use std::rc::Rc;
13
14pub(crate) trait WindowBaseParent: TopLevelParent {
16 fn window_base(&self) -> &WindowBaseImpl;
18
19 fn show_core(&self, activate: bool, is_dialog: bool) {
21 self.window_base().show(activate, is_dialog);
22 }
23}
24
25pub(crate) trait WindowEventsParent: WindowBaseParent {
28 fn on_closing(&self) -> bool;
29 fn on_window_state_changed(&self, state: FrnWindowState);
30 fn on_got_input_when_disabled(&self);
31}
32
33pub struct WindowBaseImpl {
35 base: Rc<TopLevelImpl>,
36 deactivated: RefCell<Option<Rc<dyn Fn()>>>,
37 activated: RefCell<Option<Rc<dyn Fn()>>>,
38 position_changed: RefCell<Option<Rc<dyn Fn(PixelPoint)>>>,
39}
40
41impl WindowBaseImpl {
42 pub(crate) fn new(factory: ComPtr<IFerroNativeFactory>) -> WindowBaseImpl {
43 WindowBaseImpl {
44 base: TopLevelImpl::new(factory),
45 deactivated: RefCell::new(None),
46 activated: RefCell::new(None),
47 position_changed: RefCell::new(None),
48 }
49 }
50
51 pub(crate) fn top_level(&self) -> &Rc<TopLevelImpl> {
52 &self.base
53 }
54
55 pub fn native(&self) -> Option<ComPtr<IFrnWindowBase>> {
57 self.base.native_window_base()
58 }
59
60 pub fn position(&self) -> PixelPoint {
61 match self.native() {
62 Some(native) => to_ferro_pixel_point(native.get_position().check()),
63 None => PixelPoint::default(),
64 }
65 }
66
67 pub fn set_position(&self, value: PixelPoint) {
68 if let Some(native) = self.native() {
69 native.set_position(pixel_point_to_frn_point(value)).check();
70 }
71 }
72
73 callback_property!(deactivated, set_deactivated, deactivated, dyn Fn());
74 callback_property!(activated, set_activated, activated, dyn Fn());
75 callback_property!(position_changed, set_position_changed, position_changed, dyn Fn(PixelPoint));
76
77 pub fn frame_size(&self) -> Option<Size> {
78 let native = self.native()?;
79 let s = native.get_frame_size_from(FrnSize { width: -1.0, height: -1.0 }).check();
82 if s.width < 0.0 && s.height < 0.0 {
83 None
84 } else {
85 Some(Size::new(s.width, s.height))
86 }
87 }
88
89 pub(crate) fn init(&self, handle: Rc<MacOSTopLevelHandle>) {
90 self.base.init(handle);
91
92 let mut default_width = 0;
93 let mut default_height = 0;
94
95 let position = self.position();
97 let mut screens = self.base.screens().all_screens();
98 screens.sort_by(|a, b| a.scaling().partial_cmp(&b.scaling()).unwrap_or(std::cmp::Ordering::Equal));
99 let monitor = screens.into_iter().find(|m| m.bounds().contains(position));
100
101 if let Some(monitor) = monitor {
102 default_width = (monitor.working_area().width as f64 * 0.75) as i32;
104 default_height = (monitor.working_area().height as f64 * 0.7) as i32;
105 }
106
107 default_width = default_width.max(300);
108 default_height = default_height.max(200);
109
110 self.resize(Size::new(default_width as f64, default_height as f64), WindowResizeReason::Layout);
111 }
112
113 pub fn activate(&self) {
114 if let Some(native) = self.native() {
115 native.activate().check();
116 }
117 }
118
119 pub fn resize(&self, client_size: Size, reason: WindowResizeReason) {
120 if let Some(native) = self.native() {
121 native.resize(client_size.width, client_size.height, to_frn_resize_reason(reason)).check();
122 }
123 }
124
125 pub fn set_frame_theme_variant(&self, theme_variant: Option<PlatformThemeVariant>) {
126 let theme_variant = match theme_variant {
129 Some(theme_variant) => theme_variant as i32,
130 None => FerroLocator::current()
131 .get_service::<dyn IPlatformSettings>()
132 .map_or(PlatformThemeVariant::Light as i32, |settings| {
133 settings.get_color_values().theme_variant() as i32
134 }),
135 };
136 if let Some(native) = self.native() {
137 native.set_frame_theme_variant(FrnPlatformThemeVariant(theme_variant)).check();
138 }
139 }
140
141 pub(crate) fn dispose(&self) {
142 if let Some(native) = self.native() {
143 native.close().check();
144 }
145 self.base.dispose();
146 }
147
148 pub fn show(&self, activate: bool, is_dialog: bool) {
149 if let Some(native) = self.native() {
150 native.show(activate, is_dialog).check();
151 }
152 }
153
154 pub fn hide(&self) {
155 if let Some(native) = self.native() {
156 native.hide().check();
157 }
158 }
159
160 pub fn begin_move_drag(&self) {
161 if let Some(native) = self.native() {
162 native.begin_move_drag().check();
163 }
164 }
165
166 pub fn max_auto_size_hint(&self) -> Size {
167 let mut best: Option<Size> = None;
169 for screen in self.base.screens().all_screens() {
170 let size = screen.bounds().size().to_size(1.0);
171 if best.is_none_or(|best| size.width + size.height > best.width + best.height) {
172 best = Some(size);
173 }
174 }
175 best.unwrap_or_default()
176 }
177
178 pub fn set_topmost(&self, value: bool) {
179 if let Some(native) = self.native() {
180 native.set_top_most(value).check();
181 }
182 }
183
184 pub fn begin_resize_drag(&self, _edge: WindowEdge) {}
186
187 pub fn set_min_max_size(&self, min_size: Size, max_size: Size) {
188 if let Some(native) = self.native() {
189 native.set_min_max_size(to_frn_size(min_size), to_frn_size(max_size)).check();
190 }
191 }
192}
193
194impl<P: WindowBaseParent> IFrnWindowBaseEventsImpl for TopLevelEvents<P> {
195 fn activated(&self) {
196 crate::callback_base::guard((), || {
197 let activated = self.0.window_base().activated();
198 if let Some(activated) = activated {
199 activated();
200 }
201 })
202 }
203
204 fn deactivated(&self) {
205 crate::callback_base::guard((), || {
206 let deactivated = self.0.window_base().deactivated();
207 if let Some(deactivated) = deactivated {
208 deactivated();
209 }
210 })
211 }
212
213 fn position_changed(&self, position: FrnPoint) {
214 crate::callback_base::guard((), || {
215 let position_changed = self.0.window_base().position_changed();
216 if let Some(position_changed) = position_changed {
217 position_changed(to_ferro_pixel_point(position));
218 }
219 })
220 }
221}
222
223impl<P: WindowEventsParent> IFrnWindowEventsImpl for TopLevelEvents<P> {
224 fn closing(&self) -> bool {
225 crate::callback_base::guard(true, || self.0.on_closing())
226 }
227
228 fn window_state_changed(&self, state: FrnWindowState) {
229 crate::callback_base::guard((), || self.0.on_window_state_changed(state))
230 }
231
232 fn got_input_when_disabled(&self) {
233 crate::callback_base::guard((), || self.0.on_got_input_when_disabled())
234 }
235}
236
237macro_rules! impl_window_base_contract {
239 ($ty:ty) => {
240 impl ferroui_controls::platform::IWindowBaseImpl for $ty {
241 fn frame_size(&self) -> Option<ferroui_base::Size> {
242 $crate::window_impl_base::WindowBaseParent::window_base(self).frame_size()
243 }
244
245 fn show(&self, activate: bool, is_dialog: bool) {
246 $crate::window_impl_base::WindowBaseParent::show_core(self, activate, is_dialog)
247 }
248
249 fn hide(&self) {
250 $crate::window_impl_base::WindowBaseParent::window_base(self).hide()
251 }
252
253 fn position(&self) -> ferroui_base::PixelPoint {
254 $crate::window_impl_base::WindowBaseParent::window_base(self).position()
255 }
256
257 fn position_changed(&self) -> Option<std::rc::Rc<dyn Fn(ferroui_base::PixelPoint)>> {
258 $crate::window_impl_base::WindowBaseParent::window_base(self).position_changed()
259 }
260
261 fn set_position_changed(&self, value: Option<std::rc::Rc<dyn Fn(ferroui_base::PixelPoint)>>) {
262 $crate::window_impl_base::WindowBaseParent::window_base(self).set_position_changed(value)
263 }
264
265 fn activate(&self) {
266 $crate::window_impl_base::WindowBaseParent::window_base(self).activate()
267 }
268
269 fn deactivated(&self) -> Option<std::rc::Rc<dyn Fn()>> {
270 $crate::window_impl_base::WindowBaseParent::window_base(self).deactivated()
271 }
272
273 fn set_deactivated(&self, value: Option<std::rc::Rc<dyn Fn()>>) {
274 $crate::window_impl_base::WindowBaseParent::window_base(self).set_deactivated(value)
275 }
276
277 fn activated(&self) -> Option<std::rc::Rc<dyn Fn()>> {
278 $crate::window_impl_base::WindowBaseParent::window_base(self).activated()
279 }
280
281 fn set_activated(&self, value: Option<std::rc::Rc<dyn Fn()>>) {
282 $crate::window_impl_base::WindowBaseParent::window_base(self).set_activated(value)
283 }
284
285 fn max_auto_size_hint(&self) -> ferroui_base::Size {
286 $crate::window_impl_base::WindowBaseParent::window_base(self).max_auto_size_hint()
287 }
288
289 fn set_topmost(&self, value: bool) {
290 $crate::window_impl_base::WindowBaseParent::window_base(self).set_topmost(value)
291 }
292 }
293 };
294}
295pub(crate) use impl_window_base_contract;