chiark / gitweb /
Derive YourOptionsMenuContents rendering with derive-adhoc wip.menus
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 4 Feb 2024 20:27:14 +0000 (20:27 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 4 Feb 2024 20:38:09 +0000 (20:38 +0000)
We needed a couple of traits for this.  And a context structure or it
just gets silly.

src/editor.rs
src/options.rs
src/text.rs

index c92a99e1fde31fd24a2d7b599a47e51f345122f4..50dc94804fe99ad84f1cc378802f702a5effc9b2 100644 (file)
@@ -949,9 +949,6 @@ impl<Data: EditableMenuLineData> EditableMenuLine<Data> {
     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<Data: EditableMenuLineData> EditableMenuLine<Data> {
     }
 }
 
+impl<Data: EditableMenuLineData> RenderableMenuLine
+    for EditableMenuLine<Data>
+{
+    fn render_menu_line(&self, rc: &mut RenderContext) -> Vec<ColouredString> {
+        vec![self.render(rc.w, &mut rc.cursorpos, rc.lines.len())]
+    }
+    fn is_editing(&self) -> bool {
+        self.editor.is_some()
+    }
+}
+
 impl<Data: EditableMenuLineData> MenuKeypressLineGeneral
     for EditableMenuLine<Data>
 {
index 7b26886676863c1a66c41351d745f1283c08df7f..8caad9e9ddc7011f9df6a591e26de927d4fc5b00 100644 (file)
@@ -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<String>, // N
+    #[adhoc(MenuContents(blank_line))]
     cl_default_vis: CyclingMenuLine<Visibility>,
     el_default_language: EditableMenuLine<Option<String>>,
     cl_default_sensitive: CyclingMenuLine<bool>,
+    #[adhoc(MenuContents(blank_line))]
     cl_locked: CyclingMenuLine<bool>,
     cl_hide_collections: CyclingMenuLine<bool>,
     cl_discoverable: CyclingMenuLine<bool>,
     cl_indexable: CyclingMenuLine<bool>,
+    #[adhoc(MenuContents(blank_line))]
     cl_bot: CyclingMenuLine<bool>,
     // 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<ColouredString>, 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(
index 9c72feb12c18dbe1cb4cd4d0e3c608f9255eb78a..aea03ba377fc557042d4b576044132aded58e3eb 100644 (file)
@@ -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<ColouredString>,
+    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<ColouredString>, CursorPosition) {
+        (self.lines, self.cursorpos)
+    }
+}
+
+impl Extend<ColouredString> for RenderContext {
+    fn extend<II>(&mut self, ls: II)
+    where
+        II: IntoIterator<Item = ColouredString>,
+    {
+        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<ColouredString>;
+    fn is_editing(&self) -> bool;
+}
+
 pub struct MenuKeypressLine {
     keypress: Keypress,
     lwid: usize,
@@ -2250,6 +2327,15 @@ impl<Value: Eq + Copy> TextFragment for CyclingMenuLine<Value> {
     }
 }
 
+impl<Value: Eq + Copy> RenderableMenuLine for CyclingMenuLine<Value> {
+    fn render_menu_line(&self, rc: &mut RenderContext) -> Vec<ColouredString> {
+        self.render(rc.w)
+    }
+    fn is_editing(&self) -> bool {
+        false
+    }
+}
+
 #[test]
 fn test_menu_keypress() {
     let mut mk = MenuKeypressLine::new(