Skip to main content

rsynko_memory/
screen.rs

1//! The reference renderer: a screen stated as the lines a reader would read.
2
3use rsynko_ui::{Emphasis, Gauge, ScreenSyntax};
4
5/// Renders screens as plain text.
6///
7/// A renderer states weight however its medium can. Plain text has no weight to state, so this
8/// interpreter keeps every word and drops every emphasis, which is exactly what makes it the one
9/// to read a screen's meaning back from.
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11pub struct TextScreen;
12
13impl ScreenSyntax for TextScreen {
14    type Text = String;
15    type Line = String;
16    type Row = Vec<String>;
17    type Body = Vec<String>;
18    type Screen = Vec<String>;
19
20    fn text(&self, content: impl Into<String>, _emphasis: Emphasis) -> Self::Text {
21        content.into()
22    }
23
24    fn gauge(&self, gauge: Gauge) -> Self::Text {
25        gauge.text()
26    }
27
28    fn line(&self, runs: impl Iterator<Item = Self::Text>) -> Self::Line {
29        runs.collect()
30    }
31
32    fn edited_line(&self, prefix: impl Into<String>, text: &str, cursor: usize, _emphasis: Emphasis) -> Self::Line {
33        // The cursor is stated where a reader would see it, since text has no cursor of its own.
34        let (before, after) = text.split_at(cursor.min(text.len()));
35        format!("{}{before}|{after}", prefix.into())
36    }
37
38    fn row(&self, lines: impl Iterator<Item = Self::Line>) -> Self::Row {
39        lines.collect()
40    }
41
42    fn rows(
43        &self,
44        title: impl Into<String>,
45        _focused: Option<usize>,
46        rows: impl Iterator<Item = Self::Row>,
47    ) -> Self::Body {
48        [title.into()].into_iter().chain(rows.flatten()).collect()
49    }
50
51    fn message(&self, title: impl Into<String>, message: impl Into<String>, _emphasis: Emphasis) -> Self::Body {
52        vec![title.into(), message.into()]
53    }
54
55    fn draft(
56        &self,
57        title: impl Into<String>,
58        placeholder: impl Into<String>,
59        text: &str,
60        cursor: usize,
61        examples: impl Iterator<Item = (String, String)>,
62    ) -> Self::Body {
63        let content =
64            if text.is_empty() { placeholder.into() } else { self.edited_line("", text, cursor, Emphasis::Plain) };
65        [title.into(), content].into_iter().chain(examples.map(|(shape, means)| format!("{shape}  {means}"))).collect()
66    }
67
68    fn record(&self, title: impl Into<String>, notes: impl Iterator<Item = String>) -> Self::Body {
69        [title.into()].into_iter().chain(notes).collect()
70    }
71
72    fn verbatim(&self, text: impl Into<String>) -> Self::Body {
73        vec![text.into()]
74    }
75
76    fn screen(
77        &self,
78        header: Self::Line,
79        body: Self::Body,
80        status: impl Into<String>,
81        footer: Self::Line,
82    ) -> Self::Screen {
83        [header].into_iter().chain(body).chain([status.into(), footer]).collect()
84    }
85
86    fn screen_text(&self, screen: &Self::Screen) -> impl Iterator<Item = String> {
87        screen.clone().into_iter()
88    }
89}