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

//! The order of a file chooser's folder, as Crate's.

use std::cmp::Ordering;

use super::Item;

#[ derive( Clone, Copy, Debug, Default, PartialEq, Eq ) ]
pub enum SortKey
{
	#[ default ]
	Name,
	Accessed,
	Size,
}

/// Whether each key sorts descending; each keeps its own direction, ascending until chosen otherwise.
#[ derive( Clone, Copy, Debug, Default, PartialEq, Eq ) ]
pub struct Directions
{
	pub name:     bool,
	pub accessed: bool,
	pub size:     bool,
}

impl Directions
{
	pub fn descending( self, key: SortKey ) -> bool
	{
		match key
		{
			SortKey::Name     => self.name,
			SortKey::Accessed => self.accessed,
			SortKey::Size     => self.size,
		}
	}

	fn set( &mut self, key: SortKey, descending: bool )
	{
		match key
		{
			SortKey::Name     => self.name = descending,
			SortKey::Accessed => self.accessed = descending,
			SortKey::Size     => self.size = descending,
		}
	}
}

#[ derive( Clone, Copy, Debug, Default, PartialEq, Eq ) ]
pub struct Sort
{
	pub key:        SortKey,
	pub directions: Directions,
}

impl Sort
{
	pub fn descending( self ) -> bool
	{
		self.directions.descending( self.key )
	}

	/// By `key`, in the direction it last had.
	pub fn select( self, key: SortKey ) -> Self
	{
		Sort { key, ..self }
	}

	pub fn with_direction( mut self, key: SortKey, descending: bool ) -> Self
	{
		self.directions.set( key, descending );
		self.select( key )
	}

	/// Folders always come first; the direction applies within each group, ties going by name.
	pub( crate ) fn compare( self, a: &Item, b: &Item ) -> Ordering
	{
		let by_key = match self.key
		{
			SortKey::Name     => Ordering::Equal,
			SortKey::Accessed => a.accessed.cmp( &b.accessed ),
			SortKey::Size     => if a.is_dir || b.is_dir { Ordering::Equal } else { a.size.cmp( &b.size ) },
		};
		let by_name = a.name.to_lowercase().cmp( &b.name.to_lowercase() ).then_with( || a.name.cmp( &b.name ) );
		let order   = by_key.then( by_name );
		b.is_dir.cmp( &a.is_dir ).then( if self.descending() { order.reverse() } else { order } )
	}
}