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.
118 lines
3.1 KiB
Rust
118 lines
3.1 KiB
Rust
// SPDX-License-Identifier: LGPL-2.1-only
|
||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||
|
||
use crate::types::{ Color, Rect };
|
||
use crate::render::Canvas;
|
||
use super::Element;
|
||
|
||
mod theme;
|
||
|
||
#[ cfg( test ) ]
|
||
mod tests;
|
||
|
||
/// A linear progress indicator for determinate operations.
|
||
///
|
||
/// Renders a horizontal track with a coloured fill from the left edge to
|
||
/// `value × width`. `value` is clamped to `[0.0, 1.0]` at construction.
|
||
/// For indeterminate progress (the operation has no ETA) prefer an
|
||
/// animated spinner — `ltk` has no built-in spinner widget yet; build one
|
||
/// from a [`Container`](super::container::Container) that rotates a glyph
|
||
/// while [`crate::App::is_animating`] returns `true`.
|
||
///
|
||
/// ```rust,no_run
|
||
/// # use ltk::{ column, progress_bar, text, Element };
|
||
/// # #[ derive( Clone ) ] enum Msg {}
|
||
/// # struct App { progress: f32 }
|
||
/// # impl App { fn _ex( &self ) -> Element<Msg> {
|
||
/// // In view():
|
||
/// column()
|
||
/// .push( text( format!( "Downloading… {}%", ( self.progress * 100.0 ) as u32 ) ) )
|
||
/// .push( progress_bar( self.progress ) )
|
||
/// .into()
|
||
/// # }}
|
||
/// ```
|
||
pub struct ProgressBar
|
||
{
|
||
/// Current progress in `[0.0, 1.0]`. Always clamped at construction.
|
||
pub( crate ) value: f32,
|
||
/// Fill colour. Defaults to the theme's `accent` palette slot.
|
||
pub( crate ) fill: Color,
|
||
}
|
||
|
||
impl ProgressBar
|
||
{
|
||
/// Create a progress bar at the given fraction. `value` outside
|
||
/// `[0.0, 1.0]` is clamped silently.
|
||
pub fn new( value: f32 ) -> Self
|
||
{
|
||
Self { value: value.clamp( 0.0, 1.0 ), fill: theme::fill() }
|
||
}
|
||
|
||
/// Override the fill colour. Useful for "danger" / "success" variants
|
||
/// (red for nearly-full disks, green for completed work).
|
||
pub fn color( mut self, color: Color ) -> Self
|
||
{
|
||
self.fill = color;
|
||
self
|
||
}
|
||
|
||
/// Return the preferred `(width, height)`. Width fills the available
|
||
/// `max_width`; height is the theme-defined row height.
|
||
pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> (f32, f32)
|
||
{
|
||
( max_width, canvas.geom_px( theme::HEIGHT ) )
|
||
}
|
||
|
||
pub fn draw( &self, canvas: &mut Canvas, rect: Rect )
|
||
{
|
||
let track_h = canvas.geom_px( theme::TRACK_H );
|
||
let track_y = rect.y + ( rect.height - track_h ) / 2.0;
|
||
let track_r = track_h / 2.0;
|
||
|
||
let track_rect = Rect
|
||
{
|
||
x: rect.x,
|
||
y: track_y,
|
||
width: rect.width,
|
||
height: track_h,
|
||
};
|
||
canvas.fill_rect( track_rect, theme::track_bg(), track_r );
|
||
|
||
let fill_w = rect.width * self.value;
|
||
if fill_w > 0.0
|
||
{
|
||
let fill_rect = Rect
|
||
{
|
||
x: rect.x,
|
||
y: track_y,
|
||
width: fill_w,
|
||
height: track_h,
|
||
};
|
||
canvas.fill_rect( fill_rect, self.fill, track_r );
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Create a [`ProgressBar`] at the given fraction (clamped to
|
||
/// `[0.0, 1.0]`).
|
||
///
|
||
/// ```rust,no_run
|
||
/// # use ltk::{ progress_bar, ProgressBar };
|
||
/// # struct App { download_fraction: f32 }
|
||
/// # impl App { fn _ex( &self ) -> ProgressBar {
|
||
/// progress_bar( self.download_fraction )
|
||
/// # }}
|
||
/// ```
|
||
pub fn progress_bar( value: f32 ) -> ProgressBar
|
||
{
|
||
ProgressBar::new( value )
|
||
}
|
||
|
||
impl<Msg: Clone> From<ProgressBar> for Element<Msg>
|
||
{
|
||
fn from( p: ProgressBar ) -> Self
|
||
{
|
||
Element::ProgressBar( p )
|
||
}
|
||
}
|