Skip to main content

native_theme/model/
arrow_side.rs

1// Disclosure arrow placement convention
2
3use serde::{Deserialize, Serialize};
4
5/// Which end of an expander's header row its disclosure arrow sits at.
6///
7/// A **platform convention**, like
8/// [`DialogButtonOrder`](crate::model::DialogButtonOrder): KDE's
9/// `KCollapsibleGroupBox` and macOS's disclosure triangle lead the title,
10/// libadwaita's `AdwExpanderRow` and WinUI's `Expander` end the row with it
11/// (`docs/platform-facts.md` ยง2.27, `arrow_side`). Leading and trailing are
12/// the reading direction's: left and right in a left-to-right layout.
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
14pub enum ArrowSide {
15    /// Before the title, at the row's start -- KDE, macOS.
16    #[serde(rename = "leading")]
17    Leading,
18    /// After the title, at the row's end -- GNOME, Windows.
19    #[serde(rename = "trailing")]
20    Trailing,
21}
22
23#[cfg(test)]
24#[allow(clippy::unwrap_used, clippy::expect_used)]
25mod tests {
26    use super::*;
27
28    // TOML cannot serialize a bare enum as a top-level value; use a wrapper struct.
29    #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
30    struct Wrapper {
31        side: ArrowSide,
32    }
33
34    #[test]
35    fn serde_round_trip_both_variants() {
36        for (variant, expected_str) in [
37            (ArrowSide::Leading, "leading"),
38            (ArrowSide::Trailing, "trailing"),
39        ] {
40            let original = Wrapper { side: variant };
41            let serialized = toml::to_string(&original).unwrap();
42            assert!(serialized.contains(expected_str), "got: {serialized}");
43            let deserialized: Wrapper = toml::from_str(&serialized).unwrap();
44            assert_eq!(deserialized, original);
45        }
46    }
47
48    #[test]
49    fn an_unknown_side_is_rejected() {
50        assert!(toml::from_str::<Wrapper>(r#"side = "left""#).is_err());
51    }
52}