Skip to main content

retroglyph_widgets/interact/
density.rs

1//! [`Density`]: compact vs. relaxed sizing for interactive widgets.
2
3use retroglyph_core::Size;
4
5/// How much room an interactive widget's hit target should claim.
6///
7/// Not itself consulted by anything in this crate -- there are no built-in
8/// interactive widgets yet to apply it to; every widget here is a free
9/// function or a thin, stateless composition of one (see the crate's module
10/// docs). It exists so an app choosing between a phone-sized and a
11/// desktop-sized layout has one place to ask "how big should this
12/// button/row/slider be", rather than inventing its own ad hoc breakpoint
13/// constants per widget (as e.g. `responsive_game_ui`'s own
14/// `MIN_TARGET_W`/`MIN_TARGET_H` do today). A future interactive widget in
15/// this crate (a checkbox, say) would read [`min_target_size`](Self::min_target_size)
16/// the same way it would read [`Sense`](crate::Sense).
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum Density {
19    /// Smaller interactive targets, for narrow or short terminals, or touch
20    /// input where every cell of screen space is scarce.
21    Compact,
22    /// Larger interactive targets, comfortable to hit with a mouse on a
23    /// normal desktop-sized terminal.
24    Relaxed,
25}
26
27impl Density {
28    /// The minimum size, in cells, an interactive target should claim at
29    /// this density.
30    ///
31    /// Counter-intuitively, [`Compact`](Self::Compact) targets are *taller*
32    /// than [`Relaxed`](Self::Relaxed) ones, not shorter: "compact" here
33    /// means a narrow, likely touch-driven layout (a phone-width terminal,
34    /// say), where a fingertip needs a noticeably taller row than a mouse
35    /// pointer does, at the cost of showing fewer rows at once.
36    /// [`Relaxed`](Self::Relaxed) assumes a normal desktop terminal with a
37    /// mouse, where dense, single-line rows are both legible and easy to
38    /// click precisely.
39    #[must_use]
40    pub const fn min_target_size(self) -> Size {
41        match self {
42            Self::Compact => Size {
43                width: 6,
44                height: 3,
45            },
46            Self::Relaxed => Size {
47                width: 6,
48                height: 1,
49            },
50        }
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn compact_rows_are_taller_than_relaxed_for_touch_targets() {
60        let compact = Density::Compact.min_target_size();
61        let relaxed = Density::Relaxed.min_target_size();
62        assert!(compact.height > relaxed.height);
63    }
64
65    #[test]
66    fn relaxed_still_claims_more_than_a_single_cell_wide() {
67        let size = Density::Relaxed.min_target_size();
68        assert!(size.width > 1);
69    }
70}