about summary refs log tree commit diff
path: root/src/display
diff options
context:
space:
mode:
Diffstat (limited to 'src/display')
-rw-r--r--src/display/color.rs163
-rw-r--r--src/display/draw_box.rs274
-rw-r--r--src/display/mod.rs52
-rw-r--r--src/display/utils.rs9
-rw-r--r--src/display/viewport.rs303
5 files changed, 0 insertions, 801 deletions
diff --git a/src/display/color.rs b/src/display/color.rs
deleted file mode 100644
index afe0039998b8..000000000000
--- a/src/display/color.rs
+++ /dev/null
@@ -1,163 +0,0 @@
-use serde::de::{self, Unexpected, Visitor};
-use std::fmt;
-use std::marker::PhantomData;
-use termion::color;
-
-#[derive(Debug)]
-pub struct Color(Box<dyn color::Color>);
-
-unsafe impl Sync for Color {}
-unsafe impl Send for Color {}
-
-impl Color {
-    pub fn new<C: color::Color + 'static>(c: C) -> Self {
-        Color(Box::new(c))
-    }
-}
-
-impl PartialEq for Color {
-    fn eq(&self, other: &Self) -> bool {
-        format!("{}{}", color::Fg(self), color::Bg(self))
-            == format!("{}{}", color::Fg(other), color::Bg(other))
-    }
-}
-
-impl Eq for Color {}
-
-impl color::Color for Color {
-    fn write_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.write_fg(f)
-    }
-
-    fn write_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.write_bg(f)
-    }
-}
-
-impl<'a> color::Color for &'a Color {
-    fn write_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.write_fg(f)
-    }
-
-    fn write_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.write_bg(f)
-    }
-}
-
-impl Default for Color {
-    fn default() -> Self {
-        Color::new(color::Reset)
-    }
-}
-
-pub struct ColorVisitor {
-    marker: PhantomData<fn() -> Color>,
-}
-
-impl ColorVisitor {
-    fn new() -> Self {
-        ColorVisitor {
-            marker: PhantomData,
-        }
-    }
-}
-
-impl<'de> Visitor<'de> for ColorVisitor {
-    type Value = Color;
-
-    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-        formatter.write_str("A color")
-    }
-
-    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
-    where
-        E: de::Error,
-    {
-        match v.to_lowercase().as_ref() {
-            "black" => Ok(Color(Box::new(color::Black))),
-            "blue" => Ok(Color(Box::new(color::Blue))),
-            "cyan" => Ok(Color(Box::new(color::Cyan))),
-            "green" => Ok(Color(Box::new(color::Green))),
-            "light black" | "light_black" => {
-                Ok(Color(Box::new(color::LightBlack)))
-            }
-            "light blue" | "light_blue" => {
-                Ok(Color(Box::new(color::LightBlue)))
-            }
-            "light cyan" | "light_cyan" => {
-                Ok(Color(Box::new(color::LightCyan)))
-            }
-            "light green" | "light_green" => {
-                Ok(Color(Box::new(color::LightGreen)))
-            }
-            "light magenta" | "light_magenta" => {
-                Ok(Color(Box::new(color::LightMagenta)))
-            }
-            "light red" | "light_red" => Ok(Color(Box::new(color::LightRed))),
-            "light white" | "light_white" => {
-                Ok(Color(Box::new(color::LightWhite)))
-            }
-            "light yellow" | "light_yellow" => {
-                Ok(Color(Box::new(color::LightYellow)))
-            }
-            "magenta" => Ok(Color(Box::new(color::Magenta))),
-            "red" => Ok(Color(Box::new(color::Red))),
-            "white" => Ok(Color(Box::new(color::White))),
-            "yellow" => Ok(Color(Box::new(color::Yellow))),
-            _ => Err(de::Error::invalid_value(
-                Unexpected::Str(v),
-                &"a valid color",
-            )),
-        }
-    }
-
-    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
-    where
-        A: de::MapAccess<'de>,
-    {
-        let mut red = None;
-        let mut green = None;
-        let mut blue = None;
-        while let Some((k, v)) = map.next_entry()? {
-            match k {
-                "red" => {
-                    red = Some(v);
-                }
-                "green" => {
-                    green = Some(v);
-                }
-                "blue" => {
-                    blue = Some(v);
-                }
-                _ => {
-                    return Err(de::Error::unknown_field(
-                        k,
-                        &["red", "green", "blue"],
-                    ));
-                }
-            }
-        }
-
-        match (red, green, blue) {
-            (Some(r), Some(g), Some(b)) => {
-                Ok(Color(Box::new(color::Rgb(r, g, b))))
-            }
-            (None, _, _) => Err(de::Error::missing_field("red")),
-            (_, None, _) => Err(de::Error::missing_field("green")),
-            (_, _, None) => Err(de::Error::missing_field("blue")),
-        }
-    }
-
-    fn visit_u8<E: de::Error>(self, v: u8) -> Result<Self::Value, E> {
-        Ok(Color(Box::new(color::AnsiValue(v))))
-    }
-}
-
-impl<'de> serde::Deserialize<'de> for Color {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        deserializer.deserialize_any(ColorVisitor::new())
-    }
-}
diff --git a/src/display/draw_box.rs b/src/display/draw_box.rs
deleted file mode 100644
index e4d34a7acda9..000000000000
--- a/src/display/draw_box.rs
+++ /dev/null
@@ -1,274 +0,0 @@
-use crate::display::utils::clone_times;
-use crate::display::utils::times;
-use crate::types::pos;
-use crate::types::BoundingBox;
-use crate::types::Dimensions;
-use crate::types::Neighbors;
-use itertools::Itertools;
-use proptest::prelude::Arbitrary;
-use proptest::strategy;
-use proptest_derive::Arbitrary;
-use std::io::{self, Write};
-
-// Box Drawing
-//  	    0 	1 	2 	3 	4 	5 	6 	7 	8 	9 	A 	B 	C 	D 	E 	F
-// U+250x 	─ 	━ 	│ 	┃ 	┄ 	┅ 	┆ 	┇ 	┈ 	┉ 	┊ 	┋ 	┌ 	┍ 	┎ 	┏
-// U+251x 	┐ 	┑ 	┒ 	┓ 	└ 	┕ 	┖ 	┗ 	┘ 	┙ 	┚ 	┛ 	├ 	┝ 	┞ 	┟
-// U+252x 	┠ 	┡ 	┢ 	┣ 	┤ 	┥ 	┦ 	┧ 	┨ 	┩ 	┪ 	┫ 	┬ 	┭ 	┮ 	┯
-// U+253x 	┰ 	┱ 	┲ 	┳ 	┴ 	┵ 	┶ 	┷ 	┸ 	┹ 	┺ 	┻ 	┼ 	┽ 	┾ 	┿
-// U+254x 	╀ 	╁ 	╂ 	╃ 	╄ 	╅ 	╆ 	╇ 	╈ 	╉ 	╊ 	╋ 	╌ 	╍ 	╎ 	╏
-// U+255x 	═ 	║ 	╒ 	╓ 	╔ 	╕ 	╖ 	╗ 	╘ 	╙ 	╚ 	╛ 	╜ 	╝ 	╞ 	╟
-// U+256x 	╠ 	╡ 	╢ 	╣ 	╤ 	╥ 	╦ 	╧ 	╨ 	╩ 	╪ 	╫ 	╬ 	╭ 	╮ 	╯
-// U+257x 	╰ 	╱ 	╲ 	╳ 	╴ 	╵ 	╶ 	╷ 	╸ 	╹ 	╺ 	╻ 	╼ 	╽ 	╾ 	╿
-
-static BOX: char = '☐';
-
-static BOX_CHARS: [[char; 16]; 8] = [
-    // 0
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '─', '━', '│', '┃', '┄', '┅', '┆', '┇', '┈', '┉',
-        // 10
-        '┊', '┋', '┌', '┍', '┎', '┏',
-    ],
-    // 1
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '┐', '┑', '┒', '┓', '└', '┕', '┖', '┗', '┘', '┙',
-        '┚', '┛', '├', '┝', '┞', '┟',
-    ],
-    // 2
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '┠', '┡', '┢', '┣', '┤', '┥', '┦', '┧', '┨', '┩',
-        '┪', '┫', '┬', '┭', '┮', '┯',
-    ],
-    // 3
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '┰', '┱', '┲', '┳', '┴', '┵', '┶', '┷', '┸', '┹',
-        '┺', '┻', '┼', '┽', '┾', '┿',
-    ],
-    // 4
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '╀', '╁', '╂', '╃', '╄', '╅', '╆', '╇', '╈', '╉',
-        '╊', '╋', '╌', '╍', '╎', '╏',
-    ],
-    // 5
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '═', '║', '╒', '╓', '╔', '╕', '╖', '╗', '╘', '╙',
-        '╚', '╛', '╜', '╝', '╞', '╟',
-    ],
-    // 6
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '╠', '╡', '╢', '╣', '╤', '╥', '╦', '╧', '╨', '╩',
-        '╪', '╫', '╬', '╭', '╮', '╯',
-    ],
-    // 7
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '╰', '╱', '╲', '╳', '╴', '╵', '╶', '╷', '╸', '╹',
-        '╺', '╻', '╼', '╽', '╾', '╿',
-    ],
-];
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum BoxStyle {
-    Thin,
-    Thick,
-    Dotted,
-    ThickDotted,
-    Dashed,
-    ThickDashed,
-    Double,
-}
-
-impl Arbitrary for BoxStyle {
-    type Parameters = ();
-    type Strategy = strategy::Just<Self>;
-    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
-        // TODO
-        strategy::Just(BoxStyle::Thin)
-    }
-}
-
-pub trait Stylable {
-    fn style(&self, style: BoxStyle) -> char;
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
-enum Corner {
-    TopRight,
-    TopLeft,
-    BottomRight,
-    BottomLeft,
-}
-
-impl Stylable for Corner {
-    fn style(&self, style: BoxStyle) -> char {
-        use BoxStyle::*;
-        use Corner::*;
-
-        match (self, style) {
-            (TopRight, Thin) => BOX_CHARS[1][0],
-            (TopLeft, Thin) => BOX_CHARS[0][12],
-            (BottomRight, Thin) => BOX_CHARS[1][8],
-            (BottomLeft, Thin) => BOX_CHARS[1][4],
-            _ => unimplemented!(),
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
-enum Line {
-    H,
-    V,
-}
-
-impl Stylable for Line {
-    fn style(&self, style: BoxStyle) -> char {
-        use BoxStyle::*;
-        use Line::*;
-        match (self, style) {
-            (H, Thin) => BOX_CHARS[0][0],
-            (V, Thin) => BOX_CHARS[0][2],
-            _ => unimplemented!(),
-        }
-    }
-}
-
-impl Stylable for Neighbors<Option<BoxStyle>> {
-    fn style(&self, _style: BoxStyle) -> char {
-        use BoxStyle::*;
-        match (self.left, self.right, self.top, self.bottom) {
-            (None, None, None, None) => BOX,
-            (Some(Thin), None, None, None) => BOX_CHARS[7][4],
-            (None, Some(Thin), None, None) => BOX_CHARS[7][6],
-            (None, None, Some(Thin), None) => BOX_CHARS[7][5],
-            (None, None, None, Some(Thin)) => BOX_CHARS[7][7],
-            (Some(Thin), Some(Thin), None, None) => Line::H.style(Thin),
-            (Some(Thin), None, Some(Thin), None) => {
-                Corner::BottomRight.style(Thin)
-            }
-            (Some(Thin), None, None, Some(Thin)) => {
-                Corner::TopRight.style(Thin)
-            }
-            (None, Some(Thin), Some(Thin), None) => {
-                Corner::BottomLeft.style(Thin)
-            }
-            (None, Some(Thin), None, Some(Thin)) => Corner::TopLeft.style(Thin),
-            (None, None, Some(Thin), Some(Thin)) => Line::V.style(Thin),
-            (None, Some(Thin), Some(Thin), Some(Thin)) => BOX_CHARS[1][12],
-            (Some(Thin), None, Some(Thin), Some(Thin)) => BOX_CHARS[2][4],
-            (Some(Thin), Some(Thin), None, Some(Thin)) => BOX_CHARS[2][12],
-            (Some(Thin), Some(Thin), Some(Thin), None) => BOX_CHARS[3][4],
-            (Some(Thin), Some(Thin), Some(Thin), Some(Thin)) => {
-                BOX_CHARS[3][12]
-            }
-            neighs => panic!("unimplemented: {:?}", neighs),
-        }
-    }
-}
-
-#[must_use]
-pub fn make_box(style: BoxStyle, dims: Dimensions) -> String {
-    if dims.h == 0 || dims.w == 0 {
-        "".to_string()
-    } else if dims.h == 1 && dims.w == 1 {
-        BOX.to_string()
-    } else if dims.h == 1 {
-        times(Line::H.style(style), dims.w)
-    } else if dims.w == 1 {
-        (0..dims.h).map(|_| Line::V.style(style)).join("\n\r")
-    } else {
-        let h_line: String = times(Line::H.style(style), dims.w - 2);
-        let v_line = Line::V.style(style);
-        let v_walls: String = clone_times(
-            format!(
-                "{}{}{}\n\r",
-                v_line,
-                times::<_, String>(' ', dims.w - 2),
-                v_line
-            ),
-            dims.h - 2,
-        );
-
-        format!(
-            "{}{}{}\n\r{}{}{}{}",
-            Corner::TopLeft.style(style),
-            h_line,
-            Corner::TopRight.style(style),
-            v_walls,
-            Corner::BottomLeft.style(style),
-            h_line,
-            Corner::BottomRight.style(style),
-        )
-    }
-}
-
-/// Draw the box described by the given BoundingBox's position and dimensions to
-/// the given output, with the given style
-pub fn draw_box<W: Write>(
-    out: &mut W,
-    bbox: BoundingBox,
-    style: BoxStyle,
-) -> io::Result<()> {
-    let box_str = make_box(style, bbox.dimensions);
-    if bbox.position.x == 0 {
-        write!(out, "{}{}", bbox.position.cursor_goto(), box_str)?;
-    } else {
-        for (i, line) in box_str.split("\n\r").enumerate() {
-            debug!("line: {:?}!", line);
-            write!(
-                out,
-                "{}{}",
-                (bbox.position + pos(0, i as i16)).cursor_goto(),
-                line
-            )?;
-        }
-    }
-    Ok(())
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use proptest::prelude::*;
-
-    #[test]
-    fn make_thin_box() {
-        let res = make_box(BoxStyle::Thin, Dimensions { w: 10, h: 10 });
-        assert_eq!(
-            res,
-            "┌────────┐
-\r│        │
-\r│        │
-\r│        │
-\r│        │
-\r│        │
-\r│        │
-\r│        │
-\r│        │
-\r└────────┘"
-        );
-    }
-
-    proptest! {
-        #[test]
-        fn box_has_height_lines(dims: Dimensions, style: BoxStyle) {
-            let res = make_box(style, dims);
-            prop_assume!((dims.w > 0 && dims.h > 0));
-            assert_eq!(res.split("\n\r").count(), dims.h as usize);
-        }
-
-        #[test]
-        fn box_lines_have_width_length(dims: Dimensions, style: BoxStyle) {
-            let res = make_box(style, dims);
-            prop_assume!(dims.w == 0 && dims.h == 0 || (dims.w > 0 && dims.h > 0));
-            assert!(res.split("\n\r").all(|l| l.chars().count() == dims.w as usize));
-        }
-    }
-}
diff --git a/src/display/mod.rs b/src/display/mod.rs
deleted file mode 100644
index 6e37a03d8c55..000000000000
--- a/src/display/mod.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-pub mod color;
-pub mod draw_box;
-pub mod utils;
-pub mod viewport;
-use crate::entities::entity::Entity;
-use crate::types::Neighbors;
-use crate::types::Positioned;
-pub use draw_box::{make_box, BoxStyle};
-use std::io::{self, Write};
-use termion::{clear, cursor, style};
-pub use viewport::Viewport;
-
-pub fn clear<T: Write>(out: &mut T) -> io::Result<()> {
-    write!(out, "{}{}{}", clear::All, style::Reset, cursor::Goto(1, 1))
-}
-
-pub trait Draw: Positioned {
-    /// Draw this entity, assuming the character is already at the correct
-    /// position
-    fn do_draw(&self, out: &mut dyn Write) -> io::Result<()>;
-}
-
-impl<T: Draw> Draw for &T {
-    fn do_draw(&self, out: &mut dyn Write) -> io::Result<()> {
-        (**self).do_draw(out)
-    }
-}
-
-impl<T: Draw> Draw for Box<T> {
-    fn do_draw(&self, out: &mut dyn Write) -> io::Result<()> {
-        (**self).do_draw(out)
-    }
-}
-
-pub trait DrawWithNeighbors: Positioned {
-    #[allow(clippy::borrowed_box)]
-    fn do_draw_with_neighbors<'a, 'b>(
-        &'a self,
-        out: &'b mut dyn Write,
-        neighbors: &'a Neighbors<Vec<&'a Box<dyn Entity>>>,
-    ) -> io::Result<()>;
-}
-
-impl<T: Draw> DrawWithNeighbors for T {
-    fn do_draw_with_neighbors<'a, 'b>(
-        &'a self,
-        out: &'b mut dyn Write,
-        _neighbors: &'a Neighbors<Vec<&'a Box<dyn Entity>>>,
-    ) -> io::Result<()> {
-        self.do_draw(out)
-    }
-}
diff --git a/src/display/utils.rs b/src/display/utils.rs
deleted file mode 100644
index acd4416cb884..000000000000
--- a/src/display/utils.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-use std::iter::FromIterator;
-
-pub fn times<A: Copy, B: FromIterator<A>>(elem: A, n: u16) -> B {
-    (0..n).map(|_| elem).collect()
-}
-
-pub fn clone_times<A: Clone, B: FromIterator<A>>(elem: A, n: u16) -> B {
-    (0..n).map(|_| elem.clone()).collect()
-}
diff --git a/src/display/viewport.rs b/src/display/viewport.rs
deleted file mode 100644
index c44316cdaad5..000000000000
--- a/src/display/viewport.rs
+++ /dev/null
@@ -1,303 +0,0 @@
-use super::BoxStyle;
-use super::DrawWithNeighbors;
-use crate::display::draw_box::draw_box;
-use crate::display::utils::clone_times;
-use crate::entities::entity::Entity;
-use crate::types::menu::MenuInfo;
-use crate::types::Neighbors;
-use crate::types::{pos, BoundingBox, Direction, Position, Positioned};
-use std::fmt::{self, Debug};
-use std::io::{self, Write};
-
-pub enum CursorState {
-    Game,
-    Prompt(Position),
-}
-
-impl Default for CursorState {
-    fn default() -> Self {
-        CursorState::Game
-    }
-}
-
-pub struct Viewport<W> {
-    /// The box describing the visible part of the viewport.
-    ///
-    /// Generally the size of the terminal, and positioned at 0, 0
-    pub outer: BoundingBox,
-
-    /// The box describing the game part of the viewport.
-    pub game: BoundingBox,
-
-    /// The box describing the inner part of the viewport
-    ///
-    /// Its position is relative to `outer.inner()`, and its size should
-    /// generally not be smaller than outer
-    pub inner: BoundingBox,
-
-    /// The actual screen that the viewport writes to
-    pub out: W,
-
-    cursor_state: CursorState,
-
-    /// Reset the cursor back to this position after every draw
-    pub game_cursor_position: Position,
-}
-
-impl<W> Viewport<W> {
-    pub fn new(outer: BoundingBox, inner: BoundingBox, out: W) -> Self {
-        Viewport {
-            outer,
-            inner,
-            out,
-            game: outer.move_tr_corner(Position { x: 0, y: 1 }),
-            cursor_state: Default::default(),
-            game_cursor_position: pos(0, 0),
-        }
-    }
-
-    /// Returns true if the (inner-relative) position of the given entity is
-    /// visible within this viewport
-    pub fn visible<E: Positioned>(&self, ent: &E) -> bool {
-        self.on_screen(ent.position()).within(self.game.inner())
-    }
-
-    /// Convert the given inner-relative position to one on the actual screen
-    fn on_screen(&self, pos: Position) -> Position {
-        pos + self.inner.position + self.game.inner().position
-    }
-}
-
-impl<W> Debug for Viewport<W> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "Viewport {{ outer: {:?}, inner: {:?}, out: <OUT> }}",
-            self.outer, self.inner
-        )
-    }
-}
-
-impl<W: Write> Viewport<W> {
-    /// Draw the given entity to the viewport at its position, if visible
-    #[allow(clippy::borrowed_box)]
-    pub fn draw<T: DrawWithNeighbors>(
-        &mut self,
-        entity: &T,
-        neighbors: &Neighbors<Vec<&Box<dyn Entity>>>,
-    ) -> io::Result<()> {
-        if !self.visible(entity) {
-            return Ok(());
-        }
-        self.cursor_goto(entity.position())?;
-        entity.do_draw_with_neighbors(self, neighbors)?;
-        self.reset_cursor()
-    }
-
-    fn reset_cursor(&mut self) -> io::Result<()> {
-        self.cursor_goto(self.game_cursor_position)
-    }
-
-    /// Move the cursor to the given inner-relative position
-    pub fn cursor_goto(&mut self, pos: Position) -> io::Result<()> {
-        write!(self, "{}", self.on_screen(pos).cursor_goto())
-    }
-
-    /// Clear whatever single character is drawn at the given inner-relative
-    /// position, if visible
-    pub fn clear(&mut self, pos: Position) -> io::Result<()> {
-        write!(self, "{} ", self.on_screen(pos).cursor_goto(),)?;
-        self.reset_cursor()
-    }
-
-    /// Initialize this viewport by drawing its outer box to the screen
-    pub fn init(&mut self) -> io::Result<()> {
-        draw_box(self, self.game, BoxStyle::Thin)
-    }
-
-    /// Write a message to the message area on the screen
-    ///
-    /// Will overwrite any message already present, and if the given message is
-    /// longer than the screen will truncate. This means callers should handle
-    /// message buffering and ellipsisization
-    pub fn write_message(&mut self, msg: &str) -> io::Result<usize> {
-        let msg_to_write = if msg.len() <= self.outer.dimensions.w as usize {
-            msg
-        } else {
-            &msg[0..self.outer.dimensions.w as usize]
-        };
-        write!(
-            self,
-            "{}{}{}",
-            self.outer.position.cursor_goto(),
-            msg_to_write,
-            clone_times::<_, String>(
-                " ".to_string(),
-                self.outer.dimensions.w - msg.len() as u16
-            ),
-        )?;
-        self.reset_cursor()?;
-        Ok(msg_to_write.len())
-    }
-
-    pub fn clear_message(&mut self) -> io::Result<()> {
-        write!(
-            self,
-            "{}{}",
-            self.outer.position.cursor_goto(),
-            clone_times::<_, String>(
-                " ".to_string(),
-                self.outer.dimensions.w as u16
-            )
-        )?;
-        self.reset_cursor()
-    }
-
-    /// Write a prompt requesting text input to the message area on the screen.
-    ///
-    /// Will overwrite any message already present, and if the given message is
-    /// longer than the screen will truncate. This means callers should handle
-    /// message buffering and ellipsisization
-    pub fn write_prompt<'a, 'b>(&'a mut self, msg: &'b str) -> io::Result<()> {
-        let len = self.write_message(msg)? + 1;
-        let pos = self.outer.position + pos(len as i16, 0);
-        self.cursor_state = CursorState::Prompt(pos);
-        write!(self, "{}", pos.cursor_goto())?;
-        self.flush()
-    }
-
-    pub fn push_prompt_chr(&mut self, chr: char) -> io::Result<()> {
-        if let CursorState::Prompt(pos) = self.cursor_state {
-            write!(self, "{}", chr)?;
-            self.cursor_state = CursorState::Prompt(pos + Direction::Right);
-        }
-        Ok(())
-    }
-
-    pub fn pop_prompt_chr(&mut self) -> io::Result<()> {
-        if let CursorState::Prompt(pos) = self.cursor_state {
-            let new_pos = pos + Direction::Left;
-            write!(
-                self,
-                "{} {}",
-                new_pos.cursor_goto(),
-                new_pos.cursor_goto()
-            )?;
-            self.cursor_state = CursorState::Prompt(new_pos);
-        }
-        Ok(())
-    }
-
-    pub fn clear_prompt(&mut self) -> io::Result<()> {
-        self.clear_message()?;
-        self.cursor_state = CursorState::Game;
-        Ok(())
-    }
-
-    pub fn write_menu(&mut self, menu: &MenuInfo) -> io::Result<()> {
-        let menu_dims = menu.dimensions();
-
-        // TODO: check if the menu is too big
-
-        let menu_position = self.game.position + pos(1, 1);
-
-        let menu_box = BoundingBox {
-            dimensions: menu_dims,
-            position: menu_position,
-        };
-
-        debug!("writing menu at: {:?}", menu_box);
-
-        draw_box(self, menu_box, BoxStyle::Thin)?;
-
-        write!(
-            self,
-            "{}{}",
-            (menu_position + pos(2, 2)).cursor_goto(),
-            menu.prompt
-        )?;
-
-        for (idx, option) in menu.options.iter().enumerate() {
-            write!(
-                self,
-                "{}{}",
-                (menu_position + pos(2, 4 + idx as i16)).cursor_goto(),
-                option
-            )?;
-        }
-
-        Ok(())
-    }
-}
-
-impl<W> Positioned for Viewport<W> {
-    fn position(&self) -> Position {
-        self.outer.position
-    }
-}
-
-impl<W: Write> Write for Viewport<W> {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.out.write(buf)
-    }
-
-    fn flush(&mut self) -> io::Result<()> {
-        self.out.flush()
-    }
-
-    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
-        self.out.write_all(buf)
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::types::Dimensions;
-
-    #[test]
-    fn test_visible() {
-        assert!(Viewport::new(
-            BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
-            BoundingBox {
-                position: Position { x: -10, y: -10 },
-                dimensions: Dimensions { w: 15, h: 15 },
-            },
-            ()
-        )
-        .visible(&Position { x: 13, y: 13 }));
-
-        assert!(!Viewport::new(
-            BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
-            BoundingBox {
-                position: Position { x: -10, y: -10 },
-                dimensions: Dimensions { w: 15, h: 15 },
-            },
-            (),
-        )
-        .visible(&Position { x: 1, y: 1 }));
-    }
-
-    #[test]
-    fn test_write_menu() {
-        let buf: Vec<u8> = Vec::new();
-
-        let mut viewport = Viewport::new(
-            BoundingBox::at_origin(Dimensions::default()),
-            BoundingBox::at_origin(Dimensions::default()),
-            buf,
-        );
-
-        let menu = MenuInfo::new(
-            "Test menu".to_string(),
-            vec!["option 1".to_string(), "option 2".to_string()],
-        );
-
-        viewport.write_menu(&menu).unwrap();
-
-        let res = std::str::from_utf8(&viewport.out).unwrap();
-        assert!(res.contains("Test menu"));
-        assert!(res.contains("option 1"));
-        assert!(res.contains("option 2"));
-    }
-}