native_windows_gui2/layouts/
dyn_layout.rs1use crate::NwgError;
2use crate::controls::ControlHandle;
3use crate::win32::window::bind_raw_event_handler_inner;
4use crate::win32::window_helper as wh;
5use std::cell::RefCell;
6use std::ptr;
7use std::rc::Rc;
8use winapi::shared::windef::HWND;
9
10#[derive(Debug)]
12pub struct DynLayoutItem {
13 control: HWND,
15 pos_init: (i32, i32),
16 size_init: (i32, i32),
17 mv: (i32, i32),
18 sz: (i32, i32),
19}
20
21impl DynLayoutItem {
22 pub fn new<W: Into<ControlHandle>>(c: W, mv: (i32, i32), sz: (i32, i32)) -> DynLayoutItem {
24 let control = c
25 .into()
26 .hwnd()
27 .expect("Child must be a window-like control (HWND handle)");
28 let pos_init = (0, 0);
29 let size_init = (0, 0);
30
31 DynLayoutItem {
32 control,
33 pos_init,
34 size_init,
35 mv,
36 sz,
37 }
38 }
39}
40
41pub struct DynLayoutInner {
44 base: HWND,
46
47 children: Vec<DynLayoutItem>,
49}
50
51#[derive(Clone)]
52pub struct DynLayout {
53 inner: Rc<RefCell<DynLayoutInner>>,
54}
55
56impl DynLayout {
57 pub fn builder() -> DynLayoutBuilder {
58 let layout = DynLayoutInner {
59 base: ptr::null_mut(),
60 children: Vec::new(),
61 };
62
63 DynLayoutBuilder { layout }
64 }
65
66 pub fn parent<W: Into<ControlHandle>>(&self, p: W) {
68 let mut inner = self.inner.borrow_mut();
69 inner.base = p.into().hwnd().expect("Parent must be HWND");
70 }
71
72 pub fn add_child<W: Into<ControlHandle>>(&self, m: (i32, i32), s: (i32, i32), c: W) {
81 let hwnd = c
82 .into()
83 .hwnd()
84 .expect("Child must be a window-like control (HWND handle)");
85 let pos = wh::get_window_position(hwnd);
86 let size = wh::get_window_size(hwnd);
87
88 let (whost, hhost) = wh::get_window_size(self.inner.borrow_mut().base);
89
90 let xdelta = 0.01 * whost as f32;
91 let ydelta = 0.01 * hhost as f32;
92
93 let mut xpos = pos.0;
94 if m.0 > 0 {
95 xpos -= (xdelta * m.0 as f32) as i32;
96 }
97
98 let mut ypos = pos.1;
99 if m.1 > 0 {
100 ypos -= (ydelta * m.1 as f32) as i32;
101 }
102
103 let mut xsize = size.0 as i32;
104 if s.0 > 0 {
105 xsize -= (xdelta * s.0 as f32) as i32;
106 }
107
108 let mut ysize = size.1 as i32;
109 if s.1 > 0 {
110 ysize -= (ydelta * s.1 as f32) as i32;
111 }
112
113 let item = DynLayoutItem {
114 control: hwnd,
115 pos_init: (xpos, ypos),
116 size_init: (xsize, ysize),
117 mv: m,
118 sz: s,
119 };
120
121 self.add_child_item(item);
122 }
123
124 pub fn add_child_item(&self, i: DynLayoutItem) {
132 let base = {
133 let mut inner = self.inner.borrow_mut();
134 if inner.base.is_null() {
135 panic!("DynLayout is not initialized");
136 }
137
138 inner.children.push(i);
141 inner.base
142 };
143
144 let (w, h) = wh::get_window_size(base);
145 self.update_layout(w as u32, h as u32);
146 }
147
148 pub fn remove_child<W: Into<ControlHandle>>(&self, c: W) {
158 let base = {
159 let mut inner = self.inner.borrow_mut();
160 if inner.base.is_null() {
161 panic!("DynLayout is not initialized");
162 }
163
164 let handle = c
165 .into()
166 .hwnd()
167 .expect("Control must be window-like (HWND handle)");
168 let index = inner
169 .children
170 .iter()
171 .position(|item| item.control == handle);
172 match index {
173 Some(i) => {
174 inner.children.remove(i);
175 }
176 None => {
177 return;
178 }
179 }
180
181 inner.base
182 };
183
184 let (w, h) = wh::get_window_size(base);
185 self.update_layout(w as u32, h as u32);
186 }
187
188 pub fn has_child<W: Into<ControlHandle>>(&self, c: W) -> bool {
196 let inner = self.inner.borrow();
197 if inner.base.is_null() {
198 panic!("DynLayout is not initialized");
199 }
200
201 let handle = c
202 .into()
203 .hwnd()
204 .expect("Children is not a window-like control (HWND handle)");
205 inner.children.iter().any(|c| c.control == handle)
206 }
207
208 pub fn resize(&self, w: u32, h: u32) {
217 let inner = self.inner.borrow();
218 if inner.base.is_null() {
219 panic!("Layout is not bound to a parent control.")
220 }
221 self.update_layout(w, h);
222 }
223
224 pub fn fit(&self) {
229 let inner = self.inner.borrow();
230 if inner.base.is_null() {
231 panic!("Layout is not bound to a parent control.")
232 }
233
234 let (w, h) = wh::get_window_size(inner.base);
235 self.update_layout(w, h);
236 }
237
238 fn update_layout(&self, width: u32, height: u32) -> () {
239 use winapi::ctypes::c_int;
240 use winapi::um::winuser::{BeginDeferWindowPos, DeferWindowPos, EndDeferWindowPos};
241 use winapi::um::winuser::{
242 HWND_TOP, SWP_NOACTIVATE, SWP_NOCOPYBITS, SWP_NOREPOSITION, SWP_NOZORDER,
243 };
244
245 let inner = self.inner.borrow();
246 if inner.base.is_null() || inner.children.len() == 0 {
247 return;
248 }
249
250 let xdelta = 0.01 * width as f32;
251 let ydelta = 0.01 * height as f32;
252
253 unsafe {
254 let hdwp = BeginDeferWindowPos(inner.children.len() as c_int);
255
256 let mut last_handle = None;
257 for item in inner.children.iter() {
258 let mut x = item.pos_init.0;
259 if item.mv.0 > 0 {
260 x += (xdelta * item.mv.0 as f32) as i32;
261 }
262
263 let mut y = item.pos_init.1;
264 if item.mv.1 > 0 {
265 y += (ydelta * item.mv.1 as f32) as i32;
266 }
267
268 let mut w = item.size_init.0;
269 if item.sz.0 > 0 {
270 w += (xdelta * item.sz.0 as f32) as i32;
271 }
272
273 let mut h = item.size_init.1;
274 if item.sz.1 > 0 {
275 h += (ydelta * item.sz.1 as f32) as i32;
276 }
277
278 DeferWindowPos(
279 hdwp,
280 item.control,
281 HWND_TOP,
282 x,
283 y,
284 w,
285 h,
286 SWP_NOZORDER | SWP_NOREPOSITION | SWP_NOACTIVATE | SWP_NOCOPYBITS,
287 );
288
289 wh::set_window_after(item.control, last_handle);
290 last_handle = Some(item.control);
291 }
292
293 EndDeferWindowPos(hdwp);
294 }
295 }
296}
297
298impl Default for DynLayout {
299 fn default() -> DynLayout {
300 let inner = DynLayoutInner {
301 base: ptr::null_mut(),
302 children: Vec::new(),
303 };
304
305 DynLayout {
306 inner: Rc::new(RefCell::new(inner)),
307 }
308 }
309}
310
311pub struct DynLayoutBuilder {
313 layout: DynLayoutInner,
314}
315
316impl DynLayoutBuilder {
317 pub fn parent<W: Into<ControlHandle>>(mut self, p: W) -> DynLayoutBuilder {
319 self.layout.base = p.into().hwnd().expect("Parent must be HWND");
320 self
321 }
322
323 pub fn child<W: Into<ControlHandle>>(
327 mut self,
328 m: (i32, i32),
329 s: (i32, i32),
330 c: W,
331 ) -> DynLayoutBuilder {
332 let hwnd = c.into().hwnd().expect("Child must be HWND");
333 let pos = wh::get_window_position(hwnd);
334 let size = wh::get_window_size(hwnd);
335
336 self.layout.children.push(DynLayoutItem {
337 control: hwnd,
338 pos_init: pos,
339 size_init: (size.0 as i32, size.1 as i32),
340 mv: m,
341 sz: s,
342 });
343
344 self
345 }
346
347 pub fn child_item(mut self, item: DynLayoutItem) -> DynLayoutBuilder {
350 self.layout.children.push(item);
351 self
352 }
353
354 pub fn build(self, layout: &DynLayout) -> Result<(), NwgError> {
357 use winapi::shared::minwindef::{HIWORD, LOWORD};
358 use winapi::um::winuser::WM_SIZE;
359
360 if self.layout.base.is_null() {
361 return Err(NwgError::layout_create("DynLayout does not have a parent."));
362 }
363
364 let (w, h) = wh::get_window_size(self.layout.base);
365 let base_handle = ControlHandle::Hwnd(self.layout.base);
366
367 {
369 let mut layout_inner = layout.inner.borrow_mut();
370 *layout_inner = self.layout;
371 }
372
373 layout.update_layout(w, h);
375
376 let event_layout = layout.clone();
378 let cb = move |_h, msg, _w, l| {
379 if msg == WM_SIZE {
380 let size = l as u32;
381 let width = LOWORD(size) as i32;
382 let height = HIWORD(size) as i32;
383 let (w, h) = crate::win32::high_dpi::physical_to_logical(width, height);
384 DynLayout::update_layout(&event_layout, w as u32, h as u32);
385 }
386 None
387 };
388
389 use std::sync::atomic::{AtomicUsize, Ordering};
391 static BOX_LAYOUT_ID: AtomicUsize = AtomicUsize::new(0x8FFF);
392 bind_raw_event_handler_inner(
393 &base_handle,
394 BOX_LAYOUT_ID.fetch_add(1, Ordering::SeqCst),
395 cb,
396 )
397 .unwrap();
398
399 Ok(())
400 }
401}