ltk/widget/tab_strip/
cards.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
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! The overview's grid of cards, placed by the [`TabMotion`] so a dragged card follows the finger and the others make way.

use crate::render::Canvas;
use crate::types::{ Length, Rect };
use crate::widget::Element;

use super::motion::TabMotion;

pub struct TabCards<Msg: Clone>
{
	pub( crate ) cards:   Vec<( u64, Element<Msg> )>,
	pub( crate ) columns: usize,
	pub( crate ) spacing: Length,
	pub( crate ) motion:  TabMotion,
}

impl<Msg: Clone> TabCards<Msg>
{
	pub( crate ) fn child( &self, idx: usize ) -> &Element<Msg>
	{
		&self.cards[ idx ].1
	}

	fn columns( &self ) -> usize
	{
		self.columns.max( 1 )
	}

	/// Every card as big as the tallest.
	fn cell( &self, width: f32, canvas: &Canvas ) -> ( f32, f32 )
	{
		let gap     = canvas.resolve_geom( self.spacing );
		let columns = self.columns() as f32;
		let width   = ( ( width - gap * ( columns - 1.0 ) ) / columns ).max( 0.0 );
		let height  = self.cards.iter().map( |( _, card )| card.preferred_size( width, canvas ).1 ).fold( 0.0_f32, f32::max );
		( width, height )
	}

	pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> ( f32, f32 )
	{
		let gap    = canvas.resolve_geom( self.spacing );
		let height = self.cell( max_width, canvas ).1;
		let rows   = self.cards.len().div_ceil( self.columns() ) as f32;
		( max_width, rows * height + ( rows - 1.0 ).max( 0.0 ) * gap )
	}

	/// The cards in rows, the dragged one last so it is drawn on top.
	pub fn layout( &self, rect: Rect, canvas: &Canvas ) -> Vec<( Rect, usize )>
	{
		let gap  = canvas.resolve_geom( self.spacing );
		let keys = self.cards.iter().map( |( key, _ )| *key ).collect::<Vec<_>>();
		self.motion.lay_cards( &keys, rect, self.columns(), self.cell( rect.width, canvas ), gap ).into_iter()
			.filter_map( |laid| keys.iter().position( |&key| key == laid.key ).map( |idx| ( laid.rect, idx ) ) )
			.collect()
	}

	pub( crate ) fn map_msg<U>( self, f: &crate::widget::MapFn<Msg, U> ) -> TabCards<U>
	where
		U: Clone + 'static,
		Msg: 'static,
	{
		TabCards
		{
			cards:   self.cards.into_iter().map( |( key, card )| ( key, card.map_arc( f ) ) ).collect(),
			columns: self.columns,
			spacing: self.spacing,
			motion:  self.motion,
		}
	}
}

impl<Msg: Clone> From<TabCards<Msg>> for Element<Msg>
{
	fn from( cards: TabCards<Msg> ) -> Self
	{
		Element::TabCards( cards )
	}
}