From: Simon Tatham Date: Fri, 12 Jan 2024 08:25:43 +0000 (+0000) Subject: Similarly reusable menu line for cycling between options. X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=ec14cbb32d66496c6508ab7e93acb2b82afadd39;p=mastodonochrome.git Similarly reusable menu line for cycling between options. --- diff --git a/Cargo.toml b/Cargo.toml index 05b1d6f..65d4ce2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/posting.rs b/src/posting.rs index e4d8927..5419524 100644 --- a/src/posting.rs +++ b/src/posting.rs @@ -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, el_content_warning: EditableMenuLine>, el_language: EditableMenuLine, } @@ -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, diff --git a/src/text.rs b/src/text.rs index 029c449..4c1f8ba 100644 --- a/src/text.rs +++ b/src/text.rs @@ -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 { + menulines: Vec<(Value, MenuKeypressLine)>, + index: usize, +} + +impl CyclingMenuLine { + 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 MenuKeypressLineGeneral for CyclingMenuLine { + 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 TextFragmentOneLine for CyclingMenuLine { + fn render_oneline(&self, width: usize, highlight: Option, + style: &dyn DisplayStyleGetter) -> ColouredString + { + self.menulines[self.index].1.render_oneline(width, highlight, style) + } +} + +impl TextFragment for CyclingMenuLine { + fn render_highlighted(&self, width: usize, highlight: Option, + style: &dyn DisplayStyleGetter) + -> Vec + { + vec! { + self.render_oneline(width, highlight, style) + } + } +} + #[test] fn test_menu_keypress() { let mut mk = MenuKeypressLine::new(OurKey::Pr('S'), ColouredString::general( diff --git a/src/types.rs b/src/types.rs index e512e97..2903992 100644 --- a/src/types.rs +++ b/src/types.rs @@ -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,