Add method for writing option menus to viewport
Add a method for writing single-choice menus to the viewport, within a box. Unused for now.
This commit is contained in:
parent
48fb3f6624
commit
e2d2f011c6
4 changed files with 115 additions and 21 deletions
|
@ -1,5 +1,6 @@
|
|||
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;
|
||||
|
@ -215,12 +216,21 @@ pub fn draw_box<W: Write>(
|
|||
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.cursor_goto(),
|
||||
make_box(style, bbox.dimensions)
|
||||
)
|
||||
(bbox.position + pos(0, i as i16)).cursor_goto(),
|
||||
line
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -3,6 +3,7 @@ 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};
|
||||
|
@ -192,6 +193,41 @@ impl<W: Write> Viewport<W> {
|
|||
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> {
|
||||
|
@ -218,7 +254,6 @@ impl<W: Write> Write for Viewport<W> {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::types::Dimensions;
|
||||
// use proptest::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_visible() {
|
||||
|
@ -243,19 +278,26 @@ mod tests {
|
|||
.visible(&Position { x: 1, y: 1 }));
|
||||
}
|
||||
|
||||
// proptest! {
|
||||
// #[test]
|
||||
// fn nothing_is_visible_in_viewport_off_screen(pos: Position, outer: BoundingBox) {
|
||||
// let invisible_viewport = Viewport {
|
||||
// outer,
|
||||
// inner: BoundingBox {
|
||||
// position: Position {x: -(outer.dimensions.w as i16), y: -(outer.dimensions.h as i16)},
|
||||
// dimensions: outer.dimensions,
|
||||
// },
|
||||
// out: ()
|
||||
// };
|
||||
#[test]
|
||||
fn test_write_menu() {
|
||||
let buf: Vec<u8> = Vec::new();
|
||||
|
||||
// assert!(!invisible_viewport.visible(&pos));
|
||||
// }
|
||||
// }
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
|
31
src/types/menu.rs
Normal file
31
src/types/menu.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use crate::types::Dimensions;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MenuInfo {
|
||||
pub prompt: String,
|
||||
pub options: Vec<String>,
|
||||
}
|
||||
|
||||
impl MenuInfo {
|
||||
pub fn new(prompt: String, options: Vec<String>) -> Self {
|
||||
MenuInfo { prompt, options }
|
||||
}
|
||||
|
||||
/// Returns the inner dimensions of a box necessary to draw this menu. Will
|
||||
/// not trim either dimension to the size of the terminal
|
||||
pub fn dimensions(&self) -> Dimensions {
|
||||
Dimensions {
|
||||
w: self
|
||||
.options
|
||||
.iter()
|
||||
.map(|s| s.len())
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
.max(self.prompt.len()) as u16
|
||||
+ 4,
|
||||
h: self.options.len() as u16
|
||||
+ if self.prompt.is_empty() { 0 } else { 2 }
|
||||
+ 4,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,10 +5,13 @@ use std::cmp::max;
|
|||
use std::cmp::Ordering;
|
||||
use std::ops;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub mod collision;
|
||||
pub mod command;
|
||||
pub mod direction;
|
||||
pub mod entity_map;
|
||||
pub mod menu;
|
||||
|
||||
pub use collision::Collision;
|
||||
pub use direction::Direction;
|
||||
pub use direction::Direction::*;
|
||||
|
@ -78,6 +81,14 @@ impl BoundingBox {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn ll_corner(self) -> Position {
|
||||
self.position
|
||||
+ (Position {
|
||||
x: 0,
|
||||
y: self.dimensions.h as i16,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a bounding box representing the *inside* of this box if it was
|
||||
/// drawn on the screen.
|
||||
pub fn inner(self) -> BoundingBox {
|
||||
|
|
Loading…
Reference in a new issue