#![allow(clippy::wrong_self_convention)]
use super::{Align, AlignHints, AlignPair, AxisInfo, SizeRules};
use super::{DynRowStorage, RowPositionSolver, RowSetter, RowSolver, RowStorage};
use super::{GridChildInfo, GridDimensions, GridSetter, GridSolver, GridStorage};
use super::{RulesSetter, RulesSolver};
use crate::draw::color::Rgb;
use crate::event::ConfigCx;
use crate::geom::{Coord, Offset, Rect, Size};
use crate::theme::{Background, DrawCx, FrameStyle, MarginStyle, SizeCx};
use crate::Id;
use crate::{dir::Directional, dir::Directions, Layout};
use std::iter::ExactSizeIterator;
pub trait Visitable {
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules;
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect);
fn find_id(&mut self, coord: Coord) -> Option<Id>;
fn draw(&mut self, draw: DrawCx);
}
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
pub struct Visitor<'a> {
layout: LayoutType<'a>,
}
enum LayoutType<'a> {
BoxComponent(Box<dyn Visitable + 'a>),
Single(&'a mut dyn Layout),
AlignSingle(&'a mut dyn Layout, AlignHints),
Align(Box<Visitor<'a>>, AlignHints),
Pack(Box<Visitor<'a>>, &'a mut PackStorage, AlignHints),
Margins(Box<Visitor<'a>>, Directions, MarginStyle),
Frame(Box<Visitor<'a>>, &'a mut FrameStorage, FrameStyle),
Button(Box<Visitor<'a>>, &'a mut FrameStorage, Option<Rgb>),
}
impl<'a> Visitor<'a> {
pub fn single(widget: &'a mut dyn Layout) -> Self {
let layout = LayoutType::Single(widget);
Visitor { layout }
}
pub fn align_single(widget: &'a mut dyn Layout, hints: AlignHints) -> Self {
let layout = LayoutType::AlignSingle(widget, hints);
Visitor { layout }
}
pub fn align(layout: Self, hints: AlignHints) -> Self {
let layout = LayoutType::Align(Box::new(layout), hints);
Visitor { layout }
}
pub fn pack(stor: &'a mut PackStorage, layout: Self, hints: AlignHints) -> Self {
let layout = LayoutType::Pack(Box::new(layout), stor, hints);
Visitor { layout }
}
pub fn margins(layout: Self, dirs: Directions, margins: MarginStyle) -> Self {
let layout = LayoutType::Margins(Box::new(layout), dirs, margins);
Visitor { layout }
}
pub fn frame(data: &'a mut FrameStorage, child: Self, style: FrameStyle) -> Self {
let layout = LayoutType::Frame(Box::new(child), data, style);
Visitor { layout }
}
pub fn button(data: &'a mut FrameStorage, child: Self, color: Option<Rgb>) -> Self {
let layout = LayoutType::Button(Box::new(child), data, color);
Visitor { layout }
}
pub fn list<I, D, S>(list: I, direction: D, data: &'a mut S) -> Self
where
I: ExactSizeIterator<Item = Visitor<'a>> + 'a,
D: Directional,
S: RowStorage,
{
let layout = LayoutType::BoxComponent(Box::new(List {
children: list,
direction,
data,
}));
Visitor { layout }
}
pub fn slice<W, D>(slice: &'a mut [W], direction: D, data: &'a mut DynRowStorage) -> Self
where
W: Layout,
D: Directional,
{
let layout = LayoutType::BoxComponent(Box::new(Slice {
children: slice,
direction,
data,
}));
Visitor { layout }
}
pub fn grid<I, S>(iter: I, dim: GridDimensions, data: &'a mut S) -> Self
where
I: DoubleEndedIterator<Item = (GridChildInfo, Visitor<'a>)> + 'a,
S: GridStorage,
{
let layout = LayoutType::BoxComponent(Box::new(Grid {
data,
dim,
children: iter,
}));
Visitor { layout }
}
pub fn float<I>(list: I) -> Self
where
I: DoubleEndedIterator<Item = Visitor<'a>> + 'a,
{
let layout = LayoutType::BoxComponent(Box::new(Float { children: list }));
Visitor { layout }
}
#[inline]
pub fn size_rules(mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
self.size_rules_(sizer, axis)
}
fn size_rules_(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
match &mut self.layout {
LayoutType::BoxComponent(component) => component.size_rules(sizer, axis),
LayoutType::Single(child) => child.size_rules(sizer, axis),
LayoutType::AlignSingle(child, hints) => {
child.size_rules(sizer, axis.with_align_hints(*hints))
}
LayoutType::Align(layout, hints) => {
layout.size_rules_(sizer, axis.with_align_hints(*hints))
}
LayoutType::Pack(layout, stor, hints) => {
let rules = layout.size_rules_(sizer, stor.apply_align(axis, *hints));
stor.size.set_component(axis, rules.ideal_size());
rules
}
LayoutType::Margins(child, dirs, margins) => {
let mut child_rules = child.size_rules_(sizer.re(), axis);
if dirs.intersects(Directions::from(axis)) {
let mut rule_margins = child_rules.margins();
let margins = sizer.margins(*margins).extract(axis);
if dirs.intersects(Directions::LEFT | Directions::UP) {
rule_margins.0 = margins.0;
}
if dirs.intersects(Directions::RIGHT | Directions::DOWN) {
rule_margins.1 = margins.1;
}
child_rules.set_margins(rule_margins);
}
child_rules
}
LayoutType::Frame(child, storage, style) => {
let child_rules = child.size_rules_(sizer.re(), storage.child_axis(axis));
storage.size_rules(sizer, axis, child_rules, *style)
}
LayoutType::Button(child, storage, _) => {
let child_rules = child.size_rules_(sizer.re(), storage.child_axis_centered(axis));
storage.size_rules(sizer, axis, child_rules, FrameStyle::Button)
}
}
}
#[inline]
pub fn set_rect(mut self, cx: &mut ConfigCx, rect: Rect) {
self.set_rect_(cx, rect);
}
fn set_rect_(&mut self, cx: &mut ConfigCx, rect: Rect) {
match &mut self.layout {
LayoutType::BoxComponent(layout) => layout.set_rect(cx, rect),
LayoutType::Single(child) => child.set_rect(cx, rect),
LayoutType::Align(layout, _) => layout.set_rect_(cx, rect),
LayoutType::AlignSingle(child, _) => child.set_rect(cx, rect),
LayoutType::Pack(layout, stor, _) => layout.set_rect_(cx, stor.aligned_rect(rect)),
LayoutType::Margins(child, _, _) => child.set_rect_(cx, rect),
LayoutType::Frame(child, storage, _) | LayoutType::Button(child, storage, _) => {
storage.rect = rect;
let child_rect = Rect {
pos: rect.pos + storage.offset,
size: rect.size - storage.size,
};
child.set_rect_(cx, child_rect);
}
}
}
#[inline]
pub fn find_id(mut self, coord: Coord) -> Option<Id> {
self.find_id_(coord)
}
fn find_id_(&mut self, coord: Coord) -> Option<Id> {
match &mut self.layout {
LayoutType::BoxComponent(layout) => layout.find_id(coord),
LayoutType::Single(child) | LayoutType::AlignSingle(child, _) => child.find_id(coord),
LayoutType::Align(layout, _) => layout.find_id_(coord),
LayoutType::Pack(layout, _, _) => layout.find_id_(coord),
LayoutType::Margins(layout, _, _) => layout.find_id_(coord),
LayoutType::Frame(child, _, _) => child.find_id_(coord),
LayoutType::Button(_, _, _) => None,
}
}
#[inline]
pub fn draw(mut self, draw: DrawCx) {
self.draw_(draw);
}
fn draw_(&mut self, mut draw: DrawCx) {
match &mut self.layout {
LayoutType::BoxComponent(layout) => layout.draw(draw),
LayoutType::Single(child) | LayoutType::AlignSingle(child, _) => draw.recurse(*child),
LayoutType::Align(layout, _) => layout.draw_(draw),
LayoutType::Pack(layout, _, _) => layout.draw_(draw),
LayoutType::Margins(layout, _, _) => layout.draw_(draw),
LayoutType::Frame(child, storage, style) => {
draw.frame(storage.rect, *style, Background::Default);
child.draw_(draw);
}
LayoutType::Button(child, storage, color) => {
let bg = match color {
Some(rgb) => Background::Rgb(*rgb),
None => Background::Default,
};
draw.frame(storage.rect, FrameStyle::Button, bg);
child.draw_(draw);
}
}
}
}
struct List<'a, I, D, S> {
children: I,
direction: D,
data: &'a mut S,
}
impl<'a, I, D: Directional, S: RowStorage> Visitable for List<'a, I, D, S>
where
I: ExactSizeIterator<Item = Visitor<'a>>,
{
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
let dim = (self.direction, self.children.len());
let mut solver = RowSolver::new(axis, dim, self.data);
for (n, child) in (&mut self.children).enumerate() {
solver.for_child(self.data, n, |axis| child.size_rules(sizer.re(), axis));
}
solver.finish(self.data)
}
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
let dim = (self.direction, self.children.len());
let mut setter = RowSetter::<D, Vec<i32>, _>::new(rect, dim, self.data);
for (n, child) in (&mut self.children).enumerate() {
child.set_rect(cx, setter.child_rect(self.data, n));
}
}
fn find_id(&mut self, coord: Coord) -> Option<Id> {
self.children.find_map(|child| child.find_id(coord))
}
fn draw(&mut self, mut draw: DrawCx) {
for child in &mut self.children {
child.draw(draw.re_clone());
}
}
}
struct Float<'a, I>
where
I: DoubleEndedIterator<Item = Visitor<'a>>,
{
children: I,
}
impl<'a, I> Visitable for Float<'a, I>
where
I: DoubleEndedIterator<Item = Visitor<'a>>,
{
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
let mut rules = SizeRules::EMPTY;
for child in &mut self.children {
rules = rules.max(child.size_rules(sizer.re(), axis));
}
rules
}
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
for child in &mut self.children {
child.set_rect(cx, rect);
}
}
fn find_id(&mut self, coord: Coord) -> Option<Id> {
self.children.find_map(|child| child.find_id(coord))
}
fn draw(&mut self, mut draw: DrawCx) {
let mut iter = (&mut self.children).rev();
if let Some(first) = iter.next() {
first.draw(draw.re_clone());
}
for child in iter {
draw.with_pass(|draw| child.draw(draw));
}
}
}
struct Slice<'a, W: Layout, D: Directional> {
children: &'a mut [W],
direction: D,
data: &'a mut DynRowStorage,
}
impl<'a, W: Layout, D: Directional> Visitable for Slice<'a, W, D> {
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
let dim = (self.direction, self.children.len());
let mut solver = RowSolver::new(axis, dim, self.data);
for (n, child) in self.children.iter_mut().enumerate() {
solver.for_child(self.data, n, |axis| child.size_rules(sizer.re(), axis));
}
solver.finish(self.data)
}
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
let dim = (self.direction, self.children.len());
let mut setter = RowSetter::<D, Vec<i32>, _>::new(rect, dim, self.data);
for (n, child) in self.children.iter_mut().enumerate() {
child.set_rect(cx, setter.child_rect(self.data, n));
}
}
fn find_id(&mut self, coord: Coord) -> Option<Id> {
let solver = RowPositionSolver::new(self.direction);
solver
.find_child_mut(self.children, coord)
.and_then(|child| child.find_id(coord))
}
fn draw(&mut self, mut draw: DrawCx) {
let solver = RowPositionSolver::new(self.direction);
solver.for_children(self.children, draw.get_clip_rect(), |w| draw.recurse(w));
}
}
struct Grid<'a, S, I> {
data: &'a mut S,
dim: GridDimensions,
children: I,
}
impl<'a, S: GridStorage, I> Visitable for Grid<'a, S, I>
where
I: DoubleEndedIterator<Item = (GridChildInfo, Visitor<'a>)>,
{
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
let mut solver = GridSolver::<Vec<_>, Vec<_>, _>::new(axis, self.dim, self.data);
for (info, child) in &mut self.children {
solver.for_child(self.data, info, |axis| child.size_rules(sizer.re(), axis));
}
solver.finish(self.data)
}
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
let mut setter = GridSetter::<Vec<_>, Vec<_>, _>::new(rect, self.dim, self.data);
for (info, child) in &mut self.children {
child.set_rect(cx, setter.child_rect(self.data, info));
}
}
fn find_id(&mut self, coord: Coord) -> Option<Id> {
self.children.find_map(|(_, child)| child.find_id(coord))
}
fn draw(&mut self, mut draw: DrawCx) {
for (_, child) in (&mut self.children).rev() {
child.draw(draw.re_clone());
}
}
}
#[derive(Clone, Default, Debug)]
pub struct PackStorage {
align: AlignPair,
size: Size,
}
impl PackStorage {
fn apply_align(&mut self, axis: AxisInfo, hints: AlignHints) -> AxisInfo {
let axis = axis.with_align_hints(hints);
self.align.set_component(axis, axis.align_or_default());
axis
}
fn aligned_rect(&self, rect: Rect) -> Rect {
self.align.aligned_rect(self.size, rect)
}
}
#[derive(Clone, Default, Debug)]
pub struct FrameStorage {
pub size: Size,
pub offset: Offset,
rect: Rect,
}
impl FrameStorage {
pub fn child_axis(&self, mut axis: AxisInfo) -> AxisInfo {
axis.sub_other(self.size.extract(axis.flipped()));
axis
}
pub fn child_axis_centered(&self, mut axis: AxisInfo) -> AxisInfo {
axis.sub_other(self.size.extract(axis.flipped()));
axis.set_align(Some(Align::Center));
axis
}
pub fn size_rules(
&mut self,
sizer: SizeCx,
axis: AxisInfo,
child_rules: SizeRules,
style: FrameStyle,
) -> SizeRules {
let frame_rules = sizer.frame(style, axis);
let (rules, offset, size) = frame_rules.surround(child_rules);
self.offset.set_component(axis, offset);
self.size.set_component(axis, size);
rules
}
}