1#![doc = include_str!("../readme.md")]
2
3use crate::_private::NonExhaustive;
4use crate::menuitem::{MenuItem, Separator};
5use rat_popup::PopupStyle;
6use ratatui::style::Style;
7use ratatui::widgets::Block;
8use std::fmt::Debug;
9use std::ops::Range;
10
11pub mod menubar;
12pub mod menuitem;
13pub mod menuline;
14pub mod popup_menu;
15mod util;
16
17pub mod event {
18 pub use rat_event::*;
22 use rat_popup::event::PopupOutcome;
23
24 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
26 pub enum MenuOutcome {
27 Continue,
29 Unchanged,
31 Changed,
33
34 Hide,
38
39 Selected(usize),
44
45 Activated(usize),
50
51 MenuSelected(usize, usize),
55
56 MenuActivated(usize, usize),
60 }
61
62 impl ConsumedEvent for MenuOutcome {
63 fn is_consumed(&self) -> bool {
64 *self != MenuOutcome::Continue
65 }
66 }
67
68 impl From<MenuOutcome> for Outcome {
69 fn from(value: MenuOutcome) -> Self {
70 match value {
71 MenuOutcome::Continue => Outcome::Continue,
72 MenuOutcome::Unchanged => Outcome::Unchanged,
73 MenuOutcome::Changed => Outcome::Changed,
74 MenuOutcome::Selected(_) => Outcome::Changed,
75 MenuOutcome::Activated(_) => Outcome::Changed,
76 MenuOutcome::MenuSelected(_, _) => Outcome::Changed,
77 MenuOutcome::MenuActivated(_, _) => Outcome::Changed,
78 MenuOutcome::Hide => Outcome::Changed,
79 }
80 }
81 }
82
83 impl From<PopupOutcome> for MenuOutcome {
84 fn from(value: PopupOutcome) -> Self {
85 match value {
86 PopupOutcome::Continue => MenuOutcome::Continue,
87 PopupOutcome::Unchanged => MenuOutcome::Unchanged,
88 PopupOutcome::Changed => MenuOutcome::Changed,
89 PopupOutcome::Hide => MenuOutcome::Hide,
90 }
91 }
92 }
93
94 impl From<Outcome> for MenuOutcome {
95 fn from(value: Outcome) -> Self {
96 match value {
97 Outcome::Continue => MenuOutcome::Continue,
98 Outcome::Unchanged => MenuOutcome::Unchanged,
99 Outcome::Changed => MenuOutcome::Changed,
100 }
101 }
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct MenuStyle {
108 pub style: Style,
110 pub menu_block: Option<Block<'static>>,
112 pub border_style: Option<Style>,
114 pub title_style: Option<Style>,
116
117 pub title: Option<Style>,
119 pub highlight: Option<Style>,
121 pub disabled: Option<Style>,
123 pub right: Option<Style>,
125 pub focus: Option<Style>,
127
128 pub popup_style: Option<Style>,
130 #[deprecated(since = "2.1.0", note = "use popup_block instead")]
132 pub block: Option<Block<'static>>,
133 pub popup_block: Option<Block<'static>>,
135 pub popup_border: Option<Style>,
137 pub popup_title: Option<Style>,
139 pub popup_highlight: Option<Style>,
141 pub popup_disabled: Option<Style>,
143 pub popup_right: Option<Style>,
145 pub popup_focus: Option<Style>,
147 pub popup_separator: Option<Style>,
149 pub popup: PopupStyle,
151
152 pub non_exhaustive: NonExhaustive,
153}
154
155impl Default for MenuStyle {
156 #[allow(deprecated)]
157 fn default() -> Self {
158 Self {
159 style: Default::default(),
160 menu_block: Default::default(),
161 border_style: Default::default(),
162 title_style: Default::default(),
163 title: Default::default(),
164 highlight: Default::default(),
165 disabled: Default::default(),
166 right: Default::default(),
167 focus: Default::default(),
168 popup_style: Default::default(),
169 block: Default::default(),
170 popup_block: Default::default(),
171 popup_border: Default::default(),
172 popup_title: Default::default(),
173 popup_highlight: Default::default(),
174 popup_disabled: Default::default(),
175 popup_right: Default::default(),
176 popup_focus: Default::default(),
177 popup_separator: Default::default(),
178 popup: Default::default(),
179 non_exhaustive: NonExhaustive,
180 }
181 }
182}
183
184pub trait MenuStructure<'a>: Debug {
186 fn menus(&'a self, menu: &mut MenuBuilder<'a>);
188 fn submenu(&'a self, n: usize, submenu: &mut MenuBuilder<'a>);
190}
191
192#[derive(Debug, Default, Clone)]
194pub struct MenuBuilder<'a> {
195 pub(crate) items: Vec<MenuItem<'a>>,
196}
197
198impl<'a> MenuBuilder<'a> {
199 pub fn new() -> Self {
200 Self::default()
201 }
202
203 pub fn item(&mut self, item: MenuItem<'a>) -> &mut Self {
205 self.items.push(item);
206 self
207 }
208
209 pub fn item_parsed(&mut self, text: &'a str) -> &mut Self {
215 let item = MenuItem::new_parsed(text);
216 if let Some(separator) = item.separator {
217 if let Some(last) = self.items.last_mut() {
218 last.separator = Some(separator);
219 } else {
220 self.items.push(item);
221 }
222 } else {
223 self.items.push(item);
224 }
225 self
226 }
227
228 pub fn item_str(&mut self, text: &'a str) -> &mut Self {
230 self.items.push(MenuItem::new_str(text));
231 self
232 }
233
234 pub fn item_string(&mut self, text: String) -> &mut Self {
236 self.items.push(MenuItem::new_string(text));
237 self
238 }
239
240 pub fn item_nav_str(
242 &mut self,
243 text: &'a str,
244 highlight: Range<usize>,
245 navchar: char,
246 ) -> &mut Self {
247 self.items
248 .push(MenuItem::new_nav_str(text, highlight, navchar));
249 self
250 }
251
252 pub fn item_nav_string(
254 &mut self,
255 text: String,
256 highlight: Range<usize>,
257 navchar: char,
258 ) -> &mut Self {
259 self.items
260 .push(MenuItem::new_nav_string(text, highlight, navchar));
261 self
262 }
263
264 pub fn separator(&mut self, separator: Separator) -> &mut Self {
267 if let Some(last) = self.items.last_mut() {
268 last.separator = Some(separator);
269 } else {
270 self.items.push(MenuItem::new().separator(separator));
271 }
272 self
273 }
274
275 pub fn disabled(&mut self, disable: bool) -> &mut Self {
278 if let Some(last) = self.items.last_mut() {
279 last.disabled = disable;
280 }
281 self
282 }
283
284 pub fn items(self) -> Vec<MenuItem<'a>> {
286 self.items
287 }
288}
289
290#[derive(Debug)]
292pub struct StaticMenu {
293 pub menu: &'static [(&'static str, &'static [&'static str])],
312}
313
314impl MenuStructure<'static> for StaticMenu {
315 fn menus(&'static self, menu: &mut MenuBuilder<'static>) {
316 for (s, _) in self.menu.iter() {
317 menu.item_parsed(s);
318 }
319 }
320
321 fn submenu(&'static self, n: usize, submenu: &mut MenuBuilder<'static>) {
322 for s in self.menu[n].1 {
323 submenu.item_parsed(s);
324 }
325 }
326}
327
328mod _private {
329 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
330 pub struct NonExhaustive;
331}