duat_base/widgets/
whichkey.rs1use std::sync::Once;
7
8use duat_core::{
9 context::{self, Handle},
10 data::Pass,
11 form::{self, Form},
12 hook::{self, FocusChanged, KeyTyped, OnMouseEvent},
13 mode::{self, Description, MouseEventKind},
14 text::{Text, TextMut, txt},
15 ui::{DynSpawnSpecs, PushSpecs, Side, Widget},
16};
17use duat_term::Frame;
18
19pub fn add_whichkey_hooks() {
21 hook::add::<OnMouseEvent<WhichKey>>(move |pa, event| {
22 use MouseEventKind::{ScrollDown, ScrollUp};
23 match event.kind {
24 ScrollDown | ScrollUp => {
25 let (wk, area) = event.handle.write_with_area(pa);
26 let scroll = if let ScrollDown = event.kind { 3 } else { -3 };
27 area.scroll_ver(&wk.0, scroll, wk.print_opts());
28
29 let whichkey_desc = wk.1.clone().unwrap();
30 let (wkd, area) = whichkey_desc.write_with_area(pa);
31 area.scroll_ver(&wkd.0, scroll, wkd.print_opts());
32 }
33 _ => {}
34 }
35 });
36
37 hook::add::<OnMouseEvent<WhichKeyDescriptions>>(move |pa, event| {
38 use MouseEventKind::{ScrollDown, ScrollUp};
39 match event.kind {
40 ScrollDown | ScrollUp => {
41 let (wkd, area) = event.handle.write_with_area(pa);
42 let scroll = if let MouseEventKind::ScrollDown = event.kind {
43 3
44 } else {
45 -3
46 };
47 area.scroll_ver(&wkd.0, scroll, wkd.print_opts());
48
49 let whichkey = wkd.1.clone().unwrap();
50 let (wk, area) = whichkey.write_with_area(pa);
51 area.scroll_ver(&wk.0, scroll, wk.print_opts());
52 }
53 _ => {}
54 }
55 });
56}
57
58pub struct WhichKey(Text, Option<Handle<WhichKeyDescriptions>>);
62
63impl WhichKey {
64 #[allow(clippy::type_complexity)] pub fn open(
69 pa: &mut Pass,
70 mut fmt: Option<Box<dyn FnMut(Description) -> Option<(Text, Text)>>>,
71 mut specs: DynSpawnSpecs,
72 ) {
73 static ONCE: Once = Once::new();
74 ONCE.call_once(|| {
75 form::set(
76 "default.WhichKeyDescriptions",
77 Form::mimic("default.WhichKey"),
78 );
79 });
80
81 let mut keys_builder = Text::builder();
82 let mut descs_builder = Text::builder();
83
84 let (title, descs) = mode::current_seq_descriptions(pa);
85 let title = title.cloned();
86 for desc in descs {
87 if let Some(fmt) = fmt.as_mut() {
88 if let Some((keys, desc)) = fmt(desc) {
89 keys_builder.push(keys);
90 descs_builder.push(desc);
91 }
92 } else if let Some(text) = desc.text
93 && !text.is_empty()
94 {
95 keys_builder.push(txt!("{}[colon.WhichKey]:", desc.keys.into_text()));
96 descs_builder.push(txt!("{text}"));
97 } else {
98 continue;
99 }
100
101 keys_builder.push('\n');
102 descs_builder.push('\n');
103 }
104
105 let keys = WhichKey(keys_builder.build_no_double_nl(), None);
106 let mut descs = WhichKeyDescriptions(descs_builder.build_no_double_nl(), None);
107
108 let handles: Vec<_> = context::windows()
109 .handles(pa)
110 .filter(|handle| {
111 handle.widget().is::<WhichKey>() || handle.widget().is::<WhichKeyDescriptions>()
112 })
113 .collect();
114 for handle in handles {
115 let _ = handle.close(pa);
116 }
117
118 if let Some(height) = specs.height.as_mut() {
119 *height = keys.text().end_point().line().min(*height as usize) as f32;
120 }
121
122 let keys_handle = context::current_buffer(pa)
123 .spawn_widget(pa, keys, specs)
124 .unwrap();
125 descs.1 = Some(keys_handle.clone());
126
127 let title = title.unwrap_or_else(|| txt!("{}", crate::state::mode_name().call(pa)));
128 if let Some(area) = keys_handle.area().write_as::<duat_term::Area>(pa) {
129 use duat_core::text::Spacer;
130
131 let mut frame = Frame {
132 left: true,
133 right: true,
134 above: true,
135 below: true,
136 ..Frame::default()
137 };
138 frame.set_text(Side::Above, move |_| {
139 txt!("{Spacer}[terminal.frame]┤[]{title}[terminal.frame]├{Spacer}")
140 });
141 area.set_frame(frame);
142 }
143
144 let (keys, area) = keys_handle.write_with_area(pa);
145 if let Ok(size) = area.size_of_text(keys.print_opts(), keys.text()) {
146 area.set_width(size.x + 1.0).unwrap();
147 }
148
149 let descs_handle = keys_handle.push_inner_widget(pa, descs, PushSpecs {
150 side: Side::Right,
151 ..Default::default()
152 });
153 keys_handle.write(pa).1 = Some(descs_handle.clone());
154
155 let (descs, area) = descs_handle.write_with_area(pa);
156 if let Ok(size) = area.size_of_text(descs.print_opts(), descs.text()) {
157 area.set_width(size.x).unwrap();
158 }
159
160 hook::add_once::<KeyTyped>({
161 let keys_handle = keys_handle.clone();
162 let descs_handle = descs_handle.clone();
163 move |pa, _| {
164 _ = keys_handle.close(pa);
165 _ = descs_handle.close(pa);
166 }
167 });
168
169 hook::add_once::<FocusChanged>(move |pa, _| {
170 _ = keys_handle.close(pa);
171 _ = descs_handle.close(pa);
172 });
173 }
174}
175
176impl Widget for WhichKey {
177 fn text(&self) -> &Text {
178 &self.0
179 }
180
181 fn text_mut(&mut self) -> TextMut<'_> {
182 self.0.as_mut()
183 }
184}
185
186struct WhichKeyDescriptions(Text, Option<Handle<WhichKey>>);
187
188impl Widget for WhichKeyDescriptions {
189 fn text(&self) -> &Text {
190 &self.0
191 }
192
193 fn text_mut(&mut self) -> TextMut<'_> {
194 self.0.as_mut()
195 }
196}