chiark / gitweb /
Similarly reusable menu line for cycling between options.
authorSimon Tatham <anakin@pobox.com>
Fri, 12 Jan 2024 08:25:43 +0000 (08:25 +0000)
committerSimon Tatham <anakin@pobox.com>
Fri, 12 Jan 2024 12:52:04 +0000 (12:52 +0000)
Cargo.toml
src/posting.rs
src/text.rs
src/types.rs

index 05b1d6f9ae3afead89aabf25dd43120ae923c9f3..65d4ce2912daf24d0b0cc1e6c3ff17ea694e496f 100644 (file)
@@ -17,7 +17,6 @@ regex = "1.10.2"
 reqwest = { version = "0.11.23", features = ["blocking"] }
 serde = { version = "1.0.193", features = ["derive"] }
 serde_json = "1.0.108"
-strum = { version = "0.25.0", features = ["derive"] }
 sys-locale = "0.3.1"
 tempfile = "3.9.0"
 unicode-width = "0.1.5"
index e4d892701c16f07f0d723a8baac2d1cae7cabc44..5419524e9bcd4a15ef21055ed6399dc2384a7516 100644 (file)
@@ -1,5 +1,4 @@
 use std::iter::once;
-use strum::IntoEnumIterator;
 use sys_locale::get_locale;
 
 use super::client::{Client, ClientError};
@@ -92,7 +91,7 @@ struct PostMenu {
     ml_post: MenuKeypressLine,
     ml_cancel: MenuKeypressLine,
     ml_edit: MenuKeypressLine,
-    ml_vis: MenuKeypressLine,
+    cl_vis: CyclingMenuLine<Visibility>,
     el_content_warning: EditableMenuLine<Option<String>>,
     el_language: EditableMenuLine<String>,
 }
@@ -116,7 +115,18 @@ impl PostMenu {
             Pr('Q'), ColouredString::plain("Cancel post"));
         let ml_edit = MenuKeypressLine::new(
             Pr('A'), ColouredString::plain("Re-edit post"));
-        let ml_vis = Self::visibility_item(post.m.visibility);
+        let cl_vis = CyclingMenuLine::new(
+            Pr('V'), ColouredString::plain("Visibility: "), &[
+                (Visibility::Public, ColouredString::uniform("public", 'f')),
+                (Visibility::Unlisted, ColouredString::plain(
+                    "unlisted (anyone can see it, but feeds omit it)")),
+                (Visibility::Private, ColouredString::general(
+                    "private (followees and @mentioned users can see it)",
+                    "rrrrrrr                                            ")),
+                (Visibility::Direct, ColouredString::general(
+                    "direct (only @mentioned users can see it)",
+                    "rrrrrr                                   ")),
+            ], post.m.visibility);
         let el_content_warning = EditableMenuLine::new(
             Pr('W'), ColouredString::plain("Content warning: "),
             post.m.content_warning.clone());
@@ -132,7 +142,7 @@ impl PostMenu {
             ml_post,
             ml_cancel,
             ml_edit,
-            ml_vis,
+            cl_vis,
             el_content_warning,
             el_language,
         };
@@ -146,60 +156,29 @@ impl PostMenu {
         self.ml_post.check_widths(&mut lmaxwid, &mut rmaxwid);
         self.ml_cancel.check_widths(&mut lmaxwid, &mut rmaxwid);
         self.ml_edit.check_widths(&mut lmaxwid, &mut rmaxwid);
+        self.cl_vis.check_widths(&mut lmaxwid, &mut rmaxwid);
         self.el_content_warning.check_widths(&mut lmaxwid, &mut rmaxwid);
         self.el_language.check_widths(&mut lmaxwid, &mut rmaxwid);
-        for vis in Visibility::iter() {
-            Self::visibility_item(vis).check_widths(&mut lmaxwid, &mut rmaxwid);
-        }
 
         self.ml_post.reset_widths();
         self.ml_cancel.reset_widths();
         self.ml_edit.reset_widths();
-        self.ml_vis.reset_widths();
+        self.cl_vis.reset_widths();
         self.el_content_warning.reset_widths();
         self.el_language.reset_widths();
 
         self.ml_post.ensure_widths(lmaxwid, rmaxwid);
         self.ml_cancel.ensure_widths(lmaxwid, rmaxwid);
         self.ml_edit.ensure_widths(lmaxwid, rmaxwid);
-        self.ml_vis.ensure_widths(lmaxwid, rmaxwid);
+        self.cl_vis.ensure_widths(lmaxwid, rmaxwid);
         self.el_content_warning.ensure_widths(lmaxwid, rmaxwid);
         self.el_language.ensure_widths(lmaxwid, rmaxwid);
 
         (lmaxwid, rmaxwid)
     }
 
-    fn visibility_item(vis: Visibility) -> MenuKeypressLine {
-        let text = match vis {
-            Visibility::Public => ColouredString::general(
-                "Visibility: public",
-                "            ffffff"),
-            Visibility::Unlisted => ColouredString::general(
-                "Visibility: unlisted (anyone can see it, but feeds omit it)",
-                "                                                           "),
-            Visibility::Private => ColouredString::general(
-                "Visibility: private (followees and @mentioned users can see it)",
-                "            rrrrrrr                                            "),
-            Visibility::Direct => ColouredString::general(
-                "Visibility: direct (only @mentioned users can see it)",
-                "            rrrrrr                                   "),
-        };
-        MenuKeypressLine::new(Pr('V'), text)
-    }
-
-    fn cycle_visibility(&mut self) -> LogicalAction {
-        self.post.m.visibility = match self.post.m.visibility {
-            Visibility::Public => Visibility::Unlisted,
-            Visibility::Unlisted => Visibility::Private,
-            Visibility::Private =>  Visibility::Direct,
-            Visibility::Direct => Visibility::Public,
-        };
-        self.ml_vis = Self::visibility_item(self.post.m.visibility);
-        self.fix_widths();
-        LogicalAction::Nothing
-    }
-
     fn post(&mut self, client: &mut Client) -> LogicalAction {
+        self.post.m.visibility = self.cl_vis.get_value();
         self.post.m.content_warning =
             self.el_content_warning.get_data().clone();
         self.post.m.language = self.el_language.get_data().clone();
@@ -222,7 +201,7 @@ impl ActivityState for PostMenu {
         lines.extend_from_slice(&self.ml_cancel.render(w));
         lines.extend_from_slice(&self.ml_edit.render(w));
         lines.extend_from_slice(&BlankLine::render_static());
-        lines.extend_from_slice(&self.ml_vis.render(w));
+        lines.extend_from_slice(&self.cl_vis.render(w));
         lines.push(self.el_content_warning.render(
             w, &mut cursorpos, lines.len()));
         lines.push(self.el_language.render(
@@ -259,7 +238,7 @@ impl ActivityState for PostMenu {
             Pr('q') | Pr('Q') => LogicalAction::Pop,
             Pr('a') | Pr('A') => LogicalAction::PostReEdit(
                 self.post.clone()),
-            Pr('v') | Pr('V') => self.cycle_visibility(),
+            Pr('v') | Pr('V') => self.cl_vis.cycle(),
             Pr('w') | Pr('W') => self.el_content_warning.start_editing(),
             Pr('l') | Pr('L') => self.el_language.start_editing(),
             _ => LogicalAction::Nothing,
index 029c449dbe2a35deda326d332b4533196b5abad8..4c1f8bac205b11f07a0379719c9cba8455e390e2 100644 (file)
@@ -8,7 +8,7 @@ use unicode_width::UnicodeWidthStr;
 use super::html;
 use super::client::{Client, ClientError};
 use super::types::*;
-use super::tui::OurKey;
+use super::tui::{OurKey, LogicalAction};
 use super::coloured_string::*;
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -1789,6 +1789,77 @@ impl TextFragment for MenuKeypressLine {
     }
 }
 
+pub struct CyclingMenuLine<Value: Eq + Copy> {
+    menulines: Vec<(Value, MenuKeypressLine)>,
+    index: usize,
+}
+
+impl<Value: Eq + Copy> CyclingMenuLine<Value> {
+    pub fn new(key: OurKey, description: ColouredString,
+               values: &[(Value, ColouredString)], value: Value) -> Self {
+        let menulines = values.iter().map( |(val, desc)| {
+            (*val, MenuKeypressLine::new(key, &description + desc))
+        }).collect();
+
+        let index = values.iter().position(|(val, _desc)| *val == value)
+            .expect("Input value must match one of the provided options");
+
+        CyclingMenuLine {
+            menulines,
+            index,
+        }
+    }
+
+    // Returns a LogicalAction just to make it more convenient to put
+    // in matches on keypresses
+    pub fn cycle(&mut self) -> LogicalAction {
+        self.index += 1;
+        if self.index >= self.menulines.len() {
+            self.index = 0;
+        }
+        LogicalAction::Nothing
+    }
+
+    pub fn get_value(&self) -> Value { self.menulines[self.index].0 }
+}
+
+impl<Value: Eq + Copy> MenuKeypressLineGeneral for CyclingMenuLine<Value> {
+    fn check_widths(&self, lmaxwid: &mut usize, rmaxwid: &mut usize) {
+        for (_value, ml) in self.menulines.iter() {
+            ml.check_widths(lmaxwid, rmaxwid);
+        }
+    }
+    fn reset_widths(&mut self) {
+        for (_value, ml) in self.menulines.iter_mut() {
+            ml.reset_widths();
+        }
+    }
+    fn ensure_widths(&mut self, lmaxwid: usize, rmaxwid: usize) {
+        for (_value, ml) in self.menulines.iter_mut() {
+            ml.ensure_widths(lmaxwid, rmaxwid);
+        }
+    }
+}
+
+impl<Value: Eq + Copy> TextFragmentOneLine for CyclingMenuLine<Value> {
+    fn render_oneline(&self, width: usize, highlight: Option<Highlight>,
+                      style: &dyn DisplayStyleGetter) -> ColouredString
+    {
+        self.menulines[self.index].1.render_oneline(width, highlight, style)
+    }
+}
+
+impl<Value: Eq + Copy> TextFragment for CyclingMenuLine<Value> {
+    fn render_highlighted(&self, width: usize, highlight: Option<Highlight>,
+                          style: &dyn DisplayStyleGetter)
+                          -> Vec<ColouredString>
+    {
+        vec! {
+            self.render_oneline(width, highlight, style)
+        }
+    }
+}
+
 #[test]
 fn test_menu_keypress() {
     let mut mk = MenuKeypressLine::new(OurKey::Pr('S'), ColouredString::general(
index e512e97aa1b084aa5e04d601c84e392da1a5d011..290399235f7e2a0c527b3455cab91f23862e1161 100644 (file)
@@ -1,6 +1,5 @@
 use chrono::{DateTime, NaiveDate, Utc};
 use serde::{Deserialize, Serialize};
-use strum::EnumIter;
 use std::boxed::Box;
 use std::option::Option;
 
@@ -130,7 +129,7 @@ pub struct Token {
     pub created_at: u64, // Unix timestamp
 }
 
-#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy, EnumIter)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
 pub enum Visibility {
     #[serde(rename = "public")] Public,
     #[serde(rename = "unlisted")] Unlisted,