layout: adaptive grid and vertical flex; enforce the mechanical style rules
grid_min_cell( width ) adds the adaptive mode WrapGrid was missing: instead of a fixed column count, the count is derived at layout time from the available width so every cell is at least `width` wide (any Length, so a fluid threshold works; never fewer than one column), re-derived on every resize. Cells then share the width equally, and WrapGrid::max_columns( n ) caps the derived count so cells grow instead of multiplying on very wide surfaces — the cap only applies to the adaptive mode, grid( n ) keeps the fixed behaviour untouched. Four new layout tests cover width derivation, spacing accounting, the one-column floor and the cap; the row-wrap assertions check X positions rather than Y because zero-height spacer children produce zero-height rows. flex( child ) now distributes leftover height inside a Column, mirroring its leftover-width behaviour in a Row: flex children join the weight pool alongside weight-only spacers and y-scrolls, draw their child inside the allocated share at full inner width, and contribute zero to the column's natural height exactly like a row counts flex width. This closes the documented "vertical flex is not yet implemented" gap; the Flex rustdoc and the widgets.md entry now describe both axes. Add scripts/style-check.sh and a make stylecheck target, wired into CI: mechanical verification of the grep-checkable subset of code_style_guide.md — tab indentation and spaces inside attribute brackets — with comment lines skipped so prose may cite raw attribute syntax. To turn the check on green, the 82 unspaced attribute sites across 24 files (#[test], #[derive(...)], #[cfg(...)], #[allow(...)]) were normalized to the spaced form. CONTRIBUTING and the style guide reference the new target; brace placement and paren spacing remain review concerns.
This commit is contained in:
16
src/app.rs
16
src/app.rs
@@ -23,7 +23,7 @@ pub enum ToplevelEvent
|
||||
}
|
||||
|
||||
/// Wayland shell mode for the application surface.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
|
||||
pub enum ShellMode
|
||||
{
|
||||
/// Normal application window using xdg-shell protocol.
|
||||
@@ -41,7 +41,7 @@ pub enum ShellMode
|
||||
}
|
||||
|
||||
/// Layer-shell layer position.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
|
||||
pub enum Layer
|
||||
{
|
||||
/// Below normal windows (wallpapers, desktop backgrounds).
|
||||
@@ -72,7 +72,7 @@ impl Layer
|
||||
|
||||
/// Layer-shell anchor edges.
|
||||
/// Determines which screen edges the surface is attached to.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
|
||||
pub struct Anchor
|
||||
{
|
||||
pub top: bool,
|
||||
@@ -115,13 +115,13 @@ impl Anchor
|
||||
/// overlay list between frames: if the same `OverlayId` is returned from
|
||||
/// [`App::overlays`] on consecutive frames the underlying Wayland surface
|
||||
/// and its internal state are kept; if it disappears the surface is destroyed.
|
||||
#[derive( Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord )]
|
||||
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord ) ]
|
||||
pub struct OverlayId( pub u32 );
|
||||
|
||||
/// Stable identifier for a subsurface, used to diff the list returned by
|
||||
/// [`App::subsurfaces`] between frames the same way [`OverlayId`] diffs
|
||||
/// overlays.
|
||||
#[derive( Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord )]
|
||||
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord ) ]
|
||||
pub struct SubsurfaceId( pub u32 );
|
||||
|
||||
/// Which surface a [`SubsurfaceSpec`] is composited as a child of. `Main`
|
||||
@@ -129,7 +129,7 @@ pub struct SubsurfaceId( pub u32 );
|
||||
/// parents to one of the [`App::overlays`] surfaces, so a slide can ride
|
||||
/// above app windows the way an overlay panel does. An `Overlay` parent that
|
||||
/// is absent or not yet configured is skipped for that frame.
|
||||
#[derive( Debug, Clone, Copy, PartialEq, Eq )]
|
||||
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
|
||||
pub enum SubsurfaceParent
|
||||
{
|
||||
Main,
|
||||
@@ -138,7 +138,7 @@ pub enum SubsurfaceParent
|
||||
|
||||
/// One of the surfaces an [`App`] can target with an invalidation. Used inside
|
||||
/// [`InvalidationScope::Only`] to name the affected surfaces.
|
||||
#[derive( Debug, Clone, Copy, PartialEq, Eq, Hash )]
|
||||
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
|
||||
pub enum SurfaceTarget
|
||||
{
|
||||
/// The application's main surface (the one returned by [`App::view`]).
|
||||
@@ -153,7 +153,7 @@ pub enum SurfaceTarget
|
||||
/// to let the runtime skip redraws on surfaces whose contents could not
|
||||
/// possibly have changed by the message in question. On a shell with many
|
||||
/// overlays most messages only touch one of them, so the savings are large.
|
||||
#[derive( Debug, Clone )]
|
||||
#[ derive( Debug, Clone ) ]
|
||||
pub enum InvalidationScope
|
||||
{
|
||||
/// Treat every surface as potentially affected (safe default).
|
||||
|
||||
@@ -56,7 +56,7 @@ pub struct AppData<A: App>
|
||||
/// failed to initialise or `LTK_FORCE_SOFTWARE=1` — every surface then
|
||||
/// falls back to the SHM path.
|
||||
pub egl_context: Option<Arc<EglContext>>,
|
||||
#[allow(dead_code)]
|
||||
#[ allow( dead_code ) ]
|
||||
pub xdg_shell: Option<XdgShell>,
|
||||
/// Shared layer-shell binding used for the main surface and every
|
||||
/// overlay. `None` when the compositor does not advertise the protocol.
|
||||
@@ -289,7 +289,7 @@ impl<A: App> AppData<A>
|
||||
/// refers to an overlay that is not currently registered — callers must
|
||||
/// only pass focus values obtained from [`focus_for_surface`] or from the
|
||||
/// per-device focus fields, which the run loop keeps in sync.
|
||||
#[allow( dead_code )]
|
||||
#[ allow( dead_code ) ]
|
||||
pub( crate ) fn surface( &self, focus: SurfaceFocus ) -> &SurfaceState<A::Message>
|
||||
{
|
||||
match focus
|
||||
@@ -313,7 +313,7 @@ impl<A: App> AppData<A>
|
||||
}
|
||||
|
||||
/// Mutable counterpart of [`surface`].
|
||||
#[allow( dead_code )]
|
||||
#[ allow( dead_code ) ]
|
||||
pub( crate ) fn surface_mut( &mut self, focus: SurfaceFocus ) -> &mut SurfaceState<A::Message>
|
||||
{
|
||||
match focus
|
||||
|
||||
@@ -34,18 +34,18 @@ use crate::widget::LaidOutWidget;
|
||||
/// `Main` refers to the application's main surface (xdg window or layer
|
||||
/// shell). `Overlay( id )` refers to an auxiliary layer-shell surface created
|
||||
/// from an entry in [`crate::app::App::overlays`].
|
||||
#[derive( Debug, Clone, Copy, PartialEq, Eq, Hash )]
|
||||
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
|
||||
pub( crate ) enum SurfaceFocus
|
||||
{
|
||||
Main,
|
||||
#[allow( dead_code )]
|
||||
#[ allow( dead_code ) ]
|
||||
Overlay( OverlayId ),
|
||||
}
|
||||
|
||||
/// Configuration for a layer-shell surface, used both for the main surface
|
||||
/// (when the app uses [`crate::app::ShellMode::Layer`]) and for each overlay
|
||||
/// returned by [`crate::app::App::overlays`].
|
||||
#[derive( Clone )]
|
||||
#[ derive( Clone ) ]
|
||||
pub( crate ) struct LayerConfig
|
||||
{
|
||||
pub layer: Layer,
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::types::{ Length, Rect };
|
||||
|
||||
pub const TOOLTIP_DELAY: std::time::Duration = std::time::Duration::from_millis( 600 );
|
||||
|
||||
#[derive( Clone )]
|
||||
#[ derive( Clone ) ]
|
||||
pub struct TooltipPending
|
||||
{
|
||||
pub focus: SurfaceFocus,
|
||||
@@ -18,7 +18,7 @@ pub struct TooltipPending
|
||||
pub anchor: Rect,
|
||||
}
|
||||
|
||||
#[derive( Clone )]
|
||||
#[ derive( Clone ) ]
|
||||
pub struct TooltipVisible
|
||||
{
|
||||
pub focus: SurfaceFocus,
|
||||
|
||||
@@ -160,11 +160,14 @@ impl<Msg: Clone> Column<Msg>
|
||||
|
||||
fn content_h( &self, inner_w: f32, canvas: &Canvas ) -> f32
|
||||
{
|
||||
// Spacers contribute 0 to natural height; spacing still applies between all children.
|
||||
// Spacers and flex children contribute 0 to natural height (their
|
||||
// real height is leftover distribution, mirroring how a row counts
|
||||
// flex width); spacing still applies between all children.
|
||||
self.children.iter()
|
||||
.map( |c| match c
|
||||
{
|
||||
Element::Spacer( s ) => s.resolved_height( canvas ).unwrap_or( 0.0 ),
|
||||
Element::Flex( _ ) => 0.0,
|
||||
other => other.preferred_size( inner_w, canvas ).1,
|
||||
} )
|
||||
.sum::<f32>()
|
||||
@@ -229,6 +232,7 @@ impl<Msg: Clone> Column<Msg>
|
||||
{
|
||||
Element::Spacer( s ) if s.resolved_height( canvas ).is_none() => s.weight,
|
||||
Element::Scroll( s ) if s.axis.allows_y() => 1,
|
||||
Element::Flex( f ) => f.weight,
|
||||
_ => 0,
|
||||
} )
|
||||
.sum();
|
||||
@@ -237,6 +241,9 @@ impl<Msg: Clone> Column<Msg>
|
||||
.map( |c|
|
||||
{
|
||||
if matches!( c, Element::Scroll( s ) if s.axis.allows_y() )
|
||||
{
|
||||
0.0
|
||||
} else if matches!( c, Element::Flex( _ ) )
|
||||
{
|
||||
0.0
|
||||
} else if let Element::Spacer( s ) = c {
|
||||
@@ -251,7 +258,8 @@ impl<Msg: Clone> Column<Msg>
|
||||
let avail_h = rect.height - pad * 2.0;
|
||||
let avail_spare = ( avail_h - fixed_h ).max( 0.0 );
|
||||
|
||||
// `center_y` only applies when there are no spacers.
|
||||
// `center_y` only applies when there are no flexible children
|
||||
// (weight-only spacers, y-scrolls, flex wrappers).
|
||||
let start_y = if total_weight == 0 && self.center_y
|
||||
{
|
||||
rect.y + pad + avail_spare / 2.0
|
||||
@@ -290,6 +298,16 @@ impl<Msg: Clone> Column<Msg>
|
||||
};
|
||||
( inner_w, h )
|
||||
},
|
||||
Element::Flex( f ) =>
|
||||
{
|
||||
let h = if total_weight > 0
|
||||
{
|
||||
avail_spare * f.weight as f32 / total_weight as f32
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
( inner_w, h )
|
||||
},
|
||||
other => other.preferred_size( inner_w, canvas ),
|
||||
};
|
||||
let x = if self.align_center_x && !matches!( child, Element::Spacer( _ ) )
|
||||
@@ -438,4 +456,52 @@ mod tests
|
||||
let col = column::<()>().padding( 0.0 ).max_width( Length::vmin( 20.0 ) );
|
||||
assert_eq!( col.inner_w( 200.0, &canvas ), 120.0 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn flex_child_takes_leftover_height()
|
||||
{
|
||||
// A 30 px fixed spacer and one flex child in a 100 px rect:
|
||||
// the flex gets the remaining 70 px at full inner width.
|
||||
let canvas = make_canvas();
|
||||
let col = column::<()>()
|
||||
.padding( 0.0 )
|
||||
.spacing( 0.0 )
|
||||
.push( crate::spacer().height( 30.0 ) )
|
||||
.push( crate::flex( crate::spacer() ) );
|
||||
let rect = crate::types::Rect { x: 0.0, y: 0.0, width: 200.0, height: 100.0 };
|
||||
let rects = col.layout( rect, &canvas );
|
||||
assert_eq!( rects.len(), 2 );
|
||||
assert!( ( rects[1].0.height - 70.0 ).abs() < 0.01 );
|
||||
assert!( ( rects[1].0.width - 200.0 ).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn flex_children_split_leftover_by_weight()
|
||||
{
|
||||
// Two flex children weighted 1:3 share 100 px as 25 / 75.
|
||||
let canvas = make_canvas();
|
||||
let col = column::<()>()
|
||||
.padding( 0.0 )
|
||||
.spacing( 0.0 )
|
||||
.push( crate::flex( crate::spacer() ) )
|
||||
.push( crate::flex( crate::spacer() ).weight( 3 ) );
|
||||
let rect = crate::types::Rect { x: 0.0, y: 0.0, width: 200.0, height: 100.0 };
|
||||
let rects = col.layout( rect, &canvas );
|
||||
assert!( ( rects[0].0.height - 25.0 ).abs() < 0.01 );
|
||||
assert!( ( rects[1].0.height - 75.0 ).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn flex_contributes_zero_to_natural_height()
|
||||
{
|
||||
// Natural height counts only the fixed spacer, like row width math.
|
||||
let canvas = make_canvas();
|
||||
let col = column::<()>()
|
||||
.padding( 0.0 )
|
||||
.spacing( 0.0 )
|
||||
.push( crate::spacer().height( 30.0 ) )
|
||||
.push( crate::flex( crate::spacer() ) );
|
||||
let ( _, h ) = col.preferred_size( 200.0, &canvas );
|
||||
assert_eq!( h, 30.0 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,13 @@ pub struct WrapGrid<Msg: Clone>
|
||||
{
|
||||
/// Child widgets laid out in row-major order.
|
||||
pub( crate ) children: Vec<Element<Msg>>,
|
||||
/// Number of columns per row.
|
||||
/// Number of columns per row. Ignored when `min_cell_width` is set.
|
||||
pub( crate ) columns: usize,
|
||||
/// Adaptive mode: derive the column count from the available width
|
||||
/// so every cell is at least this wide. See [`grid_min_cell`].
|
||||
pub( crate ) min_cell_width: Option<Length>,
|
||||
/// Upper bound on the derived column count in adaptive mode.
|
||||
pub( crate ) max_columns: Option<usize>,
|
||||
/// Horizontal gap between cells.
|
||||
pub( crate ) spacing_x: Length,
|
||||
/// Vertical gap between rows.
|
||||
@@ -93,6 +98,36 @@ impl<Msg: Clone> WrapGrid<Msg>
|
||||
self
|
||||
}
|
||||
|
||||
/// Cap the column count derived by [`grid_min_cell`] so cells stop
|
||||
/// multiplying on very wide surfaces and grow instead. No effect on
|
||||
/// a fixed-column [`grid`].
|
||||
pub fn max_columns( mut self, n: usize ) -> Self
|
||||
{
|
||||
self.max_columns = Some( n );
|
||||
self
|
||||
}
|
||||
|
||||
/// Column count for the given inner width: fixed, or derived from
|
||||
/// `min_cell_width` (as many columns as fit at least that wide,
|
||||
/// never fewer than one, capped by `max_columns`).
|
||||
fn effective_columns( &self, inner_w: f32, sx: f32, canvas: &Canvas ) -> usize
|
||||
{
|
||||
match self.min_cell_width
|
||||
{
|
||||
Some( m ) =>
|
||||
{
|
||||
let m = m.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT ).max( 1.0 );
|
||||
let cols = ( ( ( inner_w + sx ) / ( m + sx ) ).floor() as usize ).max( 1 );
|
||||
match self.max_columns
|
||||
{
|
||||
Some( cap ) => cols.min( cap.max( 1 ) ),
|
||||
None => cols,
|
||||
}
|
||||
}
|
||||
None => self.columns,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolved( &self, canvas: &Canvas ) -> ( f32, f32, f32 )
|
||||
{
|
||||
let vp = canvas.viewport_layout();
|
||||
@@ -107,13 +142,13 @@ impl<Msg: Clone> WrapGrid<Msg>
|
||||
/// Compute the preferred size given an available width.
|
||||
pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> (f32, f32)
|
||||
{
|
||||
if self.children.is_empty() || self.columns == 0
|
||||
let ( sx, sy, pad ) = self.resolved( canvas );
|
||||
let inner_w = (max_width - pad * 2.0).max( 0.0 );
|
||||
let cols = self.effective_columns( inner_w, sx, canvas );
|
||||
if self.children.is_empty() || cols == 0
|
||||
{
|
||||
return ( max_width, 0.0 );
|
||||
}
|
||||
let ( sx, sy, pad ) = self.resolved( canvas );
|
||||
let cols = self.columns;
|
||||
let inner_w = (max_width - pad * 2.0).max( 0.0 );
|
||||
let cell_w = (inner_w - sx * (cols as f32 - 1.0)).max( 0.0 ) / cols as f32;
|
||||
let row_count = (self.children.len() + cols - 1) / cols;
|
||||
|
||||
@@ -135,13 +170,13 @@ impl<Msg: Clone> WrapGrid<Msg>
|
||||
/// Compute child rects. Returns `(child_rect, index_in_children)` pairs.
|
||||
pub fn layout( &self, rect: Rect, canvas: &Canvas ) -> Vec<(Rect, usize)>
|
||||
{
|
||||
if self.children.is_empty() || self.columns == 0
|
||||
let ( sx, sy, pad ) = self.resolved( canvas );
|
||||
let inner_w = (rect.width - pad * 2.0).max( 0.0 );
|
||||
let cols = self.effective_columns( inner_w, sx, canvas );
|
||||
if self.children.is_empty() || cols == 0
|
||||
{
|
||||
return Vec::new();
|
||||
}
|
||||
let ( sx, sy, pad ) = self.resolved( canvas );
|
||||
let cols = self.columns;
|
||||
let inner_w = (rect.width - pad * 2.0).max( 0.0 );
|
||||
let cell_w = (inner_w - sx * (cols as f32 - 1.0)).max( 0.0 ) / cols as f32;
|
||||
let x0 = rect.x + pad;
|
||||
let mut y = rect.y + pad;
|
||||
@@ -185,6 +220,8 @@ impl<Msg: Clone> WrapGrid<Msg>
|
||||
{
|
||||
children: self.children.into_iter().map( |c| c.map_arc( f ) ).collect(),
|
||||
columns: self.columns,
|
||||
min_cell_width: self.min_cell_width,
|
||||
max_columns: self.max_columns,
|
||||
spacing_x: self.spacing_x,
|
||||
spacing_y: self.spacing_y,
|
||||
padding: self.padding,
|
||||
@@ -201,7 +238,7 @@ impl<Msg: Clone + 'static> From<WrapGrid<Msg>> for Element<Msg>
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[ cfg( test ) ]
|
||||
mod tests
|
||||
{
|
||||
use super::*;
|
||||
@@ -221,7 +258,7 @@ mod tests
|
||||
|
||||
// --- preferred_size ---
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn empty_grid_height_is_zero()
|
||||
{
|
||||
let g: WrapGrid<()> = grid( 4 );
|
||||
@@ -229,7 +266,7 @@ mod tests
|
||||
assert_eq!( h, 0.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn preferred_width_equals_max_width()
|
||||
{
|
||||
let g = spacer_grid( 4, 8, 0.0, 0.0 );
|
||||
@@ -239,7 +276,7 @@ mod tests
|
||||
|
||||
// --- layout: cell widths ---
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn cell_width_no_spacing_no_padding()
|
||||
{
|
||||
// 400px / 4 cols = 100px each
|
||||
@@ -251,7 +288,7 @@ mod tests
|
||||
for ( r, _ ) in &rects { assert!( (r.width - 100.0).abs() < 0.01 ); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn cell_width_with_spacing()
|
||||
{
|
||||
// (400 - 3 * 10) / 4 = 370 / 4 = 92.5
|
||||
@@ -262,7 +299,7 @@ mod tests
|
||||
for ( r, _ ) in &rects { assert!( (r.width - 92.5).abs() < 0.01 ); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn cell_width_with_padding()
|
||||
{
|
||||
// inner = 400 - 2*20 = 360; 360 / 4 = 90
|
||||
@@ -275,7 +312,7 @@ mod tests
|
||||
|
||||
// --- layout: child count and indices ---
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn layout_yields_one_rect_per_child()
|
||||
{
|
||||
let g = spacer_grid( 4, 7, 0.0, 0.0 );
|
||||
@@ -285,7 +322,7 @@ mod tests
|
||||
assert_eq!( rects.len(), 7 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn layout_indices_are_sequential()
|
||||
{
|
||||
let g = spacer_grid( 3, 5, 0.0, 0.0 );
|
||||
@@ -298,7 +335,7 @@ mod tests
|
||||
|
||||
// --- layout: column x-positions ---
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn column_x_positions_no_spacing()
|
||||
{
|
||||
// 300px / 3 cols = 100px each, starting at x=0
|
||||
@@ -312,7 +349,7 @@ mod tests
|
||||
assert!( (xs[2] - 200.0).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn column_x_positions_with_spacing()
|
||||
{
|
||||
// (300 - 2*10) / 3 = 280/3 ≈ 93.33; x[0]=0, x[1]=103.33, x[2]=206.67
|
||||
@@ -329,7 +366,7 @@ mod tests
|
||||
|
||||
// --- layout: partial last row ---
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn partial_last_row_has_correct_count()
|
||||
{
|
||||
// 7 children, 4 cols => row 0: 4, row 1: 3.
|
||||
@@ -343,7 +380,7 @@ mod tests
|
||||
|
||||
// --- layout: rect origin offset ---
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn layout_respects_rect_origin()
|
||||
{
|
||||
let g = spacer_grid( 2, 2, 0.0, 0.0 );
|
||||
@@ -356,7 +393,7 @@ mod tests
|
||||
|
||||
// --- layout: centre_last_row ---
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn last_row_centred_when_partial()
|
||||
{
|
||||
// 3 children, 2 cols => row 0: 2 items, row 1: 1 item centred.
|
||||
@@ -368,7 +405,7 @@ mod tests
|
||||
assert!( (rects[2].0.x - 50.0).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn centre_last_row_noop_on_full_row()
|
||||
{
|
||||
// 4 children, 2 cols => both rows full; nothing to centre.
|
||||
@@ -380,7 +417,7 @@ mod tests
|
||||
assert!( (rects[3].0.x - 100.0).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn centre_last_row_off_by_default()
|
||||
{
|
||||
// Same case as above but without the flag — last item stays at x=0.
|
||||
@@ -390,6 +427,71 @@ mod tests
|
||||
let rects = g.layout( rect, &c );
|
||||
assert!( rects[2].0.x.abs() < 0.01 );
|
||||
}
|
||||
|
||||
// --- adaptive column count (grid_min_cell) ---
|
||||
|
||||
fn min_cell_grid( min: f32, n: usize, spacing: f32 ) -> WrapGrid<()>
|
||||
{
|
||||
let mut g = grid_min_cell( min ).spacing( spacing );
|
||||
for _ in 0..n { g = g.push( spacer() ); }
|
||||
g
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn min_cell_derives_columns_from_width()
|
||||
{
|
||||
// 400px / min 90 => floor(400/90) = 4 columns, cells 100px.
|
||||
let g = min_cell_grid( 90.0, 8, 0.0 );
|
||||
let c = canvas();
|
||||
let rect = Rect { x: 0.0, y: 0.0, width: 400.0, height: 400.0 };
|
||||
let rects = g.layout( rect, &c );
|
||||
for ( r, _ ) in &rects { assert!( ( r.width - 100.0 ).abs() < 0.01 ); }
|
||||
// 8 children in 4 columns => index 3 sits in the last column and
|
||||
// index 4 wraps back to x = 0 on the next row.
|
||||
assert!( ( rects[3].0.x - 300.0 ).abs() < 0.01 );
|
||||
assert!( rects[4].0.x.abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn min_cell_accounts_for_spacing()
|
||||
{
|
||||
// With spacing 10: floor((300+10)/(90+10)) = 3 columns; the resulting
|
||||
// cells ((300 - 2*10)/3 ≈ 93.3) still clear the 90px minimum.
|
||||
let g = min_cell_grid( 90.0, 3, 10.0 );
|
||||
let c = canvas();
|
||||
let rect = Rect { x: 0.0, y: 0.0, width: 300.0, height: 100.0 };
|
||||
let rects = g.layout( rect, &c );
|
||||
assert!( rects.iter().all( |( r, _ )| r.width >= 90.0 ) );
|
||||
assert!( ( rects[0].0.y - rects[2].0.y ).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn min_cell_never_below_one_column()
|
||||
{
|
||||
// Narrower than the minimum still lays out a single column:
|
||||
// both children sit flush left at the full 80px width.
|
||||
let g = min_cell_grid( 120.0, 2, 0.0 );
|
||||
let c = canvas();
|
||||
let rect = Rect { x: 0.0, y: 0.0, width: 80.0, height: 400.0 };
|
||||
let rects = g.layout( rect, &c );
|
||||
assert_eq!( rects.len(), 2 );
|
||||
assert!( rects[1].0.x.abs() < 0.01 );
|
||||
for ( r, _ ) in &rects { assert!( ( r.width - 80.0 ).abs() < 0.01 ); }
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn max_columns_caps_adaptive_count()
|
||||
{
|
||||
// 400px / min 90 would give 4; the cap keeps 2 and cells grow to 200.
|
||||
let g = min_cell_grid( 90.0, 4, 0.0 ).max_columns( 2 );
|
||||
let c = canvas();
|
||||
let rect = Rect { x: 0.0, y: 0.0, width: 400.0, height: 400.0 };
|
||||
let rects = g.layout( rect, &c );
|
||||
for ( r, _ ) in &rects { assert!( ( r.width - 200.0 ).abs() < 0.01 ); }
|
||||
// Two columns: index 1 fills the second column, index 2 wraps.
|
||||
assert!( ( rects[1].0.x - 200.0 ).abs() < 0.01 );
|
||||
assert!( rects[2].0.x.abs() < 0.01 );
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a grid layout with the given number of columns.
|
||||
@@ -410,6 +512,50 @@ pub fn grid<Msg: Clone>( columns: usize ) -> WrapGrid<Msg>
|
||||
{
|
||||
children: Vec::new(),
|
||||
columns,
|
||||
min_cell_width: None,
|
||||
max_columns: None,
|
||||
spacing_x: Length::px( 8.0 ),
|
||||
spacing_y: Length::px( 8.0 ),
|
||||
padding: Length::px( 0.0 ),
|
||||
centre_last_row: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an adaptive grid: the column count is derived at layout time
|
||||
/// from the available width, fitting as many columns as possible while
|
||||
/// keeping every cell at least `min_cell_width` wide (never fewer than
|
||||
/// one). Cells then share the width equally, so they range between
|
||||
/// `min_cell_width` and just under twice it — cap the count with
|
||||
/// [`max_columns`](WrapGrid::max_columns) to let cells grow instead on
|
||||
/// very wide surfaces.
|
||||
///
|
||||
/// Accepts logical `f32` pixels or any [`Length`] (e.g.
|
||||
/// `Length::fluid( 96.0 )` for a threshold that scales with the
|
||||
/// surface).
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use std::sync::Arc;
|
||||
/// # use ltk::{ grid_min_cell, icon_button, scroll, Element };
|
||||
/// # #[ derive( Clone ) ] enum Msg { Open( usize ) }
|
||||
/// # fn _ex( data: Arc<Vec<u8>>, w: u32, h: u32 ) -> Element<Msg> {
|
||||
/// scroll(
|
||||
/// grid_min_cell( 96.0 )
|
||||
/// .max_columns( 8 )
|
||||
/// .spacing( 12.0 )
|
||||
/// .push( icon_button( data, w, h ).on_press( Msg::Open( 0 ) ) )
|
||||
/// // ...
|
||||
/// )
|
||||
/// .into()
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn grid_min_cell<Msg: Clone>( min_cell_width: impl Into<Length> ) -> WrapGrid<Msg>
|
||||
{
|
||||
WrapGrid
|
||||
{
|
||||
children: Vec::new(),
|
||||
columns: 0,
|
||||
min_cell_width: Some( min_cell_width.into() ),
|
||||
max_columns: None,
|
||||
spacing_x: Length::px( 8.0 ),
|
||||
spacing_y: Length::px( 8.0 ),
|
||||
padding: Length::px( 0.0 ),
|
||||
|
||||
11
src/lib.rs
11
src/lib.rs
@@ -33,7 +33,7 @@
|
||||
//! ```rust,no_run
|
||||
//! use ltk::{App, Element, column, text, button, spacer, Color, ButtonVariant};
|
||||
//!
|
||||
//! #[derive(Clone)]
|
||||
//! #[ derive( Clone ) ]
|
||||
//! enum Msg { Quit }
|
||||
//!
|
||||
//! struct MyApp;
|
||||
@@ -101,7 +101,8 @@
|
||||
//! - [`column()`] — vertical flow.
|
||||
//! - [`row()`] — horizontal flow.
|
||||
//! - [`stack()`] — z-order overlay with per-child alignment.
|
||||
//! - [`grid()`] — fixed-column-count wrapping grid.
|
||||
//! - [`grid()`] — fixed-column-count wrapping grid; [`grid_min_cell()`]
|
||||
//! derives the column count from the width instead.
|
||||
//! - [`spacer()`] — invisible flexible filler.
|
||||
//!
|
||||
//! See [`layouts`] for the grouped landing page.
|
||||
@@ -380,7 +381,7 @@ pub use layout::column::{ Column, column };
|
||||
pub use layout::row::{ Row, row };
|
||||
pub use layout::stack::{ Stack, stack, HAlign, VAlign };
|
||||
// push_aligned_margin is available as a method on Stack — no separate re-export needed.
|
||||
pub use layout::wrap_grid::{ WrapGrid, grid };
|
||||
pub use layout::wrap_grid::{ WrapGrid, grid, grid_min_cell };
|
||||
pub use widget::scroll::{ scroll, ScrollAxis };
|
||||
pub use widget::viewport::{ Viewport, viewport };
|
||||
pub use widget::carousel::{ Carousel, carousel };
|
||||
@@ -458,7 +459,7 @@ pub mod layouts
|
||||
Column, column,
|
||||
Row, row,
|
||||
Stack, stack, HAlign, VAlign,
|
||||
WrapGrid, grid,
|
||||
WrapGrid, grid, grid_min_cell,
|
||||
Spacer, spacer,
|
||||
};
|
||||
}
|
||||
@@ -490,7 +491,7 @@ pub mod window
|
||||
button, icon_button, text, text_edit, img_widget,
|
||||
container, checkbox, radio, toggle, separator,
|
||||
progress_bar, list_item, slider, vslider, scroll, viewport,
|
||||
column, row, stack, grid, spacer,
|
||||
column, row, stack, grid, grid_min_cell, spacer,
|
||||
TextAlign, SliderAxis,
|
||||
run,
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@ use crate::render::Canvas;
|
||||
use crate::types::{ Rect, WidgetId };
|
||||
use crate::widget::Element;
|
||||
|
||||
#[cfg(test)]
|
||||
#[ cfg( test ) ]
|
||||
mod tests;
|
||||
|
||||
pub struct Carousel<Msg: Clone>
|
||||
|
||||
@@ -17,7 +17,7 @@ fn three_spacer_carousel() -> Carousel<()>
|
||||
.push( spacer() )
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn empty_layout_is_empty()
|
||||
{
|
||||
let c: Carousel<()> = carousel();
|
||||
@@ -25,7 +25,7 @@ fn empty_layout_is_empty()
|
||||
assert!( c.layout( rect, &canvas() ).is_empty() );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn first_child_centred_at_offset_zero()
|
||||
{
|
||||
let c = three_spacer_carousel();
|
||||
@@ -36,7 +36,7 @@ fn first_child_centred_at_offset_zero()
|
||||
assert!( ( rects[0].0.width - 80.0 ).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn children_strided_by_child_width_plus_gap()
|
||||
{
|
||||
let c = carousel::<()>()
|
||||
@@ -50,7 +50,7 @@ fn children_strided_by_child_width_plus_gap()
|
||||
assert!( ( rects[1].0.x - ( rects[0].0.x + 108.0 ) ).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn snap_offset_centres_target_index()
|
||||
{
|
||||
let c = three_spacer_carousel();
|
||||
@@ -58,7 +58,7 @@ fn snap_offset_centres_target_index()
|
||||
assert!( ( c.snap_offset( 100.0, 2 ) - ( -160.0 ) ).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn focused_index_rounds_to_nearest_tile()
|
||||
{
|
||||
let mut c = three_spacer_carousel();
|
||||
@@ -68,7 +68,7 @@ fn focused_index_rounds_to_nearest_tile()
|
||||
assert_eq!( c.focused_index( 100.0 ), 1 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn focused_index_clamps_to_valid_range()
|
||||
{
|
||||
let mut c = three_spacer_carousel();
|
||||
@@ -78,7 +78,7 @@ fn focused_index_clamps_to_valid_range()
|
||||
assert_eq!( c.focused_index( 100.0 ), 2 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn offset_shifts_all_children_horizontally()
|
||||
{
|
||||
let c = three_spacer_carousel().offset( -25.0 );
|
||||
@@ -90,7 +90,7 @@ fn offset_shifts_all_children_horizontally()
|
||||
assert!( ( rects[1].0.x - rects[0].0.x - 80.0 ).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn focused_width_frac_is_clamped_to_unit_range()
|
||||
{
|
||||
let c: Carousel<()> = carousel().focused_width_frac( 5.0 ).push( spacer() );
|
||||
@@ -99,7 +99,7 @@ fn focused_width_frac_is_clamped_to_unit_range()
|
||||
assert!( c2.focused_width_frac > 0.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn children_use_full_rect_height()
|
||||
{
|
||||
let c = three_spacer_carousel();
|
||||
|
||||
@@ -7,7 +7,7 @@ use super::Element;
|
||||
|
||||
mod theme;
|
||||
|
||||
#[cfg(test)]
|
||||
#[ cfg( test ) ]
|
||||
mod tests;
|
||||
|
||||
/// A two-state opt-in control with a square box and a check glyph.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn checkbox_default_state()
|
||||
{
|
||||
let c = checkbox::<()>( true );
|
||||
@@ -11,7 +11,7 @@ fn checkbox_default_state()
|
||||
assert!( c.on_toggle.is_none() );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn checkbox_unchecked()
|
||||
{
|
||||
let c = checkbox::<()>( false );
|
||||
|
||||
@@ -4,16 +4,19 @@
|
||||
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.
|
||||
/// Wraps an [`Element`] so its parent treats it like a
|
||||
/// [`Spacer`](crate::layout::spacer::Spacer) for leftover-space
|
||||
/// distribution, but draws the child inside the allocated rect: leftover
|
||||
/// **width** inside a [`Row`](crate::layout::row::Row), leftover
|
||||
/// **height** inside a [`Column`](crate::layout::column::Column).
|
||||
///
|
||||
/// 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.
|
||||
/// Use this to make a non-trivial child fill remaining space without
|
||||
/// resorting to a hard-coded size. The parent computes how much of its
|
||||
/// main axis 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 };
|
||||
@@ -30,10 +33,6 @@ use super::Element;
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// 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( crate ) child: Box<Element<Msg>>,
|
||||
@@ -51,9 +50,9 @@ impl<Msg: Clone> Flex<Msg>
|
||||
}
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
/// Relative weight when sharing leftover space with other flex / spacer
|
||||
/// children in the same row or column (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;
|
||||
|
||||
@@ -9,7 +9,7 @@ use super::Element;
|
||||
|
||||
mod theme;
|
||||
|
||||
#[cfg(test)]
|
||||
#[ cfg( test ) ]
|
||||
mod tests;
|
||||
|
||||
/// A row inside a list with a primary label and optional subtitle / trailing
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn list_item_default()
|
||||
{
|
||||
let l = list_item::<()>( "Test" );
|
||||
|
||||
@@ -7,7 +7,7 @@ use super::Element;
|
||||
|
||||
mod theme;
|
||||
|
||||
#[cfg(test)]
|
||||
#[ cfg( test ) ]
|
||||
mod tests;
|
||||
|
||||
/// A linear progress indicator for determinate operations.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn value_clamped_on_creation()
|
||||
{
|
||||
let p = progress_bar( 1.5 );
|
||||
@@ -12,7 +12,7 @@ fn value_clamped_on_creation()
|
||||
assert_eq!( p.value, 0.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn preferred_width_fills_available()
|
||||
{
|
||||
let p = progress_bar( 0.5 );
|
||||
|
||||
@@ -7,7 +7,7 @@ use super::Element;
|
||||
|
||||
mod theme;
|
||||
|
||||
#[cfg(test)]
|
||||
#[ cfg( test ) ]
|
||||
mod tests;
|
||||
|
||||
/// One option inside a mutually-exclusive group.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn radio_default_state()
|
||||
{
|
||||
let r = radio::<()>( true );
|
||||
@@ -11,7 +11,7 @@ fn radio_default_state()
|
||||
assert!( r.on_select.is_none() );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn radio_unselected()
|
||||
{
|
||||
let r = radio::<()>( false );
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::render::Canvas;
|
||||
use crate::types::WidgetId;
|
||||
use crate::widget::Element;
|
||||
|
||||
#[cfg(test)]
|
||||
#[ cfg( test ) ]
|
||||
mod tests;
|
||||
|
||||
/// Which axes a `Scroll` viewport allows to move along. Determines
|
||||
|
||||
@@ -3,46 +3,46 @@
|
||||
|
||||
use super::clamp_offset;
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn offset_zero_when_content_fits()
|
||||
{
|
||||
// Content shorter than viewport — no scrolling possible
|
||||
assert_eq!( clamp_offset( 50.0, 300.0, 500.0 ), 0.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn offset_clamped_to_zero_when_negative()
|
||||
{
|
||||
assert_eq!( clamp_offset( -10.0, 600.0, 400.0 ), 0.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn offset_clamped_to_max()
|
||||
{
|
||||
// max = 600 - 400 = 200; offset 999 → clamped to 200
|
||||
assert_eq!( clamp_offset( 999.0, 600.0, 400.0 ), 200.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn offset_within_range_unchanged()
|
||||
{
|
||||
// max = 600 - 400 = 200; offset 100 stays 100
|
||||
assert_eq!( clamp_offset( 100.0, 600.0, 400.0 ), 100.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn zero_offset_stays_zero()
|
||||
{
|
||||
assert_eq!( clamp_offset( 0.0, 600.0, 400.0 ), 0.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn exact_max_offset_is_valid()
|
||||
{
|
||||
assert_eq!( clamp_offset( 200.0, 600.0, 400.0 ), 200.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn content_equal_to_viewport_gives_zero_max()
|
||||
{
|
||||
// No overflow — max = 0
|
||||
|
||||
@@ -142,5 +142,5 @@ impl<Msg: Clone> From<Separator> for Element<Msg>
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[ cfg( test ) ]
|
||||
mod tests;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn default_thickness_and_pad_follow_the_mode()
|
||||
{
|
||||
let s = separator();
|
||||
@@ -11,7 +11,7 @@ fn default_thickness_and_pad_follow_the_mode()
|
||||
assert_eq!( s.pad_v, None );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn explicit_zero_pad_v_is_flush_not_the_mode_default()
|
||||
{
|
||||
// The Option repr lets `0.0` mean a real flush divider, distinct from
|
||||
@@ -22,7 +22,7 @@ fn explicit_zero_pad_v_is_flush_not_the_mode_default()
|
||||
assert_eq!( s.preferred_size( 200.0, &canvas ).1, canvas.geom_px( theme::THICKNESS ) );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn preferred_height_includes_padding()
|
||||
{
|
||||
let _g = crate::TEST_GLOBALS_LOCK.lock().unwrap_or_else( |e| e.into_inner() );
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use super::*;
|
||||
use crate::types::Rect;
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn value_clamped_on_creation()
|
||||
{
|
||||
let s = slider::<()>( 1.5 );
|
||||
@@ -13,7 +13,7 @@ fn value_clamped_on_creation()
|
||||
assert_eq!( s.value, 0.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn value_from_x_left_edge()
|
||||
{
|
||||
let s = slider::<()>( 0.5 );
|
||||
@@ -22,7 +22,7 @@ fn value_from_x_left_edge()
|
||||
assert_eq!( v, 0.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn value_from_x_right_edge()
|
||||
{
|
||||
let s = slider::<()>( 0.5 );
|
||||
@@ -31,7 +31,7 @@ fn value_from_x_right_edge()
|
||||
assert_eq!( v, 1.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn value_from_x_center()
|
||||
{
|
||||
let s = slider::<()>( 0.0 );
|
||||
@@ -40,7 +40,7 @@ fn value_from_x_center()
|
||||
assert!( (v - 0.5).abs() < 0.1 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn axis_dispatch_horizontal_uses_x()
|
||||
{
|
||||
let rect = Rect { x: 0.0, y: 0.0, width: 200.0, height: 36.0 };
|
||||
@@ -53,7 +53,7 @@ fn axis_dispatch_horizontal_uses_x()
|
||||
assert_eq!( v, 1.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn axis_dispatch_vertical_uses_y()
|
||||
{
|
||||
let rect = Rect { x: 0.0, y: 0.0, width: 56.0, height: 160.0 };
|
||||
@@ -67,7 +67,7 @@ fn axis_dispatch_vertical_uses_y()
|
||||
assert_eq!( v, 1.0 );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn value_from_x_respects_thumb_pad()
|
||||
{
|
||||
let rect = Rect { x: 0.0, y: 0.0, width: 100.0, height: 36.0 };
|
||||
@@ -82,14 +82,14 @@ fn value_from_x_respects_thumb_pad()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn track_paint_default_is_none()
|
||||
{
|
||||
let s = slider::<()>( 0.5 );
|
||||
assert!( s.track_paint.is_none() );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn track_paint_builder_stores_paint()
|
||||
{
|
||||
use crate::theme::{ ColorStop, GradientSpace, LinearGradient, Paint };
|
||||
|
||||
@@ -7,7 +7,7 @@ use super::Element;
|
||||
|
||||
mod theme;
|
||||
|
||||
#[cfg(test)]
|
||||
#[ cfg( test ) ]
|
||||
mod tests;
|
||||
|
||||
/// A two-state on / off switch.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn toggle_default_state()
|
||||
{
|
||||
let t = toggle::<()>( true );
|
||||
@@ -12,7 +12,7 @@ fn toggle_default_state()
|
||||
assert!( t.label.is_none() );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ test ]
|
||||
fn toggle_off_state()
|
||||
{
|
||||
let t = toggle::<()>( false );
|
||||
|
||||
Reference in New Issue
Block a user