shuyi_ui/screens/key_bindings/view.rs
1//! What the Key bindings screen shows, as plain data: no cells, glyphs,
2//! colours or positions. The screen builds a [`KeyBindingsView`] each frame
3//! ([`KeyBindingsScreen::view`](super::KeyBindingsScreen::view)) and a skin
4//! paints it ([`super::glyph::paint`] today).
5
6use super::Side;
7use crate::prelude::*;
8use crate::widgets::AskView;
9
10/// The whole screen, as it is this frame.
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct KeyBindingsView<A> {
13 /// The screen's title.
14 pub title: String,
15 /// The keyboard / controller switch; `None` with no keyboard, when the
16 /// controller side is the only one.
17 pub switch: Option<SwitchView>,
18 /// The headings over the slot columns, left to right.
19 pub columns: Vec<String>,
20 /// The actions, grouped: the required ones, then the optional ones.
21 pub groups: Vec<GroupView<A>>,
22 /// The row under the actions that puts the shown side's defaults back.
23 pub restore: RestoreView,
24 /// The note on a row with nothing in any slot (`! not mapped`).
25 pub not_mapped: String,
26 /// A message for the player (a key can't be used, leaving is blocked).
27 pub message: Option<String>,
28 /// The yes/no question open over the rows, if any (no answers, but
29 /// the line naming its keys).
30 pub question: Option<AskView<bool>>,
31 /// The choices open under the focused slot, if any (the controller
32 /// side's change / clear).
33 pub choices: Option<ChoicesView>,
34 /// The help line: what the keys do here.
35 pub help: String,
36}
37
38impl<A: Copy + PartialEq> KeyBindingsView<A> {
39 /// Every action's row, top to bottom.
40 pub fn rows(&self) -> impl Iterator<Item = &RowView<A>> {
41 self.groups.iter().flat_map(|g| g.rows.iter())
42 }
43
44 /// The row of `action`, if it is shown.
45 pub fn row(&self, action: A) -> Option<&RowView<A>> {
46 self.rows().find(|r| r.action == action)
47 }
48
49 /// The focused row, if the focus is on an action.
50 pub fn focused(&self) -> Option<&RowView<A>> {
51 self.rows().find(|r| r.focus.is_some())
52 }
53}
54
55/// The switch between the keyboard and the controller.
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct SwitchView {
58 /// The name of the keyboard side, with the layout it edits.
59 pub keyboard: String,
60 /// The name of the controller side.
61 pub controller: String,
62 /// The side shown.
63 pub shown: Side,
64 /// Whether the cursor is on the switch.
65 pub focused: bool,
66}
67
68/// A heading and the actions under it.
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct GroupView<A> {
71 /// What the group is.
72 pub heading: String,
73 /// Whether these are the required actions (each must have a key or a
74 /// button before the screen can be left).
75 pub required: bool,
76 /// Its actions, top to bottom.
77 pub rows: Vec<RowView<A>>,
78}
79
80/// One action.
81#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct RowView<A> {
83 /// Which action it is.
84 pub action: A,
85 /// Its name.
86 pub label: String,
87 /// The keys it also has that can't be changed (keyboard side only).
88 pub fixed: Vec<String>,
89 /// What each of its slots holds.
90 pub slots: Vec<SlotView>,
91 /// The slot the cursor is on, if the cursor is on this row.
92 pub focus: Option<usize>,
93 /// Whether nothing is in any of its slots.
94 pub unmapped: bool,
95 /// Whether it is unmapped and so blocks leaving the screen (a required
96 /// action with nothing on the shown side).
97 pub blocks_leaving: bool,
98 /// Whether it just lost its key to another slot (it stays highlighted
99 /// for a moment).
100 pub lost_key: bool,
101}
102
103impl<A> RowView<A> {
104 /// Whether the cursor is on this row.
105 pub fn focused(&self) -> bool {
106 self.focus.is_some()
107 }
108}
109
110/// What one slot holds.
111#[derive(Debug, Clone, PartialEq, Eq)]
112pub enum SlotView {
113 /// Nothing.
114 Empty,
115 /// A key or button, by name.
116 Bound(String),
117 /// Waiting for the key or button to put in it: what it shows meanwhile.
118 Capturing(String),
119}
120
121/// The restore defaults row.
122#[derive(Debug, Clone, PartialEq, Eq)]
123pub struct RestoreView {
124 /// Its name.
125 pub label: String,
126 /// Whether the cursor is on it.
127 pub focused: bool,
128}
129
130/// The choices a slot offers.
131#[derive(Debug, Clone, PartialEq, Eq)]
132pub struct ChoicesView {
133 /// The choices, top to bottom.
134 pub lines: Vec<String>,
135 /// The one the cursor is on.
136 pub focused: usize,
137 /// The slot they belong to (on the focused row).
138 pub slot: usize,
139}