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}