+use std::collections::HashMap;
+
+use super::activity_stack::{Activity, NonUtilityActivity};
use super::coloured_string::ColouredString;
use super::text::*;
use super::tui::{
OurKey, OurKey::*
};
+enum MenuLine {
+ Blank,
+ Key(MenuKeypressLine),
+}
+
+enum Action {
+ Goto(Activity),
+}
+
+fn find_substring(haystack: &str, needle: &str)
+ -> Option<(usize, usize, usize)> {
+ if let Some(pos) = haystack.find(needle) {
+ let (pre, post) = haystack.split_at(pos);
+ let pre_nchars = pre.chars().count();
+ let needle_nchars = needle.chars().count();
+ let post_nchars = post.chars().count();
+ Some((pre_nchars, needle_nchars, post_nchars - needle_nchars))
+ } else {
+ None
+ }
+}
+
+fn find_highlight_char(desc: &str, c: char)
+ -> Option<(usize, usize, usize)> {
+ let found = find_substring(desc, &c.to_uppercase().to_string());
+ if found.is_some() {
+ found
+ } else {
+ find_substring(desc, &c.to_lowercase().to_string())
+ }
+}
+
struct Menu {
title: FileHeader,
+ status: FileStatusLineFinal,
+ lines: Vec<MenuLine>,
+ actions: HashMap<OurKey, Action>,
+}
+
+impl Menu {
+ fn new(title: ColouredString, is_main: bool) -> Self {
+ let status = FileStatusLine::new();
+ let status = if is_main {
+ status.message("Select an option")
+ } else {
+ status.add(OurKey::Return, "Back", 10)
+ };
+
+ Menu{
+ title: FileHeader::new(title),
+ status: status.finalise(),
+ lines: Vec::new(),
+ actions: HashMap::new(),
+ }
+ }
+
+ fn add_action_coloured(&mut self, key: OurKey, desc: ColouredString,
+ action: Action) {
+ self.lines.push(MenuLine::Key(MenuKeypressLine::new(key, desc)));
+ self.actions.insert(key, action);
+ }
+
+ fn add_action(&mut self, key: OurKey, desc: &str, action: Action) {
+ let highlight_char = match key {
+ Pr(c) => Some(c),
+ Ctrl(c) => Some(c),
+ _ => None,
+ };
+
+ let highlight = if let Some(c) = highlight_char {
+ find_highlight_char(desc, c)
+ } else {
+ None
+ };
+
+ let desc_coloured = if let Some((before, during, after)) = highlight {
+ ColouredString::general(
+ desc,
+ &("H".repeat(before) +
+ &"K".repeat(during) +
+ &"H".repeat(after)))
+ } else {
+ ColouredString::uniform(desc, 'H')
+ };
+
+ self.add_action_coloured(key, desc_coloured, action)
+ }
+
+ fn add_blank_line(&mut self) {
+ self.lines.push(MenuLine::Blank);
+ }
}
pub fn main_menu() -> Box<dyn ActivityState> {
- Box::new(Menu{
- title: FileHeader::new(
- ColouredString::uniform("Mastodonochrome Main Menu", 'H')),
- })
+ let mut menu = Menu::new(
+ ColouredString::uniform("Mastodonochrome Main Menu", 'H'), true);
+
+ menu.add_action(Pr('H'), "Home timeline", Action::Goto(
+ NonUtilityActivity::HomeTimelineFile.into()));
+ menu.add_blank_line();
+
+ Box::new(menu)
}
impl ActivityState for Menu {
-> (Vec<ColouredString>, CursorPosition) {
let mut lines = Vec::new();
lines.extend_from_slice(&self.title.render(w));
+ lines.extend_from_slice(&BlankLine::render_static());
+
+ // FIXME: once menus get too big, we'll need to keep a current
+ // starting position, and be able to scroll up and down the
+ // menu with [<] and [>]
+ for line in &self.lines {
+ lines.extend_from_slice(&match line {
+ MenuLine::Blank => BlankLine::render_static(),
+ MenuLine::Key(mk) => mk.render(w),
+ });
+ }
+
+ while lines.len() + 1 < h {
+ lines.extend_from_slice(&BlankLine::render_static());
+ }
+
+ lines.extend_from_slice(&self.status.render(w));
+
(lines, CursorPosition::End)
}