use std::collections::HashMap;
+use itertools::Itertools;
-use super::activity_stack::{Activity, NonUtilityActivity};
+use super::activity_stack::{NonUtilityActivity, UtilityActivity};
use super::coloured_string::ColouredString;
use super::text::*;
use super::tui::{
- ActivityState, CursorPosition, HandleEventResult,
+ ActivityState, CursorPosition, LogicalAction,
OurKey, OurKey::*
};
Key(MenuKeypressLine),
}
-enum Action {
- Goto(Activity),
-}
-
fn find_substring(haystack: &str, needle: &str)
-> Option<(usize, usize, usize)> {
if let Some(pos) = haystack.find(needle) {
title: FileHeader,
status: FileStatusLineFinal,
lines: Vec<MenuLine>,
- actions: HashMap<OurKey, Action>,
+ actions: HashMap<OurKey, LogicalAction>,
}
impl Menu {
status.add(OurKey::Return, "Back", 10)
};
- Menu{
+ let mut menu = Menu {
title: FileHeader::new(title),
status: status.finalise(),
lines: Vec::new(),
actions: HashMap::new(),
+ };
+
+ if !is_main {
+ menu.actions.insert(OurKey::Return, LogicalAction::Pop);
}
+
+ menu
}
fn add_action_coloured(&mut self, key: OurKey, desc: ColouredString,
- action: Action) {
+ action: LogicalAction) {
self.lines.push(MenuLine::Key(MenuKeypressLine::new(key, desc)));
+
+ if let Pr(c) = key {
+ if let Ok(c) = c.to_lowercase().
+ to_string().chars().exactly_one() {
+ self.actions.insert(Pr(c), action.clone());
+ }
+ if let Ok(c) = c.to_uppercase().
+ to_string().chars().exactly_one() {
+ self.actions.insert(Pr(c), action.clone());
+ }
+ }
+
self.actions.insert(key, action);
}
- fn add_action(&mut self, key: OurKey, desc: &str, action: Action) {
+ fn add_action(&mut self, key: OurKey, desc: &str, action: LogicalAction) {
let highlight_char = match key {
Pr(c) => Some(c),
Ctrl(c) => Some(c),
}
}
-pub fn main_menu() -> Box<dyn ActivityState> {
- 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 {
fn draw(&self, w: usize, h: usize)
-> (Vec<ColouredString>, CursorPosition) {
(lines, CursorPosition::End)
}
- fn handle_keypress(&mut self, key: OurKey) -> HandleEventResult {
- match key {
- Pr('b') => HandleEventResult::Beep,
- _ => HandleEventResult::Nothing,
+ fn handle_keypress(&mut self, key: OurKey) -> LogicalAction {
+ dbg!(&self.actions, &key);
+ match self.actions.get(&key) {
+ Some(action) => action.clone(),
+ None => LogicalAction::Nothing,
}
}
}
+
+pub fn main_menu() -> Box<dyn ActivityState> {
+ let mut menu = Menu::new(
+ ColouredString::uniform("Mastodonochrome Main Menu", 'H'), true);
+
+ menu.add_action(Pr('H'), "Home timeline", LogicalAction::Goto(
+ NonUtilityActivity::HomeTimelineFile.into()));
+
+ Box::new(menu)
+}
+
+pub fn utils_menu() -> Box<dyn ActivityState> {
+ let mut menu = Menu::new(
+ ColouredString::general(
+ "Utilities [ESC]",
+ "HHHHHHHHHHHKKKH"), false);
+
+ menu.add_action(Pr('E'), "Examine User", LogicalAction::NYI);
+ menu.add_action(Pr('Y'), "Examine Yourself", LogicalAction::NYI);
+ menu.add_blank_line();
+ menu.add_action(Pr('L'), "Logs menu", LogicalAction::Goto(
+ UtilityActivity::LogsMenu1.into()));
+ menu.add_blank_line();
+ menu.add_action(Pr('X'), "Exit Mastodonochrome", LogicalAction::Goto(
+ UtilityActivity::ExitMenu.into()));
+
+ Box::new(menu)
+}
+
+pub fn exit_menu() -> Box<dyn ActivityState> {
+ let mut menu = Menu::new(
+ ColouredString::general(
+ "Exit Mastodonochrome [ESC][X]",
+ "HHHHHHHHHHHHHHHHHHHHHHKKKHHKH"), false);
+
+ menu.add_action(Pr('X'), "Confirm exit", LogicalAction::Exit);
+
+ Box::new(menu)
+}
TermEv(Event),
}
-pub enum HandleEventResult {
+pub enum PhysicalAction {
Nothing,
Beep,
Exit,
if key.kind == KeyEventKind::Press {
for ourkey in Self::translate_keypress(key) {
match self.state.handle_keypress(ourkey) {
- HandleEventResult::Beep => {
+ PhysicalAction::Beep => {
Self::beep()?
},
// FIXME: errors?
- HandleEventResult::Exit => {
+ PhysicalAction::Exit => {
break 'outer Ok(());
},
- HandleEventResult::Nothing => (),
+ PhysicalAction::Nothing => (),
}
}
}
}
}
+#[derive(Debug)]
pub enum CursorPosition {
None, // cursor is hidden
End, // cursor at the end of the last drawn line (quite common in this UI)
At(usize, usize), // (x,y)
}
+#[derive(Debug, Clone)]
+pub enum LogicalAction {
+ Beep,
+ Pop,
+ Goto(Activity),
+ Exit,
+ Nothing,
+ NYI, // FIXME: get rid of this once everything is implemented
+}
+
pub trait ActivityState {
fn draw(&self, w: usize, h: usize) -> (Vec<ColouredString>, CursorPosition);
- fn handle_keypress(&mut self, key: OurKey) -> HandleEventResult;
+ fn handle_keypress(&mut self, key: OurKey) -> LogicalAction;
}
struct TuiLogicalState {
fn new_activity_state(activity: Activity) -> Box<dyn ActivityState> {
match activity {
Activity::NonUtil(NonUtilityActivity::MainMenu) => main_menu(),
+ Activity::Util(UtilityActivity::UtilsMenu) => utils_menu(),
+ Activity::Util(UtilityActivity::ExitMenu) => exit_menu(),
_ => panic!("FIXME"),
}
}
}
}
- fn handle_keypress(&mut self, key: OurKey) -> HandleEventResult {
- match key {
- // FIXME: ESC should really go to the utilities menu
- OurKey::Escape => HandleEventResult::Exit,
+ fn handle_keypress(&mut self, key: OurKey) -> PhysicalAction {
+ let logact = match key {
+ // Central handling of [ESC]: it _always_ goes to the
+ // utilities menu, from any UI context at all.
+ OurKey::Escape => LogicalAction::Goto(
+ UtilityActivity::UtilsMenu.into()),
_ => self.activity_state.handle_keypress(key),
+ };
+
+ match logact {
+ LogicalAction::Beep => PhysicalAction::Beep,
+ LogicalAction::Exit => PhysicalAction::Exit,
+ LogicalAction::Nothing => PhysicalAction::Nothing,
+ LogicalAction::NYI => PhysicalAction::Beep,
+ LogicalAction::Goto(activity) => {
+ self.activity_stack.goto(activity);
+ self.changed_activity();
+ PhysicalAction::Nothing
+ },
+ LogicalAction::Pop => {
+ self.activity_stack.pop();
+ self.changed_activity();
+ PhysicalAction::Nothing
+ },
}
}
+
+ fn changed_activity(&mut self) {
+ self.activity_state = new_activity_state(self.activity_stack.top());
+ }
}