about summary refs log tree commit diff
path: root/src/display/viewport.rs
diff options
context:
space:
mode:
authorGriffin Smith <root@gws.fyi>2019-07-07T16·41-0400
committerGriffin Smith <root@gws.fyi>2019-07-07T16·41-0400
commitc643ee1dfcb8d44b8cd198c768f31dd7659f2ff9 (patch)
tree8178c613e55c00944a6417a85ad304cf2af05587 /src/display/viewport.rs
parent78a52142d191d25a74cb2124d5cca8a69d51ba7f (diff)
Add messages, with global lookup map
Add support for messages, along with a global lookup map and random
choice of messages.
Diffstat (limited to 'src/display/viewport.rs')
-rw-r--r--src/display/viewport.rs107
1 files changed, 71 insertions, 36 deletions
diff --git a/src/display/viewport.rs b/src/display/viewport.rs
index bd2fac07146f..24cc5272cb8e 100644
--- a/src/display/viewport.rs
+++ b/src/display/viewport.rs
@@ -1,5 +1,8 @@
+use super::BoxStyle;
 use super::Draw;
-use super::{make_box, BoxStyle};
+use crate::display::draw_box::draw_box;
+use crate::display::utils::clone_times;
+use crate::display::utils::times;
 use crate::types::{BoundingBox, Position, Positioned};
 use std::fmt::{self, Debug};
 use std::io::{self, Write};
@@ -10,36 +13,47 @@ pub struct Viewport<W> {
     /// 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
+    /// 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,
 }
-
-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> 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 }),
+        }
     }
-}
 
-impl<W> Viewport<W> {
     /// Returns true if the (inner-relative) position of the given entity is
     /// visible within this viewport
-    fn visible<E: Positioned>(&self, ent: &E) -> bool {
-        self.on_screen(ent.position()).within(self.outer.inner())
+    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.outer.inner().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
+        )
     }
 }
 
@@ -49,25 +63,46 @@ impl<W: Write> Viewport<W> {
         if !self.visible(entity) {
             return Ok(());
         }
-        write!(
-            self,
-            "{}",
-            (entity.position()
-                + self.inner.position
-                + self.outer.inner().position)
-                .cursor_goto()
-        )?;
+        self.cursor_goto(entity.position())?;
         entity.do_draw(self)
     }
 
-    /// Clear whatever is drawn at the given inner-relative position, if visible
+    /// 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(),)
     }
 
     /// Initialize this viewport by drawing its outer box to the screen
     pub fn init(&mut self) -> io::Result<()> {
-        write!(self, "{}", make_box(BoxStyle::Thin, self.outer.dimensions))
+        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<()> {
+        write!(
+            self,
+            "{}{}{}",
+            self.outer.position.cursor_goto(),
+            if msg.len() <= self.outer.dimensions.w as usize {
+                msg
+            } else {
+                &msg[0..self.outer.dimensions.w as usize]
+            },
+            clone_times::<_, String>(
+                " ".to_string(),
+                self.outer.dimensions.w - msg.len() as u16
+            ),
+        )
     }
 }
 
@@ -99,24 +134,24 @@ mod tests {
 
     #[test]
     fn test_visible() {
-        assert!(Viewport {
-            outer: BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
-            inner: BoundingBox {
+        assert!(Viewport::new(
+            BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
+            BoundingBox {
                 position: Position { x: -10, y: -10 },
                 dimensions: Dimensions { w: 15, h: 15 },
             },
-            out: (),
-        }
+            ()
+        )
         .visible(&Position { x: 13, y: 13 }));
 
-        assert!(!Viewport {
-            outer: BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
-            inner: BoundingBox {
+        assert!(!Viewport::new(
+            BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
+            BoundingBox {
                 position: Position { x: -10, y: -10 },
                 dimensions: Dimensions { w: 15, h: 15 },
             },
-            out: (),
-        }
+            (),
+        )
         .visible(&Position { x: 1, y: 1 }));
     }