Skip to main content

shuyi_ui/screens/options/
view.rs

1//! What the Options screen shows, as plain data: no cells, glyphs, colours
2//! or positions. The screen builds an [`OptionsView`] each frame
3//! ([`OptionsScreen::view`](super::OptionsScreen::view)) and a skin paints
4//! it ([`super::glyph::paint`] today). Swapping the look (a bought UI
5//! pack) means another painter of the same view; the screen's logic and
6//! its tests of what happened don't change.
7
8use super::LeaveChoice;
9use crate::prelude::*;
10use crate::widgets::AskView;
11
12/// The whole screen, as it is this frame.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct OptionsView {
15    /// The screen's title.
16    pub title: String,
17    /// The rows, top to bottom.
18    pub rows: Vec<RowView>,
19    /// A message for the player (something was done, or couldn't be).
20    pub message: Option<String>,
21    /// The yes/no question open over the rows, if any (no answers, but
22    /// the line naming its keys).
23    pub question: Option<AskView<bool>>,
24    /// The question asked on leaving with changes, if it is open: keep
25    /// them, throw them away or stay.
26    pub leaving: Option<AskView<LeaveChoice>>,
27    /// The help line: what the keys do here.
28    pub help: String,
29}
30
31impl OptionsView {
32    /// The focused row.
33    pub fn focused(&self) -> Option<&RowView> {
34        self.rows.iter().find(|r| r.focused)
35    }
36
37    /// The view of the row whose key is `key`, if it is shown. A game may
38    /// name its rows by its own type, turned into their keys.
39    pub fn row(&self, key: impl Into<&'static str>) -> Option<&RowView> {
40        let key = key.into();
41        self.rows.iter().find(|r| r.key == key)
42    }
43}
44
45/// One row.
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct RowView {
48    /// Which row it is: the text key of its label.
49    pub key: &'static str,
50    /// Its name.
51    pub label: String,
52    /// What it shows beside its name.
53    pub value: ValueView,
54    /// Whether the cursor is on it.
55    pub focused: bool,
56    /// Whether it is the first row of a group (a skin sets groups apart).
57    pub starts_group: bool,
58}
59
60/// What a row shows beside its name.
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub enum ValueView {
63    /// Nothing: the row only does something.
64    None,
65    /// A value in words.
66    Text {
67        /// The words.
68        text: String,
69        /// Whether the cursor's left and right keys change it.
70        adjustable: bool,
71    },
72    /// A volume, for a slider.
73    Volume {
74        /// The volume, 0 to `max`.
75        level: u8,
76        /// The loudest.
77        max: u8,
78    },
79    /// The box that takes a volume's exact number, open on this row.
80    NumberBox {
81        /// The digits in it so far.
82        digits: String,
83        /// Whether it is being typed in (a caret shows where); otherwise
84        /// the cursor steps the number.
85        typing: bool,
86    },
87}
88
89impl ValueView {
90    /// The value in words, if it is one.
91    pub fn text(&self) -> Option<&str> {
92        match self {
93            ValueView::Text { text, .. } => Some(text),
94            _ => None,
95        }
96    }
97}