First commit. Version 0.1.0
This commit is contained in:
277
tests/layout_stack_spacer.rs
Normal file
277
tests/layout_stack_spacer.rs
Normal file
@@ -0,0 +1,277 @@
|
||||
// Integration coverage for `Stack` alignment / margin / translation and for
|
||||
// the way `Spacer` distributes leftover space inside a `Column`.
|
||||
|
||||
use ltk::core::Canvas;
|
||||
use ltk::{ stack, spacer, HAlign, VAlign, Rect };
|
||||
|
||||
fn make_canvas() -> Canvas
|
||||
{
|
||||
Canvas::new( 800, 600 )
|
||||
}
|
||||
|
||||
fn rect( x: f32, y: f32, w: f32, h: f32 ) -> Rect
|
||||
{
|
||||
Rect { x, y, width: w, height: h }
|
||||
}
|
||||
|
||||
// ── Stack: alignment ──────────────────────────────────────────────────────────
|
||||
|
||||
#[ test ]
|
||||
fn stack_fill_child_takes_full_rect()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let s = stack::<()>().push( spacer().width( 100.0 ).height( 100.0 ) );
|
||||
let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );
|
||||
|
||||
assert_eq!( layout.len(), 1 );
|
||||
let ( child_rect, _ ) = layout[ 0 ];
|
||||
// Fill ignores the spacer's preferred size and stretches across the
|
||||
// entire stack rect.
|
||||
assert_eq!( child_rect, rect( 0.0, 0.0, 400.0, 300.0 ) );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn stack_center_centers_child_inside_rect()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let child = spacer().width( 100.0 ).height( 50.0 );
|
||||
let s = stack::<()>().push_aligned( child, HAlign::Center, VAlign::Center );
|
||||
let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );
|
||||
|
||||
let ( child_rect, _ ) = layout[ 0 ];
|
||||
assert_eq!( child_rect.x, 150.0 );
|
||||
assert_eq!( child_rect.y, 125.0 );
|
||||
assert_eq!( child_rect.width, 100.0 );
|
||||
assert_eq!( child_rect.height, 50.0 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn stack_end_bottom_anchors_to_bottom_right()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let child = spacer().width( 80.0 ).height( 40.0 );
|
||||
let s = stack::<()>().push_aligned( child, HAlign::End, VAlign::Bottom );
|
||||
let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );
|
||||
|
||||
let ( child_rect, _ ) = layout[ 0 ];
|
||||
assert_eq!( child_rect.x, 320.0 );
|
||||
assert_eq!( child_rect.y, 260.0 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn stack_start_top_anchors_to_top_left()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let child = spacer().width( 40.0 ).height( 30.0 );
|
||||
let s = stack::<()>().push_aligned( child, HAlign::Start, VAlign::Top );
|
||||
let layout = s.layout( rect( 100.0, 50.0, 400.0, 300.0 ), &canvas );
|
||||
|
||||
let ( child_rect, _ ) = layout[ 0 ];
|
||||
assert_eq!( child_rect.x, 100.0 );
|
||||
assert_eq!( child_rect.y, 50.0 );
|
||||
}
|
||||
|
||||
// ── Stack: margin ─────────────────────────────────────────────────────────────
|
||||
|
||||
#[ test ]
|
||||
fn stack_margin_insets_fill_child_on_all_sides()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let child = spacer().width( 50.0 ).height( 50.0 );
|
||||
let s = stack::<()>()
|
||||
.push_aligned_margin( child, HAlign::Fill, VAlign::Fill, 12.0 );
|
||||
let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );
|
||||
|
||||
let ( child_rect, _ ) = layout[ 0 ];
|
||||
// Fill + margin = original rect inset on every side.
|
||||
assert_eq!( child_rect, rect( 12.0, 12.0, 376.0, 276.0 ) );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn stack_margin_pushes_end_anchored_child_inward()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let child = spacer().width( 50.0 ).height( 50.0 );
|
||||
let s = stack::<()>()
|
||||
.push_aligned_margin( child, HAlign::End, VAlign::Top, 8.0 );
|
||||
let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );
|
||||
|
||||
let ( child_rect, _ ) = layout[ 0 ];
|
||||
// 400 - 50 - 8 = 342 from left edge; y starts at 8.
|
||||
assert_eq!( child_rect.x, 342.0 );
|
||||
assert_eq!( child_rect.y, 8.0 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn stack_margin_larger_than_half_rect_clamps_inner_to_zero()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let child = spacer().width( 10.0 ).height( 10.0 );
|
||||
let s = stack::<()>()
|
||||
.push_aligned_margin( child, HAlign::Fill, VAlign::Fill, 200.0 );
|
||||
// Stack rect is 100×100; margin 200 → inner is clamped to (0, 0).
|
||||
let layout = s.layout( rect( 0.0, 0.0, 100.0, 100.0 ), &canvas );
|
||||
|
||||
let ( child_rect, _ ) = layout[ 0 ];
|
||||
assert_eq!( child_rect.width, 0.0 );
|
||||
assert_eq!( child_rect.height, 0.0 );
|
||||
}
|
||||
|
||||
// ── Stack: translation ────────────────────────────────────────────────────────
|
||||
|
||||
#[ test ]
|
||||
fn stack_translation_offsets_child_after_alignment()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let child = spacer().width( 40.0 ).height( 30.0 );
|
||||
let s = stack::<()>()
|
||||
.push_translated( child, HAlign::Start, VAlign::Top, 5.0, 7.0 );
|
||||
let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );
|
||||
|
||||
let ( child_rect, _ ) = layout[ 0 ];
|
||||
assert_eq!( child_rect.x, 5.0 );
|
||||
assert_eq!( child_rect.y, 7.0 );
|
||||
}
|
||||
|
||||
// ── Stack: order ─────────────────────────────────────────────────────────────
|
||||
|
||||
#[ test ]
|
||||
fn stack_layout_preserves_child_order()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let s = stack::<()>()
|
||||
.push( spacer().width( 10.0 ).height( 10.0 ) )
|
||||
.push( spacer().width( 20.0 ).height( 20.0 ) )
|
||||
.push( spacer().width( 30.0 ).height( 30.0 ) );
|
||||
let layout = s.layout( rect( 0.0, 0.0, 400.0, 300.0 ), &canvas );
|
||||
|
||||
assert_eq!( layout.len(), 3 );
|
||||
assert_eq!( layout[ 0 ].1, 0 );
|
||||
assert_eq!( layout[ 1 ].1, 1 );
|
||||
assert_eq!( layout[ 2 ].1, 2 );
|
||||
}
|
||||
|
||||
// ── Stack: preferred_size ─────────────────────────────────────────────────────
|
||||
|
||||
#[ test ]
|
||||
fn stack_preferred_size_returns_max_child_height()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let s = stack::<()>()
|
||||
.push( spacer().width( 40.0 ).height( 30.0 ) )
|
||||
.push( spacer().width( 60.0 ).height( 80.0 ) );
|
||||
let ( w, h ) = s.preferred_size( 400.0, &canvas );
|
||||
// Stack always reports `max_width` for width, and the tallest child for
|
||||
// height — Stack is meant for overlaying, not flow.
|
||||
assert_eq!( w, 400.0 );
|
||||
assert_eq!( h, 80.0 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn stack_empty_preferred_size_height_is_zero()
|
||||
{
|
||||
let canvas = make_canvas();
|
||||
let s: ltk::Stack<()> = stack();
|
||||
let ( _, h ) = s.preferred_size( 400.0, &canvas );
|
||||
assert_eq!( h, 0.0 );
|
||||
}
|
||||
|
||||
// ── Spacer: builder semantics ─────────────────────────────────────────────────
|
||||
|
||||
#[ test ]
|
||||
fn spacer_default_has_weight_one_and_no_fixed_size()
|
||||
{
|
||||
let s = spacer();
|
||||
assert_eq!( s.weight, 1 );
|
||||
assert!( s.fixed_height.is_none() );
|
||||
assert!( s.fixed_width.is_none() );
|
||||
assert_eq!( s.preferred_size(), ( 0.0, 0.0 ) );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn spacer_height_pins_vertical_axis_only()
|
||||
{
|
||||
let s = spacer().height( 24.0 );
|
||||
assert_eq!( s.fixed_height, Some( 24.0 ) );
|
||||
assert!( s.fixed_width.is_none() );
|
||||
assert_eq!( s.preferred_size(), ( 0.0, 24.0 ) );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn spacer_width_pins_horizontal_axis_only()
|
||||
{
|
||||
let s = spacer().width( 12.0 );
|
||||
assert_eq!( s.fixed_width, Some( 12.0 ) );
|
||||
assert!( s.fixed_height.is_none() );
|
||||
assert_eq!( s.preferred_size(), ( 12.0, 0.0 ) );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn spacer_weight_replaces_default()
|
||||
{
|
||||
let s = spacer().weight( 5 );
|
||||
assert_eq!( s.weight, 5 );
|
||||
}
|
||||
|
||||
// ── Spacer in Column: leftover distribution ───────────────────────────────────
|
||||
|
||||
// These use `column().layout(...)` directly (Column re-exports its `layout()`
|
||||
// method as `pub`). Fixed-height children consume known space; flexible spacers
|
||||
// must absorb the rest.
|
||||
|
||||
#[ test ]
|
||||
fn flexible_spacer_in_column_consumes_leftover_height()
|
||||
{
|
||||
use ltk::column;
|
||||
|
||||
let canvas = make_canvas();
|
||||
let col = column::<()>()
|
||||
.padding( 0.0 )
|
||||
.spacing( 0.0 )
|
||||
.push( spacer().width( 50.0 ).height( 100.0 ) )
|
||||
.push( spacer() ) // flexible
|
||||
.push( spacer().width( 50.0 ).height( 100.0 ) );
|
||||
|
||||
let layout = col.layout( rect( 0.0, 0.0, 200.0, 500.0 ), &canvas );
|
||||
assert_eq!( layout.len(), 3 );
|
||||
|
||||
// Two fixed children of 100 each + one spacer absorbing the leftover 300.
|
||||
assert!( ( layout[ 0 ].0.height - 100.0 ).abs() < 1e-3 );
|
||||
assert!( ( layout[ 1 ].0.height - 300.0 ).abs() < 1e-3 );
|
||||
assert!( ( layout[ 2 ].0.height - 100.0 ).abs() < 1e-3 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn weighted_spacers_split_leftover_proportionally()
|
||||
{
|
||||
use ltk::column;
|
||||
|
||||
let canvas = make_canvas();
|
||||
let col = column::<()>()
|
||||
.padding( 0.0 )
|
||||
.spacing( 0.0 )
|
||||
.push( spacer().weight( 1 ) )
|
||||
.push( spacer().weight( 3 ) );
|
||||
|
||||
let layout = col.layout( rect( 0.0, 0.0, 200.0, 400.0 ), &canvas );
|
||||
// Total weight 4; leftover 400; weight 1 → 100, weight 3 → 300.
|
||||
assert!( ( layout[ 0 ].0.height - 100.0 ).abs() < 1e-3 );
|
||||
assert!( ( layout[ 1 ].0.height - 300.0 ).abs() < 1e-3 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn fixed_height_spacer_does_not_consume_leftover()
|
||||
{
|
||||
use ltk::column;
|
||||
|
||||
let canvas = make_canvas();
|
||||
let col = column::<()>()
|
||||
.padding( 0.0 )
|
||||
.spacing( 0.0 )
|
||||
.push( spacer().height( 50.0 ) ) // fixed — keeps its 50 px
|
||||
.push( spacer() ); // flexible — absorbs the rest
|
||||
|
||||
let layout = col.layout( rect( 0.0, 0.0, 200.0, 500.0 ), &canvas );
|
||||
assert!( ( layout[ 0 ].0.height - 50.0 ).abs() < 1e-3 );
|
||||
assert!( ( layout[ 1 ].0.height - 450.0 ).abs() < 1e-3 );
|
||||
}
|
||||
Reference in New Issue
Block a user