use crate::controls::ControlHandle;
use crate::win32::window::bind_raw_event_handler_inner;
use crate::win32::window_helper as wh;
use crate::NwgError;
use winapi::shared::windef::{HWND};
use std::rc::Rc;
use std::cell::RefCell;
use std::ptr;
#[derive(Debug)]
pub struct GridLayoutItem {
control: HWND,
pub col: u32,
pub row: u32,
pub col_span: u32,
pub row_span: u32
}
impl GridLayoutItem {
pub fn new<W: Into<ControlHandle>>(c: W, col: u32, row: u32, col_span: u32, row_span: u32) -> GridLayoutItem {
let control = c.into().hwnd().expect("Child must be a window-like control (HWND handle)");
GridLayoutItem {
control,
col,
row,
col_span,
row_span
}
}
}
pub struct GridLayoutInner {
base: HWND,
children: Vec<GridLayoutItem>,
margins: [u32; 4],
min_size: [u32; 2],
max_size: [u32; 2],
column_count: Option<u32>,
row_count: Option<u32>,
spacing: u32
}
#[derive(Clone)]
pub struct GridLayout {
inner: Rc<RefCell<GridLayoutInner>>
}
impl GridLayout {
pub fn builder() -> GridLayoutBuilder {
let layout = GridLayoutInner {
base: ptr::null_mut(),
children: Vec::new(),
margins: [5, 5, 5, 5],
spacing: 5,
min_size: [0, 0],
max_size: [u32::max_value(), u32::max_value()],
column_count: None,
row_count: None
};
GridLayoutBuilder { layout }
}
pub fn add_child<W: Into<ControlHandle>>(&self, col: u32, row: u32, c: W) {
let h = c.into().hwnd().expect("Child must be a window-like control (HWND handle)");
let item = GridLayoutItem {
control: h,
col,
row,
col_span: 1,
row_span: 1,
};
self.add_child_item(item);
}
pub fn add_child_item(&self, i: GridLayoutItem) {
let base = {
let mut inner = self.inner.borrow_mut();
if inner.base.is_null() {
panic!("GridLayout is not initialized");
}
inner.children.push(i);
inner.base
};
let (w, h) = unsafe { wh::get_window_size(base) };
self.update_layout(w as u32, h as u32);
}
pub fn remove_child<W: Into<ControlHandle>>(&self, c: W) {
let base = {
let mut inner = self.inner.borrow_mut();
if inner.base.is_null() {
panic!("GridLayout is not initialized");
}
let handle = c.into().hwnd().expect("Control must be window-like (HWND handle)");
let index = inner.children.iter().position(|item| item.control == handle);
match index {
Some(i) => { inner.children.remove(i); },
None => { return; }
}
inner.base
};
let (w, h) = unsafe { wh::get_window_size(base) };
self.update_layout(w as u32, h as u32);
}
pub fn remove_child_by_pos(&self, col: u32, row: u32) {
let base = {
let mut inner = self.inner.borrow_mut();
if inner.base.is_null() {
panic!("GridLayout is not initialized");
}
let index = inner.children.iter().position(|item| item.col == col && item.row == row);
match index {
Some(i) => { inner.children.remove(i); },
None => {}
}
inner.base
};
let (w, h) = unsafe { wh::get_window_size(base) };
self.update_layout(w as u32, h as u32);
}
pub fn move_child<W: Into<ControlHandle>>(&self, c: W, col: u32, row: u32) {
let base = {
let mut inner = self.inner.borrow_mut();
if inner.base.is_null() {
panic!("GridLayout is not initialized");
}
let handle = c.into().hwnd().expect("Control must be window-like (HWND handle)");
let index = inner.children.iter().position(|item| item.control == handle);
match index {
Some(i) => {
let mut child = inner.children.remove(i);
child.col = col;
child.row = row;
inner.children.push(child);
}
None => { return; }
}
inner.base
};
let (w, h) = unsafe { wh::get_window_size(base) };
self.update_layout(w as u32, h as u32);
}
pub fn move_child_by_pos<W: Into<ControlHandle>>(&self, col: u32, row: u32, new_col: u32, new_row: u32) {
let base = {
let mut inner = self.inner.borrow_mut();
if inner.base.is_null() {
panic!("GridLayout is not initialized");
}
let index = inner.children.iter().position(|item| item.col == col && item.row == row);
match index {
Some(i) => {
let mut child = inner.children.remove(i);
child.col = new_col;
child.row = new_row;
inner.children.push(child);
},
None => {}
}
inner.base
};
let (w, h) = unsafe { wh::get_window_size(base) };
self.update_layout(w as u32, h as u32);
}
pub fn has_child<W: Into<ControlHandle>>(&self, c: W) -> bool {
let inner = self.inner.borrow();
if inner.base.is_null() {
panic!("GridLayout is not initialized");
}
let handle = c.into().hwnd().expect("Children is not a window-like control (HWND handle)");
inner.children.iter().any(|c| c.control == handle )
}
pub fn resize(&self, w: u32, h: u32) {
let inner = self.inner.borrow();
if inner.base.is_null() {
panic!("Grid layout is not bound to a parent control.")
}
self.update_layout(w, h);
}
pub fn fit(&self) {
let inner = self.inner.borrow();
if inner.base.is_null() {
panic!("Grid layout is not bound to a parent control.")
}
let (w, h) = unsafe { wh::get_window_size(inner.base) };
self.update_layout(w, h);
}
pub fn margin(&self, m: [u32; 4]) {
let mut inner = self.inner.borrow_mut();
inner.margins = m;
}
pub fn spacing(&self, sp: u32) {
let mut inner = self.inner.borrow_mut();
inner.spacing = sp;
}
pub fn min_size(&self, sz: [u32; 2]) {
let mut inner = self.inner.borrow_mut();
inner.min_size = sz;
}
pub fn max_size(&self, sz: [u32; 2]) {
let mut inner = self.inner.borrow_mut();
inner.max_size = sz;
}
pub fn max_column(&self, count: Option<u32>) {
let mut inner = self.inner.borrow_mut();
inner.column_count = count;
}
pub fn max_row(&self, count: Option<u32>) {
let mut inner = self.inner.borrow_mut();
inner.row_count = count;
}
fn update_layout(&self, mut width: u32, mut height: u32) -> () {
let inner = self.inner.borrow();
if inner.base.is_null() || inner.children.len() == 0 {
return;
}
let [m_top, m_right, m_bottom, m_left] = inner.margins;
let sp = inner.spacing;
let children = &inner.children;
let [min_w, min_h] = inner.min_size;
if width < min_w { width = min_w; }
if height < min_h { height = min_h; }
let [max_w, max_h] = inner.max_size;
if width > max_w { width = max_w; }
if height > max_h { height = max_h; }
let column_count = match inner.column_count {
Some(c) => c,
None => children.iter().map(|item| item.col + item.col_span).max().unwrap_or(1)
};
let row_count = match inner.row_count {
Some(c) => c,
None => children.iter().map(|item| item.row + item.row_span).max().unwrap_or(1)
};
if width < (m_right + m_left) + ((sp * 2) * column_count) {
return;
}
if height < (m_top + m_bottom) + ((sp * 2) * row_count) {
return;
}
width = width - m_right - m_left;
height = height - m_top - m_bottom;
width = width - ((sp * 2) * column_count);
height = height - ((sp * 2) * row_count);
let item_width = width / column_count;
let item_height = height / row_count;
let sp2 = sp * 2;
let mut columns = vec![item_width; column_count as usize];
let mut rows = vec![item_height; row_count as usize];
let extra_width = width - item_width * column_count;
if extra_width > 0 {
for x in &mut columns[0..(extra_width as usize)] {
*x += 1;
}
}
let extra_height = height - item_height * row_count;
if extra_height > 0 {
for x in &mut rows[0..(extra_height as usize)] {
*x += 1;
}
}
let mut last_handle = None;
for item in inner.children.iter() {
let x = m_left + (sp + (sp2 * item.col)) + &columns[0..(item.col as usize)].iter().sum();
let y = m_top + (sp + (sp2 * item.row)) + &rows[0..(item.row as usize)].iter().sum();
let local_width = &columns[(item.col as usize)..((item.col + item.col_span) as usize)].iter().sum() + (sp2 * (item.col_span - 1));
let local_height = &rows[(item.row as usize)..((item.row + item.row_span) as usize)].iter().sum() + (sp2 * (item.row_span - 1));
unsafe {
wh::set_window_position(item.control, x as i32, y as i32);
wh::set_window_size(item.control, local_width, local_height, false);
wh::set_window_after(item.control, last_handle)
}
last_handle = Some(item.control);
}
}
}
impl Default for GridLayout {
fn default() -> GridLayout {
let inner = GridLayoutInner {
base: ptr::null_mut(),
children: Vec::new(),
margins: [5, 5, 5, 5],
min_size: [0, 0],
max_size: [u32::max_value(), u32::max_value()],
column_count: None,
row_count: None,
spacing: 5,
};
GridLayout {
inner: Rc::new(RefCell::new(inner))
}
}
}
pub struct GridLayoutBuilder {
layout: GridLayoutInner
}
impl GridLayoutBuilder {
pub fn parent<W: Into<ControlHandle>>(mut self, p: W) -> GridLayoutBuilder {
self.layout.base = p.into().hwnd().expect("Parent must be HWND");
self
}
pub fn child<W: Into<ControlHandle>>(mut self, col: u32, row: u32, c: W) -> GridLayoutBuilder {
let h = c.into().hwnd().expect("Child must be HWND");
self.layout.children.push(GridLayoutItem {
control: h,
col,
row,
col_span: 1,
row_span: 1,
});
self
}
pub fn child_item(mut self, item: GridLayoutItem) -> GridLayoutBuilder {
self.layout.children.push(item);
self
}
pub fn margin(mut self, m: [u32; 4]) -> GridLayoutBuilder {
self.layout.margins = m;
self
}
pub fn spacing(mut self, sp: u32) -> GridLayoutBuilder {
self.layout.spacing = sp;
self
}
pub fn min_size(mut self, sz: [u32; 2]) -> GridLayoutBuilder {
self.layout.min_size = sz;
self
}
pub fn max_size(mut self, sz: [u32; 2]) -> GridLayoutBuilder {
self.layout.max_size = sz;
self
}
pub fn max_column(mut self, count: Option<u32>) -> GridLayoutBuilder {
self.layout.column_count = count;
self
}
pub fn max_row(mut self, count: Option<u32>) -> GridLayoutBuilder {
self.layout.row_count = count;
self
}
pub fn build(self, layout: &GridLayout) -> Result<(), NwgError> {
use winapi::um::winuser::WM_SIZE;
use winapi::shared::minwindef::{HIWORD, LOWORD};
if self.layout.base.is_null() {
return Err(NwgError::layout_create("Gridlayout does not have a parent."));
}
if let Some(max_row) = self.layout.row_count {
if let Some(item) = self.layout.children.iter().find(|c| c.row >= max_row) {
return Err(NwgError::layout_create(format!("A layout item row is bigger or equal than the max number of row. {} >= {}", item.row, max_row)));
}
}
if let Some(max_column) = self.layout.column_count {
if let Some(item) = self.layout.children.iter().find(|c| c.col >= max_column) {
return Err(NwgError::layout_create(format!("A layout item column is bigger or equal than the max number of column. {} >= {}", item.col, max_column)));
}
}
let (w, h) = unsafe { wh::get_window_size(self.layout.base) };
let base_handle = ControlHandle::Hwnd(self.layout.base);
{
let mut layout_inner = layout.inner.borrow_mut();
*layout_inner = self.layout;
}
layout.update_layout(w, h);
let event_layout = layout.clone();
let cb = move |_h, msg, _w, l| {
if msg == WM_SIZE {
let size = l as u32;
let width = LOWORD(size) as i32;
let height = HIWORD(size) as i32;
let (w, h) = unsafe { crate::win32::high_dpi::physical_to_logical(width, height) };
GridLayout::update_layout(&event_layout, w as u32, h as u32);
}
None
};
static mut BOX_LAYOUT_ID: usize = 0x8FFF;
bind_raw_event_handler_inner(&base_handle, unsafe { BOX_LAYOUT_ID += 1; BOX_LAYOUT_ID }, cb).unwrap();
Ok(())
}
}