use crate::controls::ControlHandle;
use crate::win32::window_helper as wh;
use crate::win32::window::{RawEventHandler, unbind_raw_event_handler, bind_raw_event_handler_inner};
use crate::NwgError;
use winapi::shared::windef::HWND;
use std::{ptr, rc::Rc, cell::{RefCell, RefMut, Ref} };
use stretch::{
number::Number,
geometry::{Point, Size, Rect},
node::{Node, Stretch},
style::*
};
#[derive(Debug)]
pub struct FlexboxLayoutItem {
control: HWND,
style: Style,
}
pub enum FlexboxLayoutChild {
Item(FlexboxLayoutItem),
Flexbox(FlexboxLayout)
}
impl FlexboxLayoutChild {
fn modify_style<F>(&mut self, fnc: F) where F: Fn(&mut Style) {
match self {
FlexboxLayoutChild::Item(item) => fnc(&mut item.style),
FlexboxLayoutChild::Flexbox(layout) => fnc(&mut layout.inner.borrow_mut().style),
}
}
}
struct FlexboxLayoutInner {
base: HWND,
handler: Option<RawEventHandler>,
style: Style,
children: Vec<FlexboxLayoutChild>,
parent_layout: Option<FlexboxLayout>,
}
#[derive(Clone)]
pub struct FlexboxLayout {
inner: Rc<RefCell<FlexboxLayoutInner>>
}
impl FlexboxLayout {
pub fn builder() -> FlexboxLayoutBuilder {
let layout = FlexboxLayoutInner {
base: ptr::null_mut(),
handler: None,
style: Default::default(),
children: Vec::new(),
parent_layout: None,
};
FlexboxLayoutBuilder { layout, current_index: None, auto_size: true, auto_spacing: Some(5) }
}
pub fn style(&self) -> Style {
let inner = self.inner.borrow();
if inner.base.is_null() {
panic!("Flexbox layout is not yet initialized!");
}
inner.style.clone()
}
pub fn set_style(&self, style: Style) {
let mut inner = self.inner.borrow_mut();
if inner.base.is_null() {
panic!("Flexbox layout is not yet initialized!");
}
inner.style = style;
}
pub fn add_child<W: Into<ControlHandle>>(&self, c: W, style: Style) -> Result<(), stretch::Error> {
{
let mut inner = self.inner.borrow_mut();
if inner.base.is_null() {
panic!("Flexbox layout is not yet initialized!");
}
let item = FlexboxLayoutItem {
control: c.into().hwnd().expect("Control must be window like (HWND handle)"),
style
};
inner.children.push(FlexboxLayoutChild::Item(item));
}
self.fit()
}
pub fn remove_child<W: Into<ControlHandle>>(&self, c: W) {
let mut inner = self.inner.borrow_mut();
if inner.base.is_null() {
panic!("Flexbox layout is not yet initialized!");
}
let handle = c.into().hwnd().expect("Control must be window like (HWND handle)");
let index = inner.children.iter()
.position(|child| child.is_item() && child.as_item().control == handle);
match index {
Some(i) => { inner.children.remove(i); },
None => { panic!("Control was not found in layout"); }
}
}
pub fn has_child<W: Into<ControlHandle>>(&self, c: W) -> bool {
let inner = self.inner.borrow();
if inner.base.is_null() {
panic!("Flexbox layout is not yet initialized!");
}
let handle = c.into().hwnd().expect("Control must be window like (HWND handle)");
inner.children.iter().any(|child| child.is_item() && child.as_item().control == handle)
}
pub fn children(&self) -> FlexboxLayoutChildren {
let inner = self.inner.borrow();
if inner.base.is_null() {
panic!("Flexbox layout is not yet initialized!");
}
FlexboxLayoutChildren {
inner
}
}
pub fn children_mut(&self) -> FlexboxLayoutChildrenMut {
let inner = self.inner.borrow_mut();
if inner.base.is_null() {
panic!("Flexbox layout is not yet initialized!");
}
FlexboxLayoutChildrenMut {
inner
}
}
pub fn fit(&self) -> Result<(), stretch::Error> {
let inner = self.inner.borrow();
if inner.base.is_null() {
panic!("FlexboxLayout is not bound to a parent control.")
}
if let Some(parent_layout) = &inner.parent_layout {
parent_layout.fit()
}
else {
let (w, h) = unsafe { wh::get_window_size(inner.base) };
self.update_layout(w, h, (0, 0))
}
}
fn build_child_nodes(children: &Vec<FlexboxLayoutChild>, stretch: &mut Stretch) -> Result<(usize, Vec<Node>), stretch::Error> {
let mut nodes = Vec::new();
let mut item_count = 0;
for child in children.iter() {
match child {
FlexboxLayoutChild::Item(child) =>{
nodes.push(stretch.new_node(child.style, Vec::new())?);
item_count += 1;
},
FlexboxLayoutChild::Flexbox(child) => {
let (child_count, child_nodes) = FlexboxLayout::build_child_nodes(child.children().children(), stretch)?;
nodes.push(stretch.new_node(child.style(), child_nodes)?);
item_count += child_count;
},
};
}
Ok((item_count, nodes))
}
fn apply_layout_deferred(positioner: &mut wh::DeferredWindowPositioner, stretch: &mut Stretch, nodes: Vec<Node>, children: &Vec<FlexboxLayoutChild>, last_handle: &mut Option<HWND>, offset: (i32, i32)) -> Result<(), stretch::Error> {
use FlexboxLayoutChild as Child;
for (node, child) in nodes.into_iter().zip(children.iter()) {
let layout = stretch.layout(node)?;
let Point { x, y } = layout.location;
let Size { width, height } = layout.size;
match child {
Child::Item(child) => {
positioner.defer_pos(child.control, last_handle.unwrap_or(std::ptr::null_mut()), x as i32 + offset.0, y as i32 + offset.1, width as i32, height as i32).ok();
last_handle.replace(child.control);
},
Child::Flexbox(child) => {
let children_nodes = stretch.children(node)?;
FlexboxLayout::apply_layout_deferred(positioner, stretch, children_nodes, child.children().children(), last_handle, (x as i32, y as i32))?;
}
}
}
Ok(())
}
fn apply_layout_immediate(stretch: &mut Stretch, nodes: Vec<Node>, children: &Vec<FlexboxLayoutChild>, last_handle: &mut Option<HWND>, offset: (i32, i32)) -> Result<(), stretch::Error> {
use FlexboxLayoutChild as Child;
for (node, child) in nodes.into_iter().zip(children.iter()) {
let layout = stretch.layout(node)?;
let Point { x, y } = layout.location;
let Size { width, height } = layout.size;
match child {
Child::Item(child) => unsafe {
wh::set_window_position(child.control, x as i32 + offset.0, y as i32 + offset.1);
wh::set_window_size(child.control, width as u32, height as u32, false);
wh::set_window_after(child.control, *last_handle);
last_handle.replace(child.control);
},
Child::Flexbox(child) => {
let children_nodes = stretch.children(node)?;
FlexboxLayout::apply_layout_immediate(stretch, children_nodes, child.children().children(), last_handle, (x as i32, y as i32))?;
}
}
}
Ok(())
}
fn update_layout(&self, width: u32, height: u32, offset: (i32, i32)) -> Result<(), stretch::Error> {
let inner = self.inner.borrow();
if inner.base.is_null() || inner.children.len() == 0 {
return Ok(());
}
let mut stretch = Stretch::new();
let (item_count, nodes) = FlexboxLayout::build_child_nodes(&inner.children, &mut stretch)?;
let mut style = inner.style.clone();
style.size = Size { width: Dimension::Points(width as f32), height: Dimension::Points(height as f32) };
let node = stretch.new_node(style, nodes.clone())?;
stretch.compute_layout(node, Size::undefined())?;
if let Ok(mut positioner) = wh::DeferredWindowPositioner::new(item_count as i32) {
let layout_result = FlexboxLayout::apply_layout_deferred(&mut positioner, &mut stretch, nodes, self.children().children(), &mut None, offset);
positioner.end();
layout_result
}
else {
FlexboxLayout::apply_layout_immediate(&mut stretch, nodes, self.children().children(), &mut None, offset)
}
}
}
pub struct FlexboxLayoutBuilder {
layout: FlexboxLayoutInner,
current_index: Option<usize>,
auto_size: bool,
auto_spacing: Option<u32>
}
impl FlexboxLayoutBuilder {
pub fn parent<W: Into<ControlHandle>>(mut self, p: W) -> FlexboxLayoutBuilder {
self.layout.base = p.into().hwnd().expect("Parent must be HWND");
self
}
pub fn child<W: Into<ControlHandle>>(mut self, child: W) -> FlexboxLayoutBuilder {
self.current_index = Some(self.layout.children.len());
let item = FlexboxLayoutItem {
control: child.into().hwnd().unwrap(),
style: Style::default()
};
self.layout.children.push(FlexboxLayoutChild::Item(item));
self
}
pub fn child_layout(mut self, child: &FlexboxLayout) -> FlexboxLayoutBuilder {
self.current_index = Some(self.layout.children.len());
self.layout.children.push(FlexboxLayoutChild::Flexbox(child.clone()));
self
}
pub fn auto_size(mut self, auto: bool) -> FlexboxLayoutBuilder {
self.auto_size = auto;
self
}
pub fn auto_spacing(mut self, auto: Option<u32>) -> FlexboxLayoutBuilder {
self.auto_spacing = auto;
self
}
pub fn direction(mut self, value: Direction) -> FlexboxLayoutBuilder {
self.layout.style.direction = value;
self
}
pub fn flex_direction(mut self, value: FlexDirection) -> FlexboxLayoutBuilder {
self.layout.style.flex_direction = value;
self
}
pub fn flex_wrap(mut self, value: FlexWrap) -> FlexboxLayoutBuilder {
self.layout.style.flex_wrap = value;
self
}
pub fn overflow(mut self, value: Overflow) -> FlexboxLayoutBuilder {
self.layout.style.overflow = value;
self
}
pub fn align_items(mut self, value: AlignItems) -> FlexboxLayoutBuilder {
self.layout.style.align_items = value;
self
}
pub fn align_content(mut self, value: AlignContent) -> FlexboxLayoutBuilder {
self.layout.style.align_content = value;
self
}
pub fn justify_content(mut self, value: JustifyContent) -> FlexboxLayoutBuilder {
self.layout.style.justify_content = value;
self
}
pub fn padding(mut self, value: Rect<Dimension>) -> FlexboxLayoutBuilder {
self.layout.style.padding = value;
self.auto_spacing = None;
self
}
pub fn border(mut self, value: Rect<Dimension>) -> FlexboxLayoutBuilder {
self.layout.style.border = value;
self
}
pub fn min_size(mut self, value: Size<Dimension>) -> FlexboxLayoutBuilder {
self.layout.style.min_size = value;
self
}
pub fn max_size(mut self, value: Size<Dimension>) -> FlexboxLayoutBuilder {
self.layout.style.max_size = value;
self
}
pub fn aspect_ratio(mut self, value: Number) -> FlexboxLayoutBuilder {
self.layout.style.aspect_ratio = value;
self
}
pub fn child_size(mut self, size: Size<Dimension>) -> FlexboxLayoutBuilder {
self.modify_current_child_style(|s| s.size = size);
self.auto_size = false;
self
}
pub fn child_position(mut self, position: Rect<Dimension>) -> FlexboxLayoutBuilder {
self.modify_current_child_style(|s| s.position = position);
self
}
pub fn child_margin(mut self, value: Rect<Dimension>) -> FlexboxLayoutBuilder {
self.modify_current_child_style(|s| s.margin = value);
self.auto_spacing = None;
self
}
pub fn child_min_size(mut self, value: Size<Dimension>) -> FlexboxLayoutBuilder {
self.modify_current_child_style(|s| s.min_size = value);
self.auto_size = false;
self
}
pub fn child_max_size(mut self, value: Size<Dimension>) -> FlexboxLayoutBuilder {
self.modify_current_child_style(|s| s.max_size = value);
self.auto_size = false;
self
}
pub fn child_flex_grow(mut self, value: f32) -> FlexboxLayoutBuilder {
self.modify_current_child_style(|s| s.flex_grow = value);
self.auto_size = false;
self
}
pub fn child_flex_shrink(mut self, value: f32) -> FlexboxLayoutBuilder {
self.modify_current_child_style(|s| s.flex_shrink = value);
self.auto_size = false;
self
}
pub fn child_flex_basis(mut self, value: Dimension) -> FlexboxLayoutBuilder {
self.modify_current_child_style(|s| s.flex_basis = value);
self.auto_size = false;
self
}
pub fn child_align_self(mut self, value: AlignSelf) -> FlexboxLayoutBuilder {
self.modify_current_child_style(|s| s.align_self = value);
self
}
pub fn style(mut self, style: Style) -> FlexboxLayoutBuilder {
self.modify_current_child_style(|s| *s = style);
self
}
fn modify_current_child_style<F>(&mut self, fnc: F) where F: Fn(&mut Style) {
assert!(self.current_index.is_some(), "No current children");
let index = self.current_index.unwrap();
self.layout.children[index].modify_style(|s| fnc(s));
}
pub fn build(mut self, layout: &FlexboxLayout) -> 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("Flexboxlayout does not have a parent."));
}
let (w, h) = unsafe { wh::get_window_size(self.layout.base) };
let base_handle = ControlHandle::Hwnd(self.layout.base);
if self.auto_size {
let children_count = self.layout.children.len();
let size = 1.0f32 / (children_count as f32);
for child in self.layout.children.iter_mut() {
let child_size = match &self.layout.style.flex_direction {
FlexDirection::Row | FlexDirection::RowReverse => {
Size { width: Dimension::Percent(size), height: Dimension::Auto }
},
FlexDirection::Column | FlexDirection::ColumnReverse => {
Size { width: Dimension::Auto, height: Dimension::Percent(size) }
}
};
child.modify_style(|s| s.size = child_size);
}
}
if let Some(spacing) = self.auto_spacing {
let spacing = Dimension::Points(spacing as f32);
let spacing = Rect { start: spacing, end: spacing, top: spacing, bottom: spacing};
self.layout.style.padding = spacing;
for child in self.layout.children.iter_mut() {
child.modify_style(|s| s.margin = spacing);
}
}
{
let mut layout_inner = layout.inner.borrow_mut();
if layout_inner.handler.is_some() {
drop(unbind_raw_event_handler(layout_inner.handler.as_ref().unwrap()));
}
*layout_inner = self.layout;
}
for child in layout.inner.borrow_mut().children.iter_mut() {
match child {
FlexboxLayoutChild::Item(_) => {},
FlexboxLayoutChild::Flexbox(child_layout) => {
child_layout.inner.borrow_mut().parent_layout.replace(layout.clone());
},
}
}
layout.update_layout(w, h, (0, 0)).expect("Failed to compute layout");
use std::sync::atomic::{AtomicUsize, Ordering};
static FLEX_LAYOUT_ID: AtomicUsize = AtomicUsize::new(0x9FFF);
let handler_id = FLEX_LAYOUT_ID.fetch_add(1, Ordering::SeqCst);
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) };
FlexboxLayout::update_layout(&event_layout, w as u32, h as u32, (0, 0)).expect("Failed to compute layout!");
}
None
};
{
let mut layout_inner = layout.inner.borrow_mut();
layout_inner.handler = Some(bind_raw_event_handler_inner(&base_handle, handler_id, cb).unwrap());
}
Ok(())
}
pub fn build_partial(mut self, layout: &FlexboxLayout) -> Result<(), NwgError> {
if self.layout.base.is_null() {
return Err(NwgError::layout_create("Flexboxlayout does not have a parent."));
}
if self.auto_size {
let children_count = self.layout.children.len();
let size = 1.0f32 / (children_count as f32);
for child in self.layout.children.iter_mut() {
let child_size = match &self.layout.style.flex_direction {
FlexDirection::Row | FlexDirection::RowReverse => {
Size { width: Dimension::Percent(size), height: Dimension::Auto }
},
FlexDirection::Column | FlexDirection::ColumnReverse => {
Size { width: Dimension::Auto, height: Dimension::Percent(size) }
}
};
child.modify_style(|s| s.size = child_size);
}
}
if let Some(spacing) = self.auto_spacing {
let spacing = Dimension::Points(spacing as f32);
let spacing = Rect { start: spacing, end: spacing, top: spacing, bottom: spacing};
self.layout.style.padding = spacing;
for child in self.layout.children.iter_mut() {
child.modify_style(|s| s.margin = spacing);
}
}
{
let mut layout_inner = layout.inner.borrow_mut();
if layout_inner.handler.is_some() {
drop(unbind_raw_event_handler(layout_inner.handler.as_ref().unwrap()));
}
*layout_inner = self.layout;
}
Ok(())
}
}
impl Default for FlexboxLayout {
fn default() -> FlexboxLayout {
let inner = FlexboxLayoutInner {
base: ptr::null_mut(),
handler: None,
children: Vec::new(),
style: Default::default(),
parent_layout: None,
};
FlexboxLayout {
inner: Rc::new(RefCell::new(inner))
}
}
}
impl FlexboxLayoutChild {
pub fn is_item(&self) -> bool {
match self {
FlexboxLayoutChild::Item(_) => true,
_ => false
}
}
pub fn as_item<'a>(&'a self) -> &'a FlexboxLayoutItem {
match self {
FlexboxLayoutChild::Item(i) => i,
_ => panic!("FlexboxLayoutChild is not an item")
}
}
pub fn as_item_mut<'a>(&'a mut self) -> &'a mut FlexboxLayoutItem {
match self {
FlexboxLayoutChild::Item(i) => i,
_ => panic!("FlexboxLayoutChild is not an item")
}
}
pub fn is_flexbox(&self) -> bool {
match self {
FlexboxLayoutChild::Flexbox(_) => true,
_ => false
}
}
}
pub struct FlexboxLayoutChildrenMut<'a> {
inner: RefMut<'a, FlexboxLayoutInner>
}
impl<'a> FlexboxLayoutChildrenMut<'a> {
pub fn children<'b>(&'b mut self) -> &'b mut Vec<FlexboxLayoutChild> {
&mut self.inner.children
}
}
pub struct FlexboxLayoutChildren<'a> {
inner: Ref<'a, FlexboxLayoutInner>
}
impl<'a> FlexboxLayoutChildren<'a> {
pub fn children<'b>(&'b self) -> &'b Vec<FlexboxLayoutChild> {
&self.inner.children
}
}