ltk/widget/separator/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
use crate::types::{ Color, Length, Rect };
use crate::render::Canvas;
use super::Element;
mod theme;
/// A horizontal divider line.
///
/// Renders a 1 px (default) line across the full width of its layout rect,
/// with vertical padding above and below. Use to break a column into
/// visual sections — between settings groups, list categories or content
/// blocks. The line takes the divider colour from the active theme by
/// default; override with [`Self::color`] for custom palettes.
///
/// ```rust,no_run
/// # use ltk::{ column, separator, text, Element };
/// # #[ derive( Clone ) ] enum Msg {}
/// # fn _ex() -> Element<Msg> {
/// // In view():
/// column()
/// .push( text( "General" ) )
/// .push( separator() )
/// .push( text( "Network" ) )
/// .into()
/// # }
/// ```
pub struct Separator
{
/// Stroke colour. Defaults to the theme's `divider` palette slot.
pub( crate ) color: Color,
/// Line thickness. `None` follows the process [`crate::WidgetScaling`]
/// mode; `Some( len )` pins it (including `Length::px( 0.0 )`).
pub( crate ) thickness: Option<Length>,
/// Vertical padding above and below the line, baked into
/// `preferred_size` so the divider visually centres in the row a
/// parent column allocates. `None` follows the
/// [`crate::WidgetScaling`] mode; `Some( len )` pins it — pass
/// `Length::px( 0.0 )` for a flush, padding-less divider.
pub( crate ) pad_v: Option<Length>,
}
impl Separator
{
/// Create a separator with the theme's default divider colour and
/// 1 px thickness.
pub fn new() -> Self
{
Self
{
color: theme::color(),
thickness: None,
pad_v: None,
}
}
/// Resolve the line thickness: an explicit value wins, otherwise the
/// widget-scaling default.
fn resolved_thickness( &self, canvas: &Canvas ) -> f32
{
self.thickness
.map( |l| l.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT ) )
.unwrap_or_else( || canvas.geom_px( theme::THICKNESS ) )
}
/// Resolve the vertical padding: an explicit value wins, otherwise the
/// widget-scaling default.
fn resolved_pad_v( &self, canvas: &Canvas ) -> f32
{
self.pad_v
.map( |l| l.resolve( canvas.viewport_layout(), Length::EM_BASE_DEFAULT ) )
.unwrap_or_else( || canvas.geom_px( theme::PAD_V ) )
}
/// Override the stroke colour. Useful for emphasised dividers between
/// destructive actions.
pub fn color( mut self, color: Color ) -> Self
{
self.color = color;
self
}
/// Override the line thickness. Accepts logical `f32` pixels or any
/// [`Length`]. Without this it follows the widget-scaling mode.
pub fn thickness( mut self, t: impl Into<Length> ) -> Self
{
self.thickness = Some( t.into() );
self
}
/// Override the vertical padding above and below the line. Accepts
/// logical `f32` pixels or any [`Length`]; pass `0.0` for a flush,
/// padding-less divider. Without this it follows the widget-scaling mode.
pub fn pad_v( mut self, p: impl Into<Length> ) -> Self
{
self.pad_v = Some( p.into() );
self
}
/// Return the preferred `(width, height)`. Width is `max_width`;
/// height is `thickness + 2 × pad_v`.
pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> (f32, f32)
{
( max_width, self.resolved_thickness( canvas ) + self.resolved_pad_v( canvas ) * 2.0 )
}
/// Draw the divider line into `canvas` at `rect`.
pub fn draw( &self, canvas: &mut Canvas, rect: Rect )
{
let y = rect.y + rect.height / 2.0;
let thickness = self.resolved_thickness( canvas );
canvas.draw_line( rect.x, y, rect.x + rect.width, y, self.color, thickness );
}
}
/// Create a default [`Separator`] (theme divider colour, 1 px thickness).
///
/// ```rust,no_run
/// # use ltk::{ column, separator, text, Element };
/// # #[ derive( Clone ) ] enum Msg {}
/// # fn _ex() -> Element<Msg> {
/// column()
/// .push( text( "Section A" ) )
/// .push( separator() )
/// .push( text( "Section B" ) )
/// .into()
/// # }
/// ```
pub fn separator() -> Separator
{
Separator::new()
}
impl<Msg: Clone> From<Separator> for Element<Msg>
{
fn from( s: Separator ) -> Self
{
Element::Separator( s )
}
}
#[cfg(test)]
mod tests;