// SPDX-License-Identifier: LGPL-2.1-only // Copyright (C) 2026 Liberux Labs, S. L. 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, /// # title: Element, /// # subtitle: Element, /// # ) -> Element { /// 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 { pub child: Box>, pub weight: u32, } impl Flex { pub fn new( child: impl Into> ) -> 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( self, f: &super::MapFn ) -> Flex where U: Clone + 'static, Msg: 'static, { Flex { child: Box::new( self.child.map_arc( f ) ), weight: self.weight, } } } pub fn flex( child: impl Into> ) -> Flex { Flex::new( child ) } impl From> for Element { fn from( f: Flex ) -> 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 ); } }