duat_base/widgets/
which_key.rs

1use duat_core::{
2    context::{self, Handle},
3    data::Pass,
4    hook::{self, FocusChanged, KeyTyped},
5    mode::{self, Description, MouseEvent, MouseEventKind},
6    text::{Spacer, Text, txt},
7    ui::{DynSpawnSpecs, Widget},
8};
9
10/// A [`Widget`] to display what [keys] will do
11///
12/// [keys]: mode::KeyEvent
13pub struct WhichKey {
14    text: Text,
15}
16
17impl WhichKey {
18    /// Open the `WhichKey` widget
19    ///
20    /// You can optionally pass an
21    #[allow(clippy::type_complexity)] // ??? where?
22    pub fn open(
23        pa: &mut Pass,
24        mut fmt: Option<Box<dyn FnMut(Description) -> Option<Text>>>,
25        mut specs: DynSpawnSpecs,
26    ) {
27        let mut builder = Text::builder();
28
29        for desc in mode::current_seq_descriptions(pa) {
30            if let Some(fmt) = fmt.as_mut() {
31                if let Some(text) = fmt(desc) {
32                    builder.push(text);
33                }
34            } else if let Some(text) = desc.text
35                && !text.is_empty()
36            {
37                builder.push(txt!("{}{Spacer}{text}\n", desc.keys.into_text()));
38            }
39        }
40
41        let wk = WhichKey { text: builder.build_no_double_nl() };
42
43        let handles: Vec<_> = context::windows().handles_of::<WhichKey>(pa).collect();
44        for handle in handles {
45            let _ = handle.close(pa);
46        }
47
48        if let Some(height) = specs.height.as_mut() {
49            *height = wk.text().len().line().min(*height as usize) as f32;
50        }
51
52        let handle = context::current_buffer(pa)
53            .clone()
54            .spawn_widget(pa, wk, specs)
55            .unwrap();
56
57        let (wk, area) = handle.write_with_area(pa);
58        if let Ok(width) = area.width_of_text(wk.get_print_opts(), wk.text()) {
59            area.set_width(width + 3.0).unwrap();
60        }
61
62        hook::add::<KeyTyped>({
63            let handle = handle.clone();
64            move |pa, _| Ok(_ = handle.close(pa))
65        })
66        .once();
67        hook::add::<FocusChanged>(move |pa, _| Ok(_ = handle.close(pa))).once();
68    }
69}
70
71impl Widget for WhichKey {
72    fn update(_: &mut Pass, _: &Handle<Self>) {}
73
74    fn needs_update(&self, _: &Pass) -> bool {
75        false
76    }
77
78    fn text(&self) -> &Text {
79        &self.text
80    }
81
82    fn text_mut(&mut self) -> &mut Text {
83        &mut self.text
84    }
85
86    fn on_mouse_event(pa: &mut Pass, handle: &Handle<Self>, event: MouseEvent) {
87        match event.kind {
88            MouseEventKind::ScrollDown | MouseEventKind::ScrollUp => {
89                let (wk, area) = handle.write_with_area(pa);
90                let scroll = if let MouseEventKind::ScrollDown = event.kind {
91                    3
92                } else {
93                    -3
94                };
95                area.scroll_ver(&wk.text, scroll, wk.get_print_opts());
96            }
97            _ => {}
98        }
99    }
100}