Skip to main content

oino_ui/extras/
key_hints.rs

1//! `KeyHints` node (§4.5.2).
2
3use serde::{Deserialize, Serialize};
4
5use crate::node::{ActionId, Common, IntoNode, Node};
6
7/// One key hint.
8#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct Hint {
11    /// Key id.
12    pub keys: String,
13    /// Hint label.
14    pub label: String,
15    /// Action fired when the hint is tapped (phone shows only tappable hints).
16    #[serde(default, skip_serializing_if = "Option::is_none")]
17    pub action: Option<ActionId>,
18}
19
20impl Hint {
21    /// A hint without an action.
22    #[must_use]
23    pub fn new(keys: impl Into<String>, label: impl Into<String>) -> Self {
24        Self {
25            keys: keys.into(),
26            label: label.into(),
27            action: None,
28        }
29    }
30
31    /// A hint that fires an action.
32    #[must_use]
33    pub fn with_action(
34        keys: impl Into<String>,
35        label: impl Into<String>,
36        action: impl Into<ActionId>,
37    ) -> Self {
38        Self {
39            keys: keys.into(),
40            label: label.into(),
41            action: Some(action.into()),
42        }
43    }
44}
45
46impl From<(&str, &str)> for Hint {
47    fn from((keys, label): (&str, &str)) -> Self {
48        Self::new(keys, label)
49    }
50}
51
52impl From<(&str, &str, &str)> for Hint {
53    fn from((keys, label, action): (&str, &str, &str)) -> Self {
54        Self::with_action(keys, label, action)
55    }
56}
57
58/// ratatui-style key hints line (§4.5.2): `enter open · n new`.
59#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
60#[serde(rename_all = "camelCase")]
61pub struct KeyHints {
62    /// Common node props (`key`, `style`, `block`, `fallback`).
63    #[serde(flatten)]
64    pub common: Common,
65    /// Hints, in display order.
66    pub hints: Vec<Hint>,
67}
68
69impl KeyHints {
70    /// Hints from anything convertible to [`Hint`] (`("enter", "open")` tuples).
71    #[must_use]
72    pub fn new(hints: impl IntoIterator<Item = impl Into<Hint>>) -> Self {
73        Self {
74            hints: hints.into_iter().map(Into::into).collect(),
75            ..Self::default()
76        }
77    }
78
79    /// Attaches an action to the hint with the given `keys` (§11.3).
80    #[must_use]
81    pub fn hint_action(mut self, keys: &str, action: impl Into<ActionId>) -> Self {
82        for hint in &mut self.hints {
83            if hint.keys == keys {
84                hint.action = Some(action.into());
85                break;
86            }
87        }
88        self
89    }
90}
91
92impl IntoNode for KeyHints {}
93impl From<KeyHints> for Node {
94    fn from(k: KeyHints) -> Self {
95        Node::KeyHints(k)
96    }
97}