1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use crate::{State, Tone};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Act<'a> {
/// What the control says.
pub label: &'a str,
/// The key that reaches it where a host has keys.
///
/// The one member written for a terminal before there was one. A webview
/// hangs it off `accesskey` or ignores it; a terminal has nothing else to
/// offer, so this is the whole of how a control is reached there.
pub key: Option<&'a str>,
/// What pressing it means. [`Tone::Danger`] is the destructive one.
pub tone: Tone,
/// Disabled, or nothing said.
///
/// [`State::Disabled`] is what changes what a renderer may do: see
/// [`State::suppresses_interaction`], which is what says a disabled control
/// is drawn and not reachable. A control's focus is not sayable here at
/// all: see the crate header, "Reach, focus and the focus ring".
pub state: Option<State>,
/// A sentence that is always true of this control, shown rather than hunted
/// for.
///
/// Standing help, not a message and not a tooltip. Half the hosts that read
/// this have no pointer: a hover is one spelling of it, and the shipped
/// apps reached for that spelling only because egui and a browser both had
/// one. What is being said is that the sentence is true, never that it is
/// hidden until a pointer arrives.
///
/// # Why it is here rather than a layer up
///
/// A hint left to `quasi_router::Act` alone means each renderer draws it
/// for itself: `makeover_tui` had no hint to read, so quasi-tui built
/// the muted line, and quasi-immediate called `on_hover_text` outside
/// [`crate::Act`] rather than inside it. `Field::hint` was here the whole
/// time, so the same idea sat at two layers depending on which thing
/// carried it, and a host that was not quasi could say it of a field and
/// not of a control.
///
/// What kept it out was price rather than doubt: this crate declares
/// `links`, so a member here moves 25 manifests across 12 repos. That is a
/// release's forward-fix pass, which is a cost and was being read as a
/// barrier.
///
/// # What a renderer owes it
///
/// Somewhere to put it, or nothing. Dropping it is legitimate; drawing it
/// *instead of* the label is not, and neither is drawing it in a way that
/// takes it out of the accessible tree, which is the failure `title` alone
/// has on a browser. Nothing may live only in a hint.
///
/// `None` is a control whose label is the whole of it, which is nearly all
/// of them.
pub hint: Option<&'a str>,
}
impl<'a> Act<'a> {
/// An ordinary control, reachable, with no key.
#[must_use]
pub const fn new(label: &'a str) -> Self {
Self {
label,
key: None,
tone: Tone::Neutral,
state: None,
hint: None,
}
}
/// The sentence that is always true of it; see [`hint`](Self::hint).
///
/// A renderer with nowhere to put it drops it, so this must never be the
/// only place a fact appears.
#[must_use]
pub const fn hinted(mut self, hint: &'a str) -> Self {
self.hint = Some(hint);
self
}
/// The key that reaches it.
#[must_use]
pub const fn key(mut self, key: &'a str) -> Self {
self.key = Some(key);
self
}
/// What pressing it means.
#[must_use]
pub const fn tone(mut self, tone: Tone) -> Self {
self.tone = tone;
self
}
/// Focus, or disabled.
#[must_use]
pub const fn state(mut self, state: State) -> Self {
self.state = Some(state);
self
}
/// Whether the control is drawn and does not answer.
#[must_use]
pub fn disabled(&self) -> bool {
self.state.is_some_and(State::suppresses_interaction)
}
}