First commit. Version 0.1.0
This commit is contained in:
138
src/widget/flex.rs
Normal file
138
src/widget/flex.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||||
|
||||
use crate::render::Canvas;
|
||||
use super::Element;
|
||||
|
||||
/// Wraps an [`Element`] so that a [`Row`](crate::layout::row::Row) treats it
|
||||
/// like a [`Spacer`](crate::layout::spacer::Spacer) for leftover-width
|
||||
/// distribution, but draws the child inside the allocated rect.
|
||||
///
|
||||
/// Use this to make a non-trivial child fill remaining width without resorting
|
||||
/// to a hard-coded `max_width`. The row computes how much width is left after
|
||||
/// the fixed-size siblings, splits it across all flex / spacer children
|
||||
/// proportionally to their `weight`, and gives the flex its share. The wrapped
|
||||
/// child sees that share as its layout rect — text inside it triggers its own
|
||||
/// elide path on overflow, columns adjust their inner width, etc.
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use ltk::{ column, flex, row, Element };
|
||||
/// # #[ derive( Clone ) ] enum Msg {}
|
||||
/// # fn _ex(
|
||||
/// # icon: Element<Msg>,
|
||||
/// # title: Element<Msg>,
|
||||
/// # subtitle: Element<Msg>,
|
||||
/// # ) -> Element<Msg> {
|
||||
/// row()
|
||||
/// .push( icon )
|
||||
/// .push( flex( column().push( title ).push( subtitle ) ) )
|
||||
/// .into()
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Currently only [`Row`](crate::layout::row::Row) honours flex distribution.
|
||||
/// Inside a [`Column`](crate::layout::column::Column) a flex child behaves
|
||||
/// like a regular zero-width child along the main axis — vertical flex is not
|
||||
/// yet implemented.
|
||||
pub struct Flex<Msg: Clone>
|
||||
{
|
||||
pub child: Box<Element<Msg>>,
|
||||
pub weight: u32,
|
||||
}
|
||||
|
||||
impl<Msg: Clone> Flex<Msg>
|
||||
{
|
||||
pub fn new( child: impl Into<Element<Msg>> ) -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
child: Box::new( child.into() ),
|
||||
weight: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Relative weight when sharing leftover width with other flex / spacer
|
||||
/// children in the same row (default `1`). A flex with `weight = 2`
|
||||
/// claims twice the space of a sibling with `weight = 1`.
|
||||
pub fn weight( mut self, w: u32 ) -> Self
|
||||
{
|
||||
self.weight = w;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> ( f32, f32 )
|
||||
{
|
||||
// Width contribution to the parent row is zero — the actual width
|
||||
// comes from the flex distribution. Height comes from the child so
|
||||
// the row's row-height calculation still picks us up.
|
||||
let ( _, h ) = self.child.preferred_size( max_width, canvas );
|
||||
( 0.0, h )
|
||||
}
|
||||
|
||||
pub( crate ) fn map_msg<U>( self, f: &super::MapFn<Msg, U> ) -> Flex<U>
|
||||
where
|
||||
U: Clone + 'static,
|
||||
Msg: 'static,
|
||||
{
|
||||
Flex
|
||||
{
|
||||
child: Box::new( self.child.map_arc( f ) ),
|
||||
weight: self.weight,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flex<Msg: Clone>( child: impl Into<Element<Msg>> ) -> Flex<Msg>
|
||||
{
|
||||
Flex::new( child )
|
||||
}
|
||||
|
||||
impl<Msg: Clone + 'static> From<Flex<Msg>> for Element<Msg>
|
||||
{
|
||||
fn from( f: Flex<Msg> ) -> Self
|
||||
{
|
||||
Element::Flex( f )
|
||||
}
|
||||
}
|
||||
|
||||
#[ cfg( test ) ]
|
||||
mod tests
|
||||
{
|
||||
use super::*;
|
||||
use crate::layout::spacer::spacer;
|
||||
use crate::render::Canvas;
|
||||
|
||||
fn make_canvas() -> Canvas { Canvas::new( 800, 600 ) }
|
||||
|
||||
#[ test ]
|
||||
fn new_defaults_to_weight_one()
|
||||
{
|
||||
let f = Flex::<()>::new( spacer() );
|
||||
assert_eq!( f.weight, 1 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn weight_builder_sets_relative_weight()
|
||||
{
|
||||
let f = Flex::<()>::new( spacer() ).weight( 5 );
|
||||
assert_eq!( f.weight, 5 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn preferred_size_reports_zero_width_so_row_treats_it_as_filler()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let f = Flex::<()>::new( spacer().width( 100.0 ).height( 40.0 ) );
|
||||
let ( w, _ ) = f.preferred_size( 600.0, &canvas );
|
||||
assert_eq!( w, 0.0, "flex must contribute zero width to the row's intrinsic-width sum" );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn preferred_size_passes_child_height_through()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let f = Flex::<()>::new( spacer().width( 100.0 ).height( 40.0 ) );
|
||||
let ( _, h ) = f.preferred_size( 600.0, &canvas );
|
||||
assert_eq!( h, 40.0 );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user