use crate::drawing::{Drawable, Plan};
use crate::layouts::{HasPlanner, Layout};
use anyhow::bail;
use skia_safe::{Canvas, Point, Rect};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BoxArrangement {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
Center,
}
#[derive(Clone)]
pub struct BoxLayoutElement {
pub drawable: Rc<dyn Drawable>,
pub arrangement: BoxArrangement,
pub offset: Point,
}
pub struct BoxLayout {
w: RefCell<Option<f32>>,
h: RefCell<Option<f32>>,
pub childs: RefCell<BTreeMap<String, BoxLayoutElement>>,
pub planner: RefCell<Option<Rc<dyn Layout>>>,
}
impl HasPlanner for BoxLayout {
fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
&self.planner
}
fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
*self.planner.borrow_mut() = p;
}
}
impl Drawable for BoxLayout {
fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
for (_key, element) in self.childs.borrow().iter() {
if let Err(e) = element.drawable.draw(canvas) {
println!("{:?}", e);
}
}
Ok(())
}
fn get_horizontal_plan(&self) -> Plan {
match *self.w.borrow() {
Some(w) => Plan::Fit(w),
None => Plan::Fill,
}
}
fn get_vertical_plan(&self) -> Plan {
match *self.h.borrow() {
Some(h) => Plan::Fit(h),
None => Plan::Fill,
}
}
}
impl Layout for BoxLayout {
fn measure_child(&self, c: &dyn Drawable, canvas: &Canvas) -> Rect {
let parent_area = self.get_planned_drawing_area(canvas);
let (arrangement, offset) = {
let childs = self.childs.borrow();
let target_ptr = c as *const _ as *const ();
let found = childs
.values()
.find(|el| (el.drawable.as_ref() as *const _ as *const ()) == target_ptr);
match found {
Some(el) => (el.arrangement, el.offset),
None => return Rect::new(0.0, 0.0, 0.0, 0.0),
}
};
let h_plan = c.get_horizontal_plan();
let v_plan = c.get_vertical_plan();
let child_w = match h_plan {
Plan::Fit(w) => w,
_ => parent_area.width(),
};
let child_h = match v_plan {
Plan::Fit(h) => h,
_ => parent_area.height(),
};
let (mut x, mut y) = match arrangement {
BoxArrangement::TopLeft => (parent_area.left, parent_area.top),
BoxArrangement::TopRight => (parent_area.right - child_w, parent_area.top),
BoxArrangement::BottomLeft => (parent_area.left, parent_area.bottom - child_h),
BoxArrangement::BottomRight => {
(parent_area.right - child_w, parent_area.bottom - child_h)
}
BoxArrangement::Center => (
parent_area.left + (parent_area.width() - child_w) / 2.0,
parent_area.top + (parent_area.height() - child_h) / 2.0,
),
};
x += offset.x;
y += offset.y;
Rect::from_xywh(x, y, child_w, child_h)
}
}
impl Default for BoxLayout {
fn default() -> Self {
Self::new()
}
}
impl BoxLayout {
pub fn new() -> Self {
Self {
w: RefCell::new(None),
h: RefCell::new(None),
childs: RefCell::new(BTreeMap::new()),
planner: RefCell::new(None),
}
}
pub fn set_w(&self, w: Option<f32>) {
*self.w.borrow_mut() = w;
}
pub fn set_h(&self, h: Option<f32>) {
*self.h.borrow_mut() = h;
}
pub fn add_child(self: &Rc<Self>, id: &str, e: BoxLayoutElement) {
e.drawable.set_planner(Some(self.clone()));
self.childs.borrow_mut().insert(id.to_string(), e);
}
pub fn remove_child(&self, id: &str) {
if let Some(c) = self.lookup_child(id) {
c.drawable.set_planner(None);
self.childs.borrow_mut().remove(id);
}
}
pub fn lookup_child(&self, id: &str) -> Option<BoxLayoutElement> {
self.childs.borrow().get(id).cloned()
}
#[allow(clippy::too_many_arguments)]
pub fn rewrite_child(&self, id: &str, r: BoxArrangement, p: Point) -> anyhow::Result<()> {
if let Some(v) = self.childs.borrow_mut().get_mut(id) {
v.arrangement = r;
v.offset = p;
return Ok(());
}
bail!("Id not found")
}
}