native_windows_gui2/layouts/
grid_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 GridLayoutItem {
13 control: HWND,
15
16 pub col: u32,
18
19 pub row: u32,
21
22 pub col_span: u32,
24
25 pub row_span: u32,
27}
28
29impl GridLayoutItem {
30 pub fn new<W: Into<ControlHandle>>(
32 c: W,
33 col: u32,
34 row: u32,
35 col_span: u32,
36 row_span: u32,
37 ) -> GridLayoutItem {
38 let control = c
39 .into()
40 .hwnd()
41 .expect("Child must be a window-like control (HWND handle)");
42
43 GridLayoutItem {
44 control,
45 col,
46 row,
47 col_span,
48 row_span,
49 }
50 }
51}
52
53pub struct GridLayoutInner {
56 base: HWND,
58
59 children: Vec<GridLayoutItem>,
61
62 margins: [u32; 4],
64
65 min_size: [u32; 2],
67
68 max_size: [u32; 2],
70
71 column_count: Option<u32>,
73
74 row_count: Option<u32>,
76
77 spacing: u32,
79}
80
81#[derive(Clone)]
108pub struct GridLayout {
109 inner: Rc<RefCell<GridLayoutInner>>,
110}
111
112impl GridLayout {
113 pub fn builder() -> GridLayoutBuilder {
114 let layout = GridLayoutInner {
115 base: ptr::null_mut(),
116 children: Vec::new(),
117 margins: [5, 5, 5, 5],
118 spacing: 5,
119 min_size: [0, 0],
120 max_size: [u32::max_value(), u32::max_value()],
121 column_count: None,
122 row_count: None,
123 };
124
125 GridLayoutBuilder { layout }
126 }
127
128 pub fn add_child<W: Into<ControlHandle>>(&self, col: u32, row: u32, c: W) {
137 let h = c
138 .into()
139 .hwnd()
140 .expect("Child must be a window-like control (HWND handle)");
141 let item = GridLayoutItem {
142 control: h,
143 col,
144 row,
145 col_span: 1,
146 row_span: 1,
147 };
148
149 self.add_child_item(item);
150 }
151
152 pub fn add_child_item(&self, i: GridLayoutItem) {
160 let base = {
161 let mut inner = self.inner.borrow_mut();
162 if inner.base.is_null() {
163 panic!("GridLayout is not initialized");
164 }
165
166 inner.children.push(i);
169 inner.base
170 };
171
172 let (w, h) = wh::get_window_size(base);
173 self.update_layout(w as u32, h as u32);
174 }
175
176 pub fn remove_child<W: Into<ControlHandle>>(&self, c: W) {
186 let base = {
187 let mut inner = self.inner.borrow_mut();
188 if inner.base.is_null() {
189 panic!("GridLayout is not initialized");
190 }
191
192 let handle = c
193 .into()
194 .hwnd()
195 .expect("Control must be window-like (HWND handle)");
196 let index = inner
197 .children
198 .iter()
199 .position(|item| item.control == handle);
200 match index {
201 Some(i) => {
202 inner.children.remove(i);
203 }
204 None => {
205 return;
206 }
207 }
208
209 inner.base
210 };
211
212 let (w, h) = wh::get_window_size(base);
213 self.update_layout(w as u32, h as u32);
214 }
215
216 pub fn remove_child_by_pos(&self, col: u32, row: u32) {
226 let base = {
227 let mut inner = self.inner.borrow_mut();
228 if inner.base.is_null() {
229 panic!("GridLayout is not initialized");
230 }
231
232 let index = inner
233 .children
234 .iter()
235 .position(|item| item.col == col && item.row == row);
236 match index {
237 Some(i) => {
238 inner.children.remove(i);
239 }
240 None => {}
241 }
242
243 inner.base
244 };
245
246 let (w, h) = wh::get_window_size(base);
247 self.update_layout(w as u32, h as u32);
248 }
249
250 pub fn move_child<W: Into<ControlHandle>>(&self, c: W, col: u32, row: u32) {
261 let base = {
262 let mut inner = self.inner.borrow_mut();
263 if inner.base.is_null() {
264 panic!("GridLayout is not initialized");
265 }
266
267 let handle = c
268 .into()
269 .hwnd()
270 .expect("Control must be window-like (HWND handle)");
271 let index = inner
272 .children
273 .iter()
274 .position(|item| item.control == handle);
275 match index {
276 Some(i) => {
277 let mut child = inner.children.remove(i);
278 child.col = col;
279 child.row = row;
280 inner.children.push(child);
281 }
282 None => {
283 return;
284 }
285 }
286
287 inner.base
288 };
289
290 let (w, h) = wh::get_window_size(base);
291 self.update_layout(w as u32, h as u32);
292 }
293
294 pub fn move_child_by_pos<W: Into<ControlHandle>>(
305 &self,
306 col: u32,
307 row: u32,
308 new_col: u32,
309 new_row: u32,
310 ) {
311 let base = {
312 let mut inner = self.inner.borrow_mut();
313 if inner.base.is_null() {
314 panic!("GridLayout is not initialized");
315 }
316
317 let index = inner
318 .children
319 .iter()
320 .position(|item| item.col == col && item.row == row);
321 match index {
322 Some(i) => {
323 let mut child = inner.children.remove(i);
324 child.col = new_col;
325 child.row = new_row;
326 inner.children.push(child);
327 }
328 None => {}
329 }
330
331 inner.base
332 };
333
334 let (w, h) = wh::get_window_size(base);
335 self.update_layout(w as u32, h as u32);
336 }
337
338 pub fn has_child<W: Into<ControlHandle>>(&self, c: W) -> bool {
346 let inner = self.inner.borrow();
347 if inner.base.is_null() {
348 panic!("GridLayout is not initialized");
349 }
350
351 let handle = c
352 .into()
353 .hwnd()
354 .expect("Children is not a window-like control (HWND handle)");
355 inner.children.iter().any(|c| c.control == handle)
356 }
357
358 pub fn resize(&self, w: u32, h: u32) {
367 let inner = self.inner.borrow();
368 if inner.base.is_null() {
369 panic!("Grid layout is not bound to a parent control.")
370 }
371 self.update_layout(w, h);
372 }
373
374 pub fn fit(&self) {
379 let inner = self.inner.borrow();
380 if inner.base.is_null() {
381 panic!("Grid layout is not bound to a parent control.")
382 }
383
384 let (w, h) = wh::get_window_size(inner.base);
385 self.update_layout(w, h);
386 }
387
388 pub fn margin(&self, m: [u32; 4]) {
390 let mut inner = self.inner.borrow_mut();
391 inner.margins = m;
392 }
393
394 pub fn spacing(&self, sp: u32) {
396 let mut inner = self.inner.borrow_mut();
397 inner.spacing = sp;
398 }
399
400 pub fn min_size(&self, sz: [u32; 2]) {
402 let mut inner = self.inner.borrow_mut();
403 inner.min_size = sz;
404 }
405
406 pub fn max_size(&self, sz: [u32; 2]) {
408 let mut inner = self.inner.borrow_mut();
409 inner.max_size = sz;
410 }
411
412 pub fn max_column(&self, count: Option<u32>) {
414 let mut inner = self.inner.borrow_mut();
415 inner.column_count = count;
416 }
417
418 pub fn max_row(&self, count: Option<u32>) {
420 let mut inner = self.inner.borrow_mut();
421 inner.row_count = count;
422 }
423
424 fn update_layout(&self, mut width: u32, mut height: u32) -> () {
425 let inner = self.inner.borrow();
426 if inner.base.is_null() || inner.children.len() == 0 {
427 return;
428 }
429
430 let [m_top, m_right, m_bottom, m_left] = inner.margins;
431 let sp = inner.spacing;
432
433 let children = &inner.children;
434
435 let [min_w, min_h] = inner.min_size;
436 if width < min_w {
437 width = min_w;
438 }
439 if height < min_h {
440 height = min_h;
441 }
442
443 let [max_w, max_h] = inner.max_size;
444 if width > max_w {
445 width = max_w;
446 }
447 if height > max_h {
448 height = max_h;
449 }
450
451 let column_count = match inner.column_count {
452 Some(c) => c,
453 None => children
454 .iter()
455 .map(|item| item.col + item.col_span)
456 .max()
457 .unwrap_or(1),
458 };
459
460 let row_count = match inner.row_count {
461 Some(c) => c,
462 None => children
463 .iter()
464 .map(|item| item.row + item.row_span)
465 .max()
466 .unwrap_or(1),
467 };
468
469 if width < (m_right + m_left) + ((sp * 2) * column_count) {
470 return;
471 }
472
473 if height < (m_top + m_bottom) + ((sp * 2) * row_count) {
474 return;
475 }
476
477 width = width - m_right - m_left;
479 height = height - m_top - m_bottom;
480
481 width = width - ((sp * 2) * column_count);
483 height = height - ((sp * 2) * row_count);
484
485 let item_width = width / column_count;
486 let item_height = height / row_count;
487 let sp2 = sp * 2;
488
489 let mut columns = vec![item_width; column_count as usize];
490 let mut rows = vec![item_height; row_count as usize];
491 let extra_width = width - item_width * column_count;
492 if extra_width > 0 {
493 for x in &mut columns[0..(extra_width as usize)] {
494 *x += 1;
495 }
496 }
497 let extra_height = height - item_height * row_count;
498 if extra_height > 0 {
499 for x in &mut rows[0..(extra_height as usize)] {
500 *x += 1;
501 }
502 }
503
504 let mut last_handle = None;
505 for item in inner.children.iter() {
506 let x: u32 = m_left
507 + (sp + (sp2 * item.col))
508 + columns[0..(item.col as usize)].iter().sum::<u32>();
509 let y: u32 =
510 m_top + (sp + (sp2 * item.row)) + rows[0..(item.row as usize)].iter().sum::<u32>();
511
512 let local_width: u32 = &columns
513 [(item.col as usize)..((item.col + item.col_span) as usize)]
514 .iter()
515 .sum::<u32>()
516 + (sp2 * (item.col_span - 1));
517 let local_height: u32 = &rows
518 [(item.row as usize)..((item.row + item.row_span) as usize)]
519 .iter()
520 .sum::<u32>()
521 + (sp2 * (item.row_span - 1));
522
523 wh::set_window_position(item.control, x as i32, y as i32);
524 wh::set_window_size(item.control, local_width, local_height, false);
525 wh::set_window_after(item.control, last_handle);
526
527 last_handle = Some(item.control);
528 }
529 }
530}
531
532impl Default for GridLayout {
533 fn default() -> GridLayout {
534 let inner = GridLayoutInner {
535 base: ptr::null_mut(),
536 children: Vec::new(),
537 margins: [5, 5, 5, 5],
538 min_size: [0, 0],
539 max_size: [u32::max_value(), u32::max_value()],
540 column_count: None,
541 row_count: None,
542 spacing: 5,
543 };
544
545 GridLayout {
546 inner: Rc::new(RefCell::new(inner)),
547 }
548 }
549}
550
551pub struct GridLayoutBuilder {
553 layout: GridLayoutInner,
554}
555
556impl GridLayoutBuilder {
557 pub fn parent<W: Into<ControlHandle>>(mut self, p: W) -> GridLayoutBuilder {
559 self.layout.base = p.into().hwnd().expect("Parent must be HWND");
560 self
561 }
562
563 pub fn child<W: Into<ControlHandle>>(mut self, col: u32, row: u32, c: W) -> GridLayoutBuilder {
567 let h = c.into().hwnd().expect("Child must be HWND");
568 self.layout.children.push(GridLayoutItem {
569 control: h,
570 col,
571 row,
572 col_span: 1,
573 row_span: 1,
574 });
575
576 self
577 }
578
579 pub fn child_item(mut self, item: GridLayoutItem) -> GridLayoutBuilder {
582 self.layout.children.push(item);
583 self
584 }
585
586 pub fn margin(mut self, m: [u32; 4]) -> GridLayoutBuilder {
588 self.layout.margins = m;
589 self
590 }
591
592 pub fn spacing(mut self, sp: u32) -> GridLayoutBuilder {
594 self.layout.spacing = sp;
595 self
596 }
597
598 pub fn min_size(mut self, sz: [u32; 2]) -> GridLayoutBuilder {
600 self.layout.min_size = sz;
601 self
602 }
603
604 pub fn max_size(mut self, sz: [u32; 2]) -> GridLayoutBuilder {
606 self.layout.max_size = sz;
607 self
608 }
609
610 pub fn max_column(mut self, count: Option<u32>) -> GridLayoutBuilder {
612 self.layout.column_count = count;
613 self
614 }
615
616 pub fn max_row(mut self, count: Option<u32>) -> GridLayoutBuilder {
618 self.layout.row_count = count;
619 self
620 }
621
622 pub fn build(self, layout: &GridLayout) -> Result<(), NwgError> {
625 use winapi::shared::minwindef::{HIWORD, LOWORD};
626 use winapi::um::winuser::WM_SIZE;
627
628 if self.layout.base.is_null() {
629 return Err(NwgError::layout_create(
630 "Gridlayout does not have a parent.",
631 ));
632 }
633
634 if let Some(max_row) = self.layout.row_count {
636 if let Some(item) = self.layout.children.iter().find(|c| c.row >= max_row) {
637 return Err(NwgError::layout_create(format!(
638 "A layout item row is bigger or equal than the max number of row. {} >= {}",
639 item.row, max_row
640 )));
641 }
642 }
643
644 if let Some(max_column) = self.layout.column_count {
645 if let Some(item) = self.layout.children.iter().find(|c| c.col >= max_column) {
646 return Err(NwgError::layout_create(format!(
647 "A layout item column is bigger or equal than the max number of column. {} >= {}",
648 item.col, max_column
649 )));
650 }
651 }
652
653 let (w, h) = wh::get_window_size(self.layout.base);
654 let base_handle = ControlHandle::Hwnd(self.layout.base);
655
656 {
658 let mut layout_inner = layout.inner.borrow_mut();
659 *layout_inner = self.layout;
660 }
661
662 layout.update_layout(w, h);
664
665 let event_layout = layout.clone();
667 let cb = move |_h, msg, _w, l| {
668 if msg == WM_SIZE {
669 let size = l as u32;
670 let width = LOWORD(size) as i32;
671 let height = HIWORD(size) as i32;
672 let (w, h) = crate::win32::high_dpi::physical_to_logical(width, height);
673 GridLayout::update_layout(&event_layout, w as u32, h as u32);
674 }
675 None
676 };
677
678 use std::sync::atomic::{AtomicUsize, Ordering};
680 static BOX_LAYOUT_ID: AtomicUsize = AtomicUsize::new(0x8FFF);
681 bind_raw_event_handler_inner(
682 &base_handle,
683 BOX_LAYOUT_ID.fetch_add(1, Ordering::SeqCst),
684 cb,
685 )
686 .unwrap();
687
688 Ok(())
689 }
690}