use ltk::core::{ Canvas, RenderOptions, UiSurface }; use ltk::{ button, column, window_controls, Color, Element, Point, WindowButtonKind }; #[ derive( Clone, Debug, PartialEq, Eq ) ] enum Msg { Pressed, Close, Maximize, Minimize, } fn view() -> Element { column::() .padding( 16.0 ) .push( button( "Press" ).on_press( Msg::Pressed ) ) .into() } fn decoration_view() -> Element { window_controls( Some( Msg::Minimize ), WindowButtonKind::Maximize, Some( Msg::Maximize ), Some( Msg::Close ), ) .into() } #[ test ] fn core_surface_new_is_software_by_default() { let surface = UiSurface::::new( 240, 120 ); assert!( matches!( surface.canvas(), Canvas::Software( _ ) ) ); assert!( surface.canvas().borrowed_gles_texture().is_none() ); } #[ test ] fn core_surface_renders_and_exposes_handlers_without_run() { let mut surface = UiSurface::::new( 240, 120 ); let out = surface.render( &view(), RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) ), ); assert!( out.full_redraw ); assert_eq!( surface.widget_rects().len(), 1 ); let first = &surface.widget_rects()[ 0 ]; let hit = surface.hit_test( Point { x: first.rect.x + 1.0, y: first.rect.y + 1.0 } ); assert_eq!( hit, Some( first.flat_idx ) ); let msg = surface.handlers( first.flat_idx ).and_then( |h| h.press_msg() ); assert_eq!( msg, Some( Msg::Pressed ) ); } #[ test ] fn core_surface_can_drive_window_controls() { let mut surface = UiSurface::::new( 180, 48 ); let _ = surface.render( &decoration_view(), RenderOptions::full_canvas( 180, 48 ).background( Color::rgb( 0.08, 0.08, 0.09 ) ), ); assert_eq!( surface.widget_rects().len(), 3 ); let close = surface.widget_rects()[ 2 ].flat_idx; let msg = surface.handlers( close ).and_then( |h| h.press_msg() ); assert_eq!( msg, Some( Msg::Close ) ); } #[ test ] fn core_surface_reports_partial_damage_for_focus_change() { let mut surface = UiSurface::::new( 240, 120 ); let opts = RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) ); let _ = surface.render( &view(), opts ); let idx = surface.widget_rects()[ 0 ].flat_idx; surface.set_focused( Some( idx ) ); let out = surface.render( &view(), opts ); assert!( !out.full_redraw, "focus-only transition should be partial damage" ); assert!( !out.damage_rects.is_empty() ); } // ── interaction-only damage paths ───────────────────────────────────────────── #[ test ] fn hover_only_change_produces_partial_damage() { let mut surface = UiSurface::::new( 240, 120 ); let opts = RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) ); let _ = surface.render( &view(), opts ); let idx = surface.widget_rects()[ 0 ].flat_idx; surface.set_hovered( Some( idx ) ); let out = surface.render( &view(), opts ); assert!( !out.full_redraw, "hover-only transition should be partial damage" ); assert!( !out.damage_rects.is_empty() ); } #[ test ] fn pressed_only_change_produces_partial_damage() { let mut surface = UiSurface::::new( 240, 120 ); let opts = RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) ); let _ = surface.render( &view(), opts ); let idx = surface.widget_rects()[ 0 ].flat_idx; surface.set_pressed( Some( idx ) ); let out = surface.render( &view(), opts ); assert!( !out.full_redraw ); } #[ test ] fn focused_hovered_pressed_setters_round_trip() { let mut surface = UiSurface::::new( 240, 120 ); let opts = RenderOptions::full_canvas( 240, 120 ); let _ = surface.render( &view(), opts ); let idx = surface.widget_rects()[ 0 ].flat_idx; surface.set_focused( Some( idx ) ); surface.set_hovered( Some( idx ) ); surface.set_pressed( Some( idx ) ); assert_eq!( surface.focused(), Some( idx ) ); assert_eq!( surface.hovered(), Some( idx ) ); assert_eq!( surface.pressed(), Some( idx ) ); surface.set_focused( None ); surface.set_hovered( None ); surface.set_pressed( None ); assert_eq!( surface.focused(), None ); assert_eq!( surface.hovered(), None ); assert_eq!( surface.pressed(), None ); } // ── full-redraw paths ───────────────────────────────────────────────────────── #[ test ] fn first_render_is_a_full_redraw() { let mut surface = UiSurface::::new( 240, 120 ); let out = surface.render( &view(), RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) ), ); assert!( out.full_redraw, "the first render of a fresh surface is always a full redraw" ); } #[ test ] fn mark_content_dirty_forces_next_render_to_full_redraw() { let mut surface = UiSurface::::new( 240, 120 ); let opts = RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) ); let _ = surface.render( &view(), opts ); // No interaction change — the next render would normally be partial. surface.mark_content_dirty(); let out = surface.render( &view(), opts ); assert!( out.full_redraw ); } #[ test ] fn resize_forces_next_render_to_full_redraw() { let mut surface = UiSurface::::new( 240, 120 ); let _ = surface.render( &view(), RenderOptions::full_canvas( 240, 120 ) ); surface.resize( 320, 200 ); assert_eq!( surface.size(), ( 320, 200 ) ); let out = surface.render( &view(), RenderOptions::full_canvas( 320, 200 ) ); assert!( out.full_redraw ); } #[ test ] fn tree_size_change_forces_full_redraw() { let mut surface = UiSurface::::new( 320, 200 ); let opts = RenderOptions::full_canvas( 320, 200 ); let _ = surface.render( &view(), opts ); let initial_count = surface.widget_rects().len(); // Render a different tree (decoration_view has 3 buttons; view has 1). let out = surface.render( &decoration_view(), opts ); assert!( out.full_redraw ); assert_ne!( surface.widget_rects().len(), initial_count ); } // ── lookups ─────────────────────────────────────────────────────────────────── #[ test ] fn hit_test_outside_any_widget_returns_none() { let mut surface = UiSurface::::new( 320, 200 ); let _ = surface.render( &view(), RenderOptions::full_canvas( 320, 200 ) ); // (0, 0) is the top-left corner — far from the centred button. assert_eq!( surface.hit_test( Point { x: 0.0, y: 0.0 } ), None ); } #[ test ] fn handlers_lookup_for_unknown_idx_returns_none() { let mut surface = UiSurface::::new( 240, 120 ); let _ = surface.render( &view(), RenderOptions::full_canvas( 240, 120 ) ); assert!( surface.handlers( 999_999 ).is_none() ); } #[ test ] fn widget_lookup_for_known_idx_returns_some() { let mut surface = UiSurface::::new( 240, 120 ); let _ = surface.render( &view(), RenderOptions::full_canvas( 240, 120 ) ); let idx = surface.widget_rects()[ 0 ].flat_idx; assert!( surface.widget( idx ).is_some() ); }