use crate::kurbo::{Point, Rect, Size};
use crate::{
BaseState, BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, PaintCtx, UpdateCtx, Widget,
WidgetPod,
Window, WindowType, WindowBox, widget::{WidgetBox, WidgetId, WidgetType},
};
pub struct Row;
pub struct Column;
type MaxWidgets = heapless::consts::U8;
type Vec<T> = heapless::Vec::<T, MaxWidgets>;
#[derive(Clone)]
pub struct Flex<T: Data + 'static + Default> {
id: WidgetId,
direction: Axis,
children: Vec<ChildWidget<T>>,
}
#[derive(Clone)]
struct ChildWidget<T: Data + 'static + Default> {
widget: WidgetPod<T, WidgetBox<T>>,
params: Params,
}
#[derive(Clone, Copy)]
pub enum Axis {
Horizontal,
Vertical,
}
#[derive(Copy, Clone, Default)]
struct Params {
flex: f64,
}
impl Axis {
fn major(&self, coords: Size) -> f64 {
match *self {
Axis::Horizontal => coords.width,
Axis::Vertical => coords.height,
}
}
fn minor(&self, coords: Size) -> f64 {
match *self {
Axis::Horizontal => coords.height,
Axis::Vertical => coords.width,
}
}
fn pack(&self, major: f64, minor: f64) -> (f64, f64) {
match *self {
Axis::Horizontal => (major, minor),
Axis::Vertical => (minor, major),
}
}
}
impl Row {
pub fn new<T: Data + 'static + Default>() -> Flex<T> {
Flex {
id: super::get_widget_id(),
direction: Axis::Horizontal,
children: Vec::new(),
}
}
}
impl Column {
pub fn new<T: Data + 'static + Default>() -> Flex<T> {
Flex {
id: super::get_widget_id(),
direction: Axis::Vertical,
children: Vec::new(),
}
}
}
impl<T: Data + 'static + Default> Flex<T> {
pub fn add_child<W: Widget<T> + Clone>(&mut self, child: W, flex: f64) {
let params = Params { flex };
let child = ChildWidget {
widget: WidgetPod::new(
WidgetBox::<T>::new(child)
),
params,
};
self.children.push(child)
.expect("add child fail");
}
}
impl<T: Data + 'static + Default> Widget<T> for Flex<T> {
fn paint(&mut self, paint_ctx: &mut PaintCtx, _base_state: &BaseState, data: &T, env: &Env) {
for child in &mut self.children {
child.widget.paint_with_offset(paint_ctx, data, env);
}
}
fn layout(
&mut self,
layout_ctx: &mut LayoutCtx,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> Size {
bc.debug_check("Flex");
let mut total_non_flex = 0.0;
let mut minor = self.direction.minor(bc.min());
for child in &mut self.children {
if child.params.flex == 0.0 {
let child_bc = match self.direction {
Axis::Horizontal => BoxConstraints::new(
Size::new(0.0, bc.min().height),
Size::new(core::f64::INFINITY, bc.max.height),
),
Axis::Vertical => BoxConstraints::new(
Size::new(bc.min().width, 0.0),
Size::new(bc.max().width, core::f64::INFINITY),
),
};
let child_size = child.widget.layout(layout_ctx, &child_bc, data, env);
minor = minor.max(self.direction.minor(child_size));
total_non_flex += self.direction.major(child_size);
let rect = Rect::from_origin_size(Point::ORIGIN, child_size);
child.widget.set_layout_rect(rect);
}
}
let total_major = self.direction.major(bc.max());
let remaining = (total_major - total_non_flex).max(0.0);
let flex_sum: f64 = self.children.iter().map(|child| child.params.flex).sum();
for child in &mut self.children {
if child.params.flex != 0.0 {
let major = remaining * child.params.flex / flex_sum;
let min_major = if major.is_infinite() { 0.0 } else { major };
let child_bc = match self.direction {
Axis::Horizontal => BoxConstraints::new(
Size::new(min_major, bc.min().height),
Size::new(major, bc.max().height),
),
Axis::Vertical => BoxConstraints::new(
Size::new(bc.min().width, min_major),
Size::new(bc.max().width, major),
),
};
let child_size = child.widget.layout(layout_ctx, &child_bc, data, env);
minor = minor.max(self.direction.minor(child_size));
let rect = Rect::from_origin_size(Point::ORIGIN, child_size);
child.widget.set_layout_rect(rect);
}
}
let mut major = 0.0;
for child in &mut self.children {
let rect = child.widget.get_layout_rect();
let pos: Point = self.direction.pack(major, 0.0).into();
child.widget.set_layout_rect(rect.with_origin(pos));
major += self.direction.major(rect.size());
}
if flex_sum > 0.0 && total_major.is_infinite() {
}
if flex_sum > 0.0 {
major = total_major;
}
let (width, height) = self.direction.pack(major, minor);
Size::new(width, height)
}
fn event(&mut self, ctx: &mut EventCtx<T>, event: &Event, data: &mut T, env: &Env) {
for child in &mut self.children {
child.widget.event(ctx, event, data, env);
}
}
fn update(&mut self, ctx: &mut UpdateCtx<T>, _old_data: Option<&T>, data: &T, env: &Env) {
for child in &mut self.children {
child.widget.update(ctx, data, env);
}
}
fn to_type(self) -> WidgetType<T> {
WidgetType::Flex(self)
}
fn new_window(self) -> WindowBox<T> {
let window = Window::new(self);
let window_box = WindowBox(
WindowType::Flex(window),
);
window_box
}
fn get_id(self) -> WidgetId {
self.id
}
}
impl<T: Data + Default> core::fmt::Debug for ChildWidget<T> {
fn fmt(&self, _fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
Ok(())
}
}