chiark / gitweb /
Implement a specialised stack of UI activities.
authorSimon Tatham <anakin@pobox.com>
Wed, 27 Dec 2023 16:31:26 +0000 (16:31 +0000)
committerSimon Tatham <anakin@pobox.com>
Wed, 27 Dec 2023 16:35:00 +0000 (16:35 +0000)
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().)

src/activity_stack.rs [new file with mode: 0644]
src/lib.rs

diff --git a/src/activity_stack.rs b/src/activity_stack.rs
new file mode 100644 (file)
index 0000000..21d7af5
--- /dev/null
@@ -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<NonUtilityActivity> for Activity {
+    fn from(value: NonUtilityActivity) -> Self { Activity::NonUtil(value) }
+}
+impl From<UtilityActivity> for Activity {
+    fn from(value: UtilityActivity) -> Self { Activity::Util(value) }
+}
+
+#[derive(PartialEq, Eq, Debug)]
+pub struct ActivityStack {
+    nonutil: Vec<NonUtilityActivity>, // not counting MainMenu at the top
+    util: Option<UtilityActivity>,
+}
+
+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,
+    });
+}
index 50b086db1ab00761f2260da970b4c9d6e1d78e35..f87dbfef98548155debeb675941f30751666c034 100644 (file)
@@ -8,3 +8,4 @@ pub mod scan_re;
 pub mod coloured_string;
 pub mod text;
 pub mod client;
+pub mod activity_stack;