use crate::render::Canvas;
use crate::types::{ Length, Rect };
use crate::widget::Element;
use super::motion::{ Placed, TabMotion };
use super::theme::{ EDGE_SCROLL, GAP, MAX_TAB_FRACTION, MIN_TAB_WIDTH, RESERVE };
pub struct TabRow<Msg: Clone>
{
pub( crate ) child: Box<Element<Msg>>,
pub( crate ) keys: Vec<u64>,
pub( crate ) selected: Option<u64>,
pub( crate ) motion: TabMotion,
}
impl<Msg: Clone> TabRow<Msg>
{
pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> ( f32, f32 )
{
( max_width, self.child.preferred_size( max_width, canvas ).1 )
}
pub( crate ) fn arrange( &self, rect: Rect, canvas: &Canvas ) -> Rect
{
let room = rect.width * ( 1.0 - RESERVE );
let gap = canvas.resolve_geom( Length::widget( GAP ) );
let after = self.cells().map_or( 0.0, |cells| cells.after_width( canvas, gap ) );
let count = self.keys.len();
let spare = room - after - gap * count.saturating_sub( 1 ) as f32;
let screen = crate::types::output_logical_size().map_or( canvas.size().0 as f32, |size| size.0 as f32 * canvas.dpi_scale() );
let widest = screen * MAX_TAB_FRACTION;
let least = canvas.resolve_geom( Length::widget( MIN_TAB_WIDTH ) );
let width = if count == 0 { 0.0 } else { ( spare / count as f32 ).min( widest ).max( least ) };
self.motion.arrange( &self.keys, width );
Rect { width: room.max( 0.0 ), ..rect }
}
pub( crate ) fn scroll_to( &self, offset: f32, viewport: f32, canvas: &Canvas ) -> Option<f32>
{
self.motion.scroll( self.selected, offset, viewport, canvas.resolve_geom( Length::widget( EDGE_SCROLL ) ) )
}
fn cells( &self ) -> Option<&TabCells<Msg>>
{
match self.child.as_ref()
{
Element::Scroll( scroll ) => match scroll.child.as_ref()
{
Element::TabCells( cells ) => Some( cells ),
_ => None,
},
_ => None,
}
}
pub( crate ) fn map_msg<U>( self, f: &crate::widget::MapFn<Msg, U> ) -> TabRow<U>
where
U: Clone + 'static,
Msg: 'static,
{
TabRow { child: Box::new( self.child.map_arc( f ) ), keys: self.keys, selected: self.selected, motion: self.motion }
}
}
pub struct TabCells<Msg: Clone>
{
pub( crate ) tabs: Vec<( u64, Element<Msg> )>,
pub( crate ) after: Vec<Element<Msg>>,
pub( crate ) motion: TabMotion,
}
impl<Msg: Clone> TabCells<Msg>
{
pub( crate ) fn child( &self, idx: usize ) -> &Element<Msg>
{
match self.tabs.get( idx )
{
Some( ( _, tab ) ) => tab,
None => &self.after[ idx - self.tabs.len() ],
}
}
pub( crate ) fn after_width( &self, canvas: &Canvas, gap: f32 ) -> f32
{
self.after.iter().map( |button| gap + button.preferred_size( f32::INFINITY, canvas ).0 ).sum()
}
pub fn preferred_size( &self, _max_width: f32, canvas: &Canvas ) -> ( f32, f32 )
{
let gap = canvas.resolve_geom( Length::widget( GAP ) );
let placed = self.motion.placed();
let tabs = placed.iter().map( |slot| slot.width ).sum::<f32>() + gaps( &placed, gap );
let height = self.tabs.iter().map( |( _, tab )| tab ).chain( &self.after )
.map( |child| child.preferred_size( f32::INFINITY, canvas ).1 )
.fold( 0.0_f32, f32::max );
( tabs + self.after_width( canvas, gap ), height )
}
pub fn layout( &self, rect: Rect, canvas: &Canvas ) -> Vec<( Rect, usize )>
{
let gap = canvas.resolve_geom( Length::widget( GAP ) );
let ( laid, end ) = self.motion.lay( rect, gap );
let mut rects: Vec<_> = laid.iter()
.filter_map( |laid| self.tabs.iter().position( |( key, _ )| *key == laid.key )
.map( |idx| ( laid.rect, idx ) ) )
.collect();
let mut x = end;
for ( i, button ) in self.after.iter().enumerate()
{
let ( width, height ) = button.preferred_size( f32::INFINITY, canvas );
x += gap;
rects.push( ( Rect { x, y: rect.y + ( rect.height - height ) / 2.0, width, height }, self.tabs.len() + i ) );
x += width;
}
rects
}
pub( crate ) fn map_msg<U>( self, f: &crate::widget::MapFn<Msg, U> ) -> TabCells<U>
where
U: Clone + 'static,
Msg: 'static,
{
TabCells
{
tabs: self.tabs.into_iter().map( |( key, tab )| ( key, tab.map_arc( f ) ) ).collect(),
after: self.after.into_iter().map( |button| button.map_arc( f ) ).collect(),
motion: self.motion,
}
}
}
fn gaps( placed: &[ Placed ], gap: f32 ) -> f32
{
let all: f32 = placed.iter().map( |slot| gap * slot.presence ).sum();
let last = placed.last().map_or( 0.0, |slot| gap * slot.presence );
all - last
}
impl<Msg: Clone> From<TabRow<Msg>> for Element<Msg>
{
fn from( row: TabRow<Msg> ) -> Self
{
Element::TabRow( row )
}
}
impl<Msg: Clone> From<TabCells<Msg>> for Element<Msg>
{
fn from( cells: TabCells<Msg> ) -> Self
{
Element::TabCells( cells )
}
}