From: Ian Jackson Date: Sun, 4 Feb 2024 20:27:14 +0000 (+0000) Subject: Derive YourOptionsMenuContents rendering with derive-adhoc X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=4d1f2e91aa39d809888e5288e0e4ecce2eb8ef60;p=mastodonochrome.git Derive YourOptionsMenuContents rendering with derive-adhoc We needed a couple of traits for this. And a context structure or it just gets silly. --- diff --git a/src/editor.rs b/src/editor.rs index c92a99e..50dc948 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -949,9 +949,6 @@ impl EditableMenuLine { pub fn get_data(&self) -> &Data { &self.data } - pub fn is_editing(&self) -> bool { - self.editor.is_some() - } pub fn set_text(&mut self, text: &str) { self.data.update(text); self.menuline = @@ -959,6 +956,17 @@ impl EditableMenuLine { } } +impl RenderableMenuLine + for EditableMenuLine +{ + fn render_menu_line(&self, rc: &mut RenderContext) -> Vec { + vec![self.render(rc.w, &mut rc.cursorpos, rc.lines.len())] + } + fn is_editing(&self) -> bool { + self.editor.is_some() + } +} + impl MenuKeypressLineGeneral for EditableMenuLine { diff --git a/src/options.rs b/src/options.rs index 7b26886..8caad9e 100644 --- a/src/options.rs +++ b/src/options.rs @@ -12,8 +12,6 @@ use derive_adhoc::Adhoc; struct YourOptionsMenu { title: FileHeader, - normal_status: FileStatusLineFinal, - edit_status: FileStatusLineFinal, c: YourOptionsMenuContents, } @@ -21,13 +19,16 @@ struct YourOptionsMenu { #[derive_adhoc(MenuContents)] struct YourOptionsMenuContents { el_display_name: EditableMenuLine, // N + #[adhoc(MenuContents(blank_line))] cl_default_vis: CyclingMenuLine, el_default_language: EditableMenuLine>, cl_default_sensitive: CyclingMenuLine, + #[adhoc(MenuContents(blank_line))] cl_locked: CyclingMenuLine, cl_hide_collections: CyclingMenuLine, cl_discoverable: CyclingMenuLine, cl_indexable: CyclingMenuLine, + #[adhoc(MenuContents(blank_line))] cl_bot: CyclingMenuLine, // fields (harder because potentially open-ended number of them) // note (bio) (harder because flip to an editor) @@ -51,14 +52,6 @@ impl YourOptionsMenu { "HHHHHHHHHHHHHHHHHHHKKKHHKHHKH", )); - let normal_status = FileStatusLine::new() - .add(Return, "Back", 10) - .add(Space, "Submit Changes", 15) - .finalise(); - let edit_status = FileStatusLine::new() - .message("Edit line and press Return") - .finalise(); - let el_display_name = EditableMenuLine::new( Pr('N'), ColouredString::plain("Display name: "), @@ -148,12 +141,7 @@ impl YourOptionsMenu { cl_indexable, }; c.fix_widths(); - let menu = YourOptionsMenu { - title, - normal_status, - edit_status, - c, - }; + let menu = YourOptionsMenu { title, c }; Ok(menu) } @@ -184,44 +172,14 @@ impl ActivityState for YourOptionsMenu { w: usize, h: usize, ) -> (Vec, CursorPosition) { - let mut lines = Vec::new(); - let mut cursorpos = CursorPosition::End; - lines.extend_from_slice(&self.title.render(w)); - lines.extend_from_slice(&BlankLine::render_static()); - lines.push(self.c.el_display_name.render( - w, - &mut cursorpos, - lines.len(), - )); - lines.extend_from_slice(&BlankLine::render_static()); - lines.extend_from_slice(&self.c.cl_default_vis.render(w)); - lines.push(self.c.el_default_language.render( - w, - &mut cursorpos, - lines.len(), - )); - lines.extend_from_slice(&self.c.cl_default_sensitive.render(w)); - lines.extend_from_slice(&BlankLine::render_static()); - lines.extend_from_slice(&self.c.cl_locked.render(w)); - lines.extend_from_slice(&self.c.cl_hide_collections.render(w)); - lines.extend_from_slice(&self.c.cl_discoverable.render(w)); - lines.extend_from_slice(&self.c.cl_indexable.render(w)); - lines.extend_from_slice(&BlankLine::render_static()); - lines.extend_from_slice(&self.c.cl_bot.render(w)); - - while lines.len() + 1 < h { - lines.extend_from_slice(&BlankLine::render_static()); - } + let mut rc = RenderContext::new(w); - if self.c.el_display_name.is_editing() - || self.c.el_default_language.is_editing() - { - lines.extend_from_slice(&self.edit_status.render(w)); - } else { - lines.extend_from_slice(&self.normal_status.render(w)); - } + rc.extend(self.title.render(w)); + rc.extend(BlankLine::render_static()); + rc.add_menu_contents(&self.c); + rc.pad_add_editing_status(&self.c, h); - (lines, cursorpos) + rc.finish() } fn handle_keypress( diff --git a/src/text.rs b/src/text.rs index 9c72feb..aea03ba 100644 --- a/src/text.rs +++ b/src/text.rs @@ -10,7 +10,7 @@ use unicode_width::UnicodeWidthStr; use super::client::{Client, ClientError}; use super::coloured_string::*; use super::html; -use super::tui::{LogicalAction, OurKey}; +use super::tui::{CursorPosition, LogicalAction, OurKey}; use super::types::*; #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -2044,8 +2044,61 @@ fn test_filestatus_render() { ); } +pub struct RenderContext { + pub lines: Vec, + pub w: usize, + pub cursorpos: CursorPosition, +} + +impl RenderContext { + pub fn new(w: usize) -> Self { + RenderContext { + w, + lines: vec![], + cursorpos: CursorPosition::End, + } + } + pub fn add_menu_contents(&mut self, c: &impl MenuContents) { + c.render(self); + } + pub fn pad_add_editing_status(&mut self, c: &impl MenuContents, h: usize) { + while self.lines.len() + 1 < h { + self.extend(BlankLine::render_static()); + } + + if c.is_editing() { + let edit_status = FileStatusLine::new() + .message("Edit line and press Return") + .finalise(); + + self.extend(edit_status.render(self.w)); + } else { + let normal_status = FileStatusLine::new() + .add(OurKey::Return, "Back", 10) + .add(OurKey::Space, "Submit Changes", 15) + .finalise(); + + self.extend(normal_status.render(self.w)); + } + } + pub fn finish(self) -> (Vec, CursorPosition) { + (self.lines, self.cursorpos) + } +} + +impl Extend for RenderContext { + fn extend(&mut self, ls: II) + where + II: IntoIterator, + { + self.lines.extend(ls) + } +} + pub trait MenuContents { fn fix_widths(&mut self) -> (usize, usize); + fn render(&self, rc: &mut RenderContext); + fn is_editing(&self) -> bool; } define_derive_adhoc! { @@ -2060,9 +2113,33 @@ define_derive_adhoc! { $( self.$fname.ensure_widths(lmaxwid, rmaxwid); ) (lmaxwid, rmaxwid) } + + fn render(&self, rc: &mut RenderContext) { + $( + ${if fmeta(MenuContents(blank_line)) { + rc.extend(BlankLine::render_static()); + }} + + { + let l = self.$fname.render_menu_line(rc); + rc.extend(l); + } + ) + } + fn is_editing(&self) -> bool { + $( + self.$fname.is_editing() || + ) + false + } } } +pub trait RenderableMenuLine { + fn render_menu_line(&self, rc: &mut RenderContext) -> Vec; + fn is_editing(&self) -> bool; +} + pub struct MenuKeypressLine { keypress: Keypress, lwid: usize, @@ -2250,6 +2327,15 @@ impl TextFragment for CyclingMenuLine { } } +impl RenderableMenuLine for CyclingMenuLine { + fn render_menu_line(&self, rc: &mut RenderContext) -> Vec { + self.render(rc.w) + } + fn is_editing(&self) -> bool { + false + } +} + #[test] fn test_menu_keypress() { let mut mk = MenuKeypressLine::new(