use crate::types::Rect;
use crate::render::Canvas;
use crate::widget::Element;
#[ derive( Debug, Clone, Copy, PartialEq ) ]
pub enum HAlign
{
Start,
Center,
End,
Fill,
}
#[ derive( Debug, Clone, Copy, PartialEq ) ]
pub enum VAlign
{
Top,
Center,
Bottom,
Fill,
}
pub struct Stack<Msg: Clone>
{
pub children: Vec<( Element<Msg>, HAlign, VAlign, f32, f32, f32 )>,
pub fit_content: bool,
}
impl<Msg: Clone> Stack<Msg>
{
pub fn new() -> Self
{
Self { children: Vec::new(), fit_content: false }
}
pub fn fit_content( mut self ) -> Self
{
self.fit_content = true;
self
}
pub fn push( self, e: impl Into<Element<Msg>> ) -> Self
{
self.push_aligned_margin( e, HAlign::Fill, VAlign::Fill, 0.0 )
}
pub fn push_aligned(
self,
e: impl Into<Element<Msg>>,
h_align: HAlign,
v_align: VAlign,
) -> Self
{
self.push_aligned_margin( e, h_align, v_align, 0.0 )
}
pub fn push_aligned_margin(
mut self,
e: impl Into<Element<Msg>>,
h_align: HAlign,
v_align: VAlign,
margin: f32,
) -> Self
{
self.children.push( ( e.into(), h_align, v_align, margin, 0.0, 0.0 ) );
self
}
pub fn push_translated(
mut self,
e: impl Into<Element<Msg>>,
h_align: HAlign,
v_align: VAlign,
offset_x: f32,
offset_y: f32,
) -> Self
{
self.children.push( ( e.into(), h_align, v_align, 0.0, offset_x, offset_y ) );
self
}
pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> (f32, f32)
{
if self.fit_content
{
let content_w = self.children.iter()
.map( |( c, _, _, _, _, _ )| match c
{
Element::Spacer( s ) => s.resolved_width( canvas ).unwrap_or( 0.0 ),
Element::Separator( _ ) => 0.0,
Element::Scroll( _ ) => 0.0,
Element::ProgressBar( _ ) => 0.0,
Element::Slider( _ ) => 0.0,
Element::TextEdit( t ) => if t.fixed_width.is_some()
{
t.preferred_size( max_width, canvas ).0
} else { 0.0 },
other => other.preferred_size( max_width, canvas ).0,
} )
.fold( 0.0_f32, f32::max );
let max_h = self.children.iter()
.map( |( c, _, _, _, _, _ )| c.preferred_size( max_width, canvas ).1 )
.fold( 0.0_f32, f32::max );
return ( content_w.min( max_width ), max_h );
}
let max_h = self.children.iter()
.map( |( c, _, _, _, _, _ )| c.preferred_size( max_width, canvas ).1 )
.fold( 0.0_f32, f32::max );
( max_width, max_h )
}
pub fn layout( &self, rect: Rect, canvas: &Canvas ) -> Vec<(Rect, usize)>
{
self.children.iter().enumerate().map( |( i, ( child, h_align, v_align, margin, ox, oy ) )|
{
let inner_w = ( rect.width - margin * 2.0 ).max( 0.0 );
let inner_h = ( rect.height - margin * 2.0 ).max( 0.0 );
let ( pref_w, pref_h ) = child.preferred_size( inner_w, canvas );
let ( x, width ) = match h_align
{
HAlign::Start => ( rect.x + margin, pref_w ),
HAlign::Center => ( rect.x + ( rect.width - pref_w ) / 2.0, pref_w ),
HAlign::End => ( rect.x + rect.width - pref_w - margin, pref_w ),
HAlign::Fill => ( rect.x + margin, inner_w ),
};
let ( y, height ) = match v_align
{
VAlign::Top => ( rect.y + margin, pref_h ),
VAlign::Center => ( rect.y + ( rect.height - pref_h ) / 2.0, pref_h ),
VAlign::Bottom => ( rect.y + rect.height - pref_h - margin, pref_h ),
VAlign::Fill => ( rect.y + margin, inner_h ),
};
( Rect { x: x + ox, y: y + oy, width, height }, i )
} ).collect()
}
pub fn draw( &self, _canvas: &mut Canvas, _rect: Rect, _focused: bool ) {}
pub( crate ) fn map_msg<U>( self, f: &crate::widget::MapFn<Msg, U> ) -> Stack<U>
where
U: Clone + 'static,
Msg: 'static,
{
Stack
{
children: self.children.into_iter()
.map( |( child, ha, va, margin, ox, oy )|
( child.map_arc( f ), ha, va, margin, ox, oy ) )
.collect(),
fit_content: self.fit_content,
}
}
}
impl<Msg: Clone> Default for Stack<Msg>
{
fn default() -> Self
{
Self::new()
}
}
pub fn stack<Msg: Clone>() -> Stack<Msg>
{
Stack::new()
}