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::clone_times;
|
||||||
use crate::display::utils::times;
|
use crate::display::utils::times;
|
||||||
|
use crate::types::pos;
|
||||||
use crate::types::BoundingBox;
|
use crate::types::BoundingBox;
|
||||||
use crate::types::Dimensions;
|
use crate::types::Dimensions;
|
||||||
use crate::types::Neighbors;
|
use crate::types::Neighbors;
|
||||||
|
@ -215,12 +216,21 @@ pub fn draw_box<W: Write>(
|
||||||
bbox: BoundingBox,
|
bbox: BoundingBox,
|
||||||
style: BoxStyle,
|
style: BoxStyle,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
write!(
|
let box_str = make_box(style, bbox.dimensions);
|
||||||
out,
|
if bbox.position.x == 0 {
|
||||||
"{}{}",
|
write!(out, "{}{}", bbox.position.cursor_goto(), box_str)?;
|
||||||
bbox.position.cursor_goto(),
|
} else {
|
||||||
make_box(style, bbox.dimensions)
|
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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -3,6 +3,7 @@ use super::DrawWithNeighbors;
|
||||||
use crate::display::draw_box::draw_box;
|
use crate::display::draw_box::draw_box;
|
||||||
use crate::display::utils::clone_times;
|
use crate::display::utils::clone_times;
|
||||||
use crate::entities::entity::Entity;
|
use crate::entities::entity::Entity;
|
||||||
|
use crate::types::menu::MenuInfo;
|
||||||
use crate::types::Neighbors;
|
use crate::types::Neighbors;
|
||||||
use crate::types::{pos, BoundingBox, Direction, Position, Positioned};
|
use crate::types::{pos, BoundingBox, Direction, Position, Positioned};
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
|
@ -192,6 +193,41 @@ impl<W: Write> Viewport<W> {
|
||||||
self.cursor_state = CursorState::Game;
|
self.cursor_state = CursorState::Game;
|
||||||
Ok(())
|
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> {
|
impl<W> Positioned for Viewport<W> {
|
||||||
|
@ -218,7 +254,6 @@ impl<W: Write> Write for Viewport<W> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::types::Dimensions;
|
use crate::types::Dimensions;
|
||||||
// use proptest::prelude::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_visible() {
|
fn test_visible() {
|
||||||
|
@ -243,19 +278,26 @@ mod tests {
|
||||||
.visible(&Position { x: 1, y: 1 }));
|
.visible(&Position { x: 1, y: 1 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// proptest! {
|
#[test]
|
||||||
// #[test]
|
fn test_write_menu() {
|
||||||
// fn nothing_is_visible_in_viewport_off_screen(pos: Position, outer: BoundingBox) {
|
let buf: Vec<u8> = Vec::new();
|
||||||
// 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: ()
|
|
||||||
// };
|
|
||||||
|
|
||||||
// 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::cmp::Ordering;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub mod collision;
|
pub mod collision;
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod direction;
|
pub mod direction;
|
||||||
pub mod entity_map;
|
pub mod entity_map;
|
||||||
|
pub mod menu;
|
||||||
|
|
||||||
pub use collision::Collision;
|
pub use collision::Collision;
|
||||||
pub use direction::Direction;
|
pub use direction::Direction;
|
||||||
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
|
/// Returns a bounding box representing the *inside* of this box if it was
|
||||||
/// drawn on the screen.
|
/// drawn on the screen.
|
||||||
pub fn inner(self) -> BoundingBox {
|
pub fn inner(self) -> BoundingBox {
|
||||||
|
|
Loading…
Reference in a new issue