From: Simon Tatham Date: Wed, 27 Dec 2023 16:31:26 +0000 (+0000) Subject: Implement a specialised stack of UI activities. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=17992a9fcd089430aa37d534f8dc4a5886df85a2;p=mastodonochrome.git Implement a specialised stack of UI activities. I think this mostly matches Monochrome's rules about what activities you can and can't have stacked on top of each other. To wit, you basically get an arbitrary keypath of menus from the Main Menu, plus optionally a single utilities-menu thing pushed on top of that. Utilities things replace each other rather than stacking, so that popping the stack returns to the non-utility below it. (An exception on real Monochrome is that popping from Send Message replaces it with the Users On screen - also a utility-class activity - rather than popping back to the topmost non-util. Mastodon has no such thing, so we can omit that special case here. If anything like that were needed anyway, it could be special-cased inside pop().) --- diff --git a/src/activity_stack.rs b/src/activity_stack.rs new file mode 100644 index 0000000..21d7af5 --- /dev/null +++ b/src/activity_stack.rs @@ -0,0 +1,191 @@ +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum NonUtilityActivity { + MainMenu, + HomeTimelineFile, +} + +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum UtilityActivity { + UtilsMenu, + ReadMentions, + LogsMenu1, + LogsMenu2, + ExitMenu, + ExamineUser(String), + ListUserFollowers(String), + ListUserFollowees(String), + InfoStatus(String), + ListStatusFavouriters(String), + ListStatusBoosters(String), +} + +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum Activity { + NonUtil(NonUtilityActivity), + Util(UtilityActivity), +} + +impl From for Activity { + fn from(value: NonUtilityActivity) -> Self { Activity::NonUtil(value) } +} +impl From for Activity { + fn from(value: UtilityActivity) -> Self { Activity::Util(value) } +} + +#[derive(PartialEq, Eq, Debug)] +pub struct ActivityStack { + nonutil: Vec, // not counting MainMenu at the top + util: Option, +} + +impl ActivityStack { + pub fn new() -> Self { + ActivityStack { + nonutil: Vec::new(), + util: None, + } + } + + pub fn pop(&mut self) { + if self.util.is_some() { + self.util = None; + } else { + // deliberately ignore failed pop at root + let _ = self.nonutil.pop(); + } + } + + pub fn goto(&mut self, act: Activity) { + match act { + Activity::Util(x) => self.util = Some(x), + Activity::NonUtil(x) => { + self.util = None; + match x { + NonUtilityActivity::MainMenu => self.nonutil.clear(), + y => { + let trunc = match self.nonutil.iter() + .position(|z| *z == y) { + Some(pos) => pos, + None => self.nonutil.len(), + }; + self.nonutil.truncate(trunc); + self.nonutil.push(y); + }, + } + } + } + } + + pub fn top(&self) -> Activity { + match &self.util { + Some(x) => Activity::Util(x.clone()), + _ => match self.nonutil.last() { + Some(y) => Activity::NonUtil(y.clone()), + _ => Activity::NonUtil(NonUtilityActivity::MainMenu), + }, + } + } +} + +#[test] +fn test() { + let mut stk = ActivityStack::new(); + + assert_eq!(stk, ActivityStack { + nonutil: vec! {}, + util: None, + }); + + stk.goto(NonUtilityActivity::HomeTimelineFile.into()); + + assert_eq!(stk, ActivityStack { + nonutil: vec! { + NonUtilityActivity::HomeTimelineFile, + }, + util: None, + }); + + stk.goto(NonUtilityActivity::HomeTimelineFile.into()); + + assert_eq!(stk, ActivityStack { + nonutil: vec! { + NonUtilityActivity::HomeTimelineFile, + }, + util: None, + }); + + stk.goto(NonUtilityActivity::MainMenu.into()); + + assert_eq!(stk, ActivityStack { + nonutil: vec! {}, + util: None, + }); + + stk.goto(NonUtilityActivity::HomeTimelineFile.into()); + + assert_eq!(stk, ActivityStack { + nonutil: vec! { + NonUtilityActivity::HomeTimelineFile, + }, + util: None, + }); + + stk.goto(UtilityActivity::UtilsMenu.into()); + + assert_eq!(stk, ActivityStack { + nonutil: vec! { + NonUtilityActivity::HomeTimelineFile, + }, + util: Some(UtilityActivity::UtilsMenu), + }); + + stk.goto(UtilityActivity::ReadMentions.into()); + + assert_eq!(stk, ActivityStack { + nonutil: vec! { + NonUtilityActivity::HomeTimelineFile, + }, + util: Some(UtilityActivity::ReadMentions), + }); + + stk.pop(); + + assert_eq!(stk, ActivityStack { + nonutil: vec! { + NonUtilityActivity::HomeTimelineFile, + }, + util: None, + }); + + stk.goto(UtilityActivity::ReadMentions.into()); + + assert_eq!(stk, ActivityStack { + nonutil: vec! { + NonUtilityActivity::HomeTimelineFile, + }, + util: Some(UtilityActivity::ReadMentions), + }); + + stk.goto(NonUtilityActivity::HomeTimelineFile.into()); + + assert_eq!(stk, ActivityStack { + nonutil: vec! { + NonUtilityActivity::HomeTimelineFile, + }, + util: None, + }); + + stk.pop(); + + assert_eq!(stk, ActivityStack { + nonutil: vec! {}, + util: None, + }); + + stk.pop(); + + assert_eq!(stk, ActivityStack { + nonutil: vec! {}, + util: None, + }); +} diff --git a/src/lib.rs b/src/lib.rs index 50b086d..f87dbfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,3 +8,4 @@ pub mod scan_re; pub mod coloured_string; pub mod text; pub mod client; +pub mod activity_stack;