native_windows_gui/layouts/
grid_layout.rs1use crate::controls::ControlHandle;
2use crate::win32::window::bind_raw_event_handler_inner;
3use crate::win32::window_helper as wh;
4use crate::NwgError;
5use winapi::shared::windef::{HWND};
6use std::rc::Rc;
7use std::cell::RefCell;
8use std::ptr;
9
10
11#[derive(Debug)]
13pub struct GridLayoutItem {
14 control: HWND,
16
17 pub col: u32,
19
20 pub row: u32,
22
23 pub col_span: u32,
25
26 pub row_span: u32
28}
29
30impl GridLayoutItem {
31
32 pub fn new<W: Into<ControlHandle>>(c: W, col: u32, row: u32, col_span: u32, row_span: u32) -> GridLayoutItem {
34 let control = c.into().hwnd().expect("Child must be a window-like control (HWND handle)");
35
36 GridLayoutItem {
37 control,
38 col,
39 row,
40 col_span,
41 row_span
42 }
43 }
44
45}
46
47
48pub struct GridLayoutInner {
51 base: HWND,
53
54 children: Vec<GridLayoutItem>,
56
57 margins: [u32; 4],
59
60 min_size: [u32; 2],
62
63 max_size: [u32; 2],
65
66 column_count: Option<u32>,
68
69 row_count: Option<u32>,
71
72 spacing: u32
74}
75
76#[derive(Clone)]
103pub struct GridLayout {
104 inner: Rc<RefCell<GridLayoutInner>>
105}
106
107impl GridLayout {
108
109 pub fn builder() -> GridLayoutBuilder {
110 let layout = GridLayoutInner {
111 base: ptr::null_mut(),
112 children: Vec::new(),
113 margins: [5, 5, 5, 5],
114 spacing: 5,
115 min_size: [0, 0],
116 max_size: [u32::max_value(), u32::max_value()],
117 column_count: None,
118 row_count: None
119 };
120
121 GridLayoutBuilder { layout }
122 }
123
124 pub fn add_child<W: Into<ControlHandle>>(&self, col: u32, row: u32, c: W) {
133 let h = c.into().hwnd().expect("Child must be a window-like control (HWND handle)");
134 let item = GridLayoutItem {
135 control: h,
136 col,
137 row,
138 col_span: 1,
139 row_span: 1,
140 };
141
142 self.add_child_item(item);
143 }
144
145 pub fn add_child_item(&self, i: GridLayoutItem) {
153 let base = {
154 let mut inner = self.inner.borrow_mut();
155 if inner.base.is_null() {
156 panic!("GridLayout is not initialized");
157 }
158
159 inner.children.push(i);
162 inner.base
163 };
164
165
166 let (w, h) = unsafe { wh::get_window_size(base) };
167 self.update_layout(w as u32, h as u32);
168 }
169
170 pub fn remove_child<W: Into<ControlHandle>>(&self, c: W) {
180 let base = {
181 let mut inner = self.inner.borrow_mut();
182 if inner.base.is_null() {
183 panic!("GridLayout is not initialized");
184 }
185
186 let handle = c.into().hwnd().expect("Control must be window-like (HWND handle)");
187 let index = inner.children.iter().position(|item| item.control == handle);
188 match index {
189 Some(i) => { inner.children.remove(i); },
190 None => { return; }
191 }
192
193 inner.base
194 };
195
196
197 let (w, h) = unsafe { wh::get_window_size(base) };
198 self.update_layout(w as u32, h as u32);
199 }
200
201 pub fn remove_child_by_pos(&self, col: u32, row: u32) {
211 let base = {
212 let mut inner = self.inner.borrow_mut();
213 if inner.base.is_null() {
214 panic!("GridLayout is not initialized");
215 }
216
217 let index = inner.children.iter().position(|item| item.col == col && item.row == row);
218 match index {
219 Some(i) => { inner.children.remove(i); },
220 None => {}
221 }
222
223 inner.base
224 };
225
226
227 let (w, h) = unsafe { wh::get_window_size(base) };
228 self.update_layout(w as u32, h as u32);
229 }
230
231
232 pub fn move_child<W: Into<ControlHandle>>(&self, c: W, col: u32, row: u32) {
243 let base = {
244 let mut inner = self.inner.borrow_mut();
245 if inner.base.is_null() {
246 panic!("GridLayout is not initialized");
247 }
248
249 let handle = c.into().hwnd().expect("Control must be window-like (HWND handle)");
250 let index = inner.children.iter().position(|item| item.control == handle);
251 match index {
252 Some(i) => {
253 let mut child = inner.children.remove(i);
254 child.col = col;
255 child.row = row;
256 inner.children.push(child);
257 }
258 None => { return; }
259 }
260
261 inner.base
262 };
263
264
265 let (w, h) = unsafe { wh::get_window_size(base) };
266 self.update_layout(w as u32, h as u32);
267 }
268
269 pub fn move_child_by_pos<W: Into<ControlHandle>>(&self, col: u32, row: u32, new_col: u32, new_row: u32) {
280 let base = {
281 let mut inner = self.inner.borrow_mut();
282 if inner.base.is_null() {
283 panic!("GridLayout is not initialized");
284 }
285
286 let index = inner.children.iter().position(|item| item.col == col && item.row == row);
287 match index {
288 Some(i) => {
289 let mut child = inner.children.remove(i);
290 child.col = new_col;
291 child.row = new_row;
292 inner.children.push(child);
293 },
294 None => {}
295 }
296
297 inner.base
298 };
299
300
301 let (w, h) = unsafe { wh::get_window_size(base) };
302 self.update_layout(w as u32, h as u32);
303 }
304
305 pub fn has_child<W: Into<ControlHandle>>(&self, c: W) -> bool {
313 let inner = self.inner.borrow();
314 if inner.base.is_null() {
315 panic!("GridLayout is not initialized");
316 }
317
318 let handle = c.into().hwnd().expect("Children is not a window-like control (HWND handle)");
319 inner.children.iter().any(|c| c.control == handle )
320 }
321
322 pub fn resize(&self, w: u32, h: u32) {
331 let inner = self.inner.borrow();
332 if inner.base.is_null() {
333 panic!("Grid layout is not bound to a parent control.")
334 }
335 self.update_layout(w, h);
336 }
337
338 pub fn fit(&self) {
343 let inner = self.inner.borrow();
344 if inner.base.is_null() {
345 panic!("Grid layout is not bound to a parent control.")
346 }
347
348 let (w, h) = unsafe { wh::get_window_size(inner.base) };
349 self.update_layout(w, h);
350 }
351
352 pub fn margin(&self, m: [u32; 4]) {
354 let mut inner = self.inner.borrow_mut();
355 inner.margins = m;
356 }
357
358 pub fn spacing(&self, sp: u32) {
360 let mut inner = self.inner.borrow_mut();
361 inner.spacing = sp;
362 }
363
364 pub fn min_size(&self, sz: [u32; 2]) {
366 let mut inner = self.inner.borrow_mut();
367 inner.min_size = sz;
368 }
369
370 pub fn max_size(&self, sz: [u32; 2]) {
372 let mut inner = self.inner.borrow_mut();
373 inner.max_size = sz;
374 }
375
376 pub fn max_column(&self, count: Option<u32>) {
378 let mut inner = self.inner.borrow_mut();
379 inner.column_count = count;
380 }
381
382 pub fn max_row(&self, count: Option<u32>) {
384 let mut inner = self.inner.borrow_mut();
385 inner.row_count = count;
386 }
387
388 fn update_layout(&self, mut width: u32, mut height: u32) -> () {
389 let inner = self.inner.borrow();
390 if inner.base.is_null() || inner.children.len() == 0 {
391 return;
392 }
393
394 let [m_top, m_right, m_bottom, m_left] = inner.margins;
395 let sp = inner.spacing;
396
397 let children = &inner.children;
398
399 let [min_w, min_h] = inner.min_size;
400 if width < min_w { width = min_w; }
401 if height < min_h { height = min_h; }
402
403 let [max_w, max_h] = inner.max_size;
404 if width > max_w { width = max_w; }
405 if height > max_h { height = max_h; }
406
407 let column_count = match inner.column_count {
408 Some(c) => c,
409 None => children.iter().map(|item| item.col + item.col_span).max().unwrap_or(1)
410 };
411
412 let row_count = match inner.row_count {
413 Some(c) => c,
414 None => children.iter().map(|item| item.row + item.row_span).max().unwrap_or(1)
415 };
416
417 if width < (m_right + m_left) + ((sp * 2) * column_count) {
418 return;
419 }
420
421 if height < (m_top + m_bottom) + ((sp * 2) * row_count) {
422 return;
423 }
424
425 width = width - m_right - m_left;
427 height = height - m_top - m_bottom;
428
429 width = width - ((sp * 2) * column_count);
431 height = height - ((sp * 2) * row_count);
432
433 let item_width = width / column_count;
434 let item_height = height / row_count;
435 let sp2 = sp * 2;
436
437 let mut columns = vec![item_width; column_count as usize];
438 let mut rows = vec![item_height; row_count as usize];
439 let extra_width = width - item_width * column_count;
440 if extra_width > 0 {
441 for x in &mut columns[0..(extra_width as usize)] {
442 *x += 1;
443 }
444 }
445 let extra_height = height - item_height * row_count;
446 if extra_height > 0 {
447 for x in &mut rows[0..(extra_height as usize)] {
448 *x += 1;
449 }
450 }
451
452 let mut last_handle = None;
453 for item in inner.children.iter() {
454 let x: u32 = m_left + (sp + (sp2 * item.col)) + columns[0..(item.col as usize)].iter().sum::<u32>();
455 let y: u32 = m_top + (sp + (sp2 * item.row)) + rows[0..(item.row as usize)].iter().sum::<u32>();
456
457 let local_width: u32 = &columns[(item.col as usize)..((item.col + item.col_span) as usize)].iter().sum::<u32>() + (sp2 * (item.col_span - 1));
458 let local_height: u32 = &rows[(item.row as usize)..((item.row + item.row_span) as usize)].iter().sum::<u32>() + (sp2 * (item.row_span - 1));
459
460 unsafe {
461 wh::set_window_position(item.control, x as i32, y as i32);
462 wh::set_window_size(item.control, local_width, local_height, false);
463 wh::set_window_after(item.control, last_handle)
464 }
465
466 last_handle = Some(item.control);
467 }
468 }
469}
470
471impl Default for GridLayout {
472
473 fn default() -> GridLayout {
474 let inner = GridLayoutInner {
475 base: ptr::null_mut(),
476 children: Vec::new(),
477 margins: [5, 5, 5, 5],
478 min_size: [0, 0],
479 max_size: [u32::max_value(), u32::max_value()],
480 column_count: None,
481 row_count: None,
482 spacing: 5,
483 };
484
485 GridLayout {
486 inner: Rc::new(RefCell::new(inner))
487 }
488 }
489
490}
491
492
493pub struct GridLayoutBuilder {
495 layout: GridLayoutInner
496}
497
498impl GridLayoutBuilder {
499
500 pub fn parent<W: Into<ControlHandle>>(mut self, p: W) -> GridLayoutBuilder {
502 self.layout.base = p.into().hwnd().expect("Parent must be HWND");
503 self
504 }
505
506 pub fn child<W: Into<ControlHandle>>(mut self, col: u32, row: u32, c: W) -> GridLayoutBuilder {
510 let h = c.into().hwnd().expect("Child must be HWND");
511 self.layout.children.push(GridLayoutItem {
512 control: h,
513 col,
514 row,
515 col_span: 1,
516 row_span: 1,
517 });
518
519 self
520 }
521
522 pub fn child_item(mut self, item: GridLayoutItem) -> GridLayoutBuilder {
525 self.layout.children.push(item);
526 self
527 }
528
529 pub fn margin(mut self, m: [u32; 4]) -> GridLayoutBuilder {
531 self.layout.margins = m;
532 self
533 }
534
535 pub fn spacing(mut self, sp: u32) -> GridLayoutBuilder {
537 self.layout.spacing = sp;
538 self
539 }
540
541 pub fn min_size(mut self, sz: [u32; 2]) -> GridLayoutBuilder {
543 self.layout.min_size = sz;
544 self
545 }
546
547 pub fn max_size(mut self, sz: [u32; 2]) -> GridLayoutBuilder {
549 self.layout.max_size = sz;
550 self
551 }
552
553 pub fn max_column(mut self, count: Option<u32>) -> GridLayoutBuilder {
555 self.layout.column_count = count;
556 self
557 }
558
559 pub fn max_row(mut self, count: Option<u32>) -> GridLayoutBuilder {
561 self.layout.row_count = count;
562 self
563 }
564
565 pub fn build(self, layout: &GridLayout) -> Result<(), NwgError> {
568 use winapi::um::winuser::WM_SIZE;
569 use winapi::shared::minwindef::{HIWORD, LOWORD};
570
571 if self.layout.base.is_null() {
572 return Err(NwgError::layout_create("Gridlayout does not have a parent."));
573 }
574
575 if let Some(max_row) = self.layout.row_count {
577 if let Some(item) = self.layout.children.iter().find(|c| c.row >= max_row) {
578 return Err(NwgError::layout_create(format!("A layout item row is bigger or equal than the max number of row. {} >= {}", item.row, max_row)));
579 }
580 }
581
582 if let Some(max_column) = self.layout.column_count {
583 if let Some(item) = self.layout.children.iter().find(|c| c.col >= max_column) {
584 return Err(NwgError::layout_create(format!("A layout item column is bigger or equal than the max number of column. {} >= {}", item.col, max_column)));
585 }
586 }
587
588 let (w, h) = unsafe { wh::get_window_size(self.layout.base) };
589 let base_handle = ControlHandle::Hwnd(self.layout.base);
590
591 {
593 let mut layout_inner = layout.inner.borrow_mut();
594 *layout_inner = self.layout;
595 }
596
597 layout.update_layout(w, h);
599
600 let event_layout = layout.clone();
602 let cb = move |_h, msg, _w, l| {
603 if msg == WM_SIZE {
604 let size = l as u32;
605 let width = LOWORD(size) as i32;
606 let height = HIWORD(size) as i32;
607 let (w, h) = unsafe { crate::win32::high_dpi::physical_to_logical(width, height) };
608 GridLayout::update_layout(&event_layout, w as u32, h as u32);
609 }
610 None
611 };
612
613 use std::sync::atomic::{AtomicUsize, Ordering};
615 static BOX_LAYOUT_ID: AtomicUsize = AtomicUsize::new(0x8FFF);
616 bind_raw_event_handler_inner(&base_handle, BOX_LAYOUT_ID.fetch_add(1, Ordering::SeqCst), cb).unwrap();
617
618 Ok(())
619 }
620
621}