Add XY generic struct

This commit is contained in:
Alexandre Bury 2016-07-12 21:01:11 -07:00
parent f35aebd66e
commit 7f530e34b7
7 changed files with 121 additions and 56 deletions

View File

@ -47,12 +47,15 @@ pub mod menu;
// This probably doesn't need to be public?
mod menubar;
mod xy;
mod div;
mod utf8;
mod backend;
pub use xy::XY;
use backend::{Backend, NcursesBackend};
use std::any::Any;

View File

@ -125,7 +125,7 @@ impl Printer {
/// # let printer = Printer::new((6,4), theme::load_default());
/// printer.print_box((0,0), (6,4));
/// ```
pub fn print_box<T: Into<Vec2>>(&self, start: T, size: T) {
pub fn print_box<T: Into<Vec2>, S: Into<Vec2>>(&self, start: T, size: S) {
let start_v = start.into();
let size_v = size.into() - (1, 1);

View File

@ -1,16 +1,11 @@
//! Points on the 2D character grid.
use XY;
use std::ops::{Add, Div, Mul, Sub};
use std::cmp::{Ordering, max, min};
/// Simple 2D size, in characters.
#[derive(Clone,Copy,PartialEq,Debug)]
pub struct Vec2 {
/// X coordinate (column), from left to right.
pub x: usize,
/// Y coordinate (row), from top to bottom.
pub y: usize,
}
pub type Vec2 = XY<usize>;
impl PartialOrd for Vec2 {
fn partial_cmp(&self, other: &Vec2) -> Option<Ordering> {
@ -27,21 +22,30 @@ impl PartialOrd for Vec2 {
}
impl Vec2 {
/// Creates a new Vec2 from coordinates.
pub fn new(x: usize, y: usize) -> Self {
Vec2 { x: x, y: y }
}
/// Returns a new Vec2 that is a maximum per coordinate.
pub fn max(a: Self, b: Self) -> Self {
pub fn max<A: Into<Vec2>, B: Into<Vec2>>(a: A, b: B) -> Self {
let a = a.into();
let b = b.into();
Vec2::new(max(a.x, b.x), max(a.y, b.y))
}
/// Returns a new Vec2 that is no larger than any input in both dimensions.
pub fn min(a: Self, b: Self) -> Self {
pub fn min<A: Into<Vec2>, B: Into<Vec2>>(a: A, b: B) -> Self {
let a = a.into();
let b = b.into();
Vec2::new(min(a.x, b.x), min(a.y, b.y))
}
/// Returns the minimum of `self` and `other`.
pub fn or_min<T: Into<Vec2>>(self, other: T) -> Self {
Vec2::min(self, other)
}
/// Returns the maximum of `self` and `other`.
pub fn or_max<T: Into<Vec2>>(self, other: T) -> Self {
Vec2::max(self, other)
}
/// Returns a vector with the X component of self, and y=0.
pub fn keep_x(&self) -> Self {
Vec2::new(self.x, 0)
@ -68,12 +72,6 @@ impl Vec2 {
}
}
impl From<(usize, usize)> for Vec2 {
fn from((x, y): (usize, usize)) -> Self {
Vec2::new(x, y)
}
}
impl From<(i32, i32)> for Vec2 {
fn from((x, y): (i32, i32)) -> Self {
(x as usize, y as usize).into()

View File

@ -1,5 +1,6 @@
use std::cmp;
use XY;
use vec::Vec2;
use super::{View, ViewWrapper};
@ -13,8 +14,7 @@ use super::{View, ViewWrapper};
/// let view = BoxView::fixed_size((20,4), TextView::new("Hello!"));
/// ```
pub struct BoxView<T: View> {
width: Option<usize>,
height: Option<usize>,
size: XY<Option<usize>>,
view: T,
}
@ -31,8 +31,7 @@ impl<T: View> BoxView<T> {
/// `None` values will use the wrapped view's preferences.
pub fn new(width: Option<usize>, height: Option<usize>, view: T) -> Self {
BoxView {
width: width,
height: height,
size: (width, height).into(),
view: view,
}
}
@ -60,15 +59,15 @@ impl<T: View> ViewWrapper for BoxView<T> {
fn wrap_get_min_size(&mut self, req: Vec2) -> Vec2 {
if let (Some(w), Some(h)) = (self.width, self.height) {
if let (Some(w), Some(h)) = self.size.pair() {
Vec2::new(w, h)
} else {
let req = Vec2::new(min(req.x, self.width),
min(req.y, self.height));
let req = Vec2::new(min(req.x, self.size.x),
min(req.y, self.size.y));
let child_size = self.view.get_min_size(req);
Vec2::new(self.width.unwrap_or(child_size.x),
self.height.unwrap_or(child_size.y))
Vec2::new(self.size.x.unwrap_or(child_size.x),
self.size.y.unwrap_or(child_size.y))
}
}
}

View File

@ -1,21 +1,11 @@
use std::cmp::min;
use XY;
use vec::Vec2;
/// Location of the view on screen
#[derive(PartialEq,Debug,Clone)]
pub struct Position {
/// Horizontal offset
pub x: Offset,
/// Vertical offset
pub y: Offset,
}
pub type Position = XY<Offset>;
impl Position {
/// Creates a new position with the given offsets.
pub fn new(x: Offset, y: Offset) -> Self {
Position { x: x, y: y }
}
/// Returns a position centered on both axis.
pub fn center() -> Self {
Position::new(Offset::Center, Offset::Center)

View File

@ -1,3 +1,4 @@
use XY;
use vec::Vec2;
use view::View;
use printer::Printer;
@ -8,6 +9,39 @@ use super::scroll::ScrollBase;
use unicode_width::UnicodeWidthStr;
use unicode_segmentation::UnicodeSegmentation;
#[derive(PartialEq, Debug, Clone)]
struct SizeCache {
value: usize,
// If unconstrained, any request larger than this value
// would return the same size.
constrained: bool,
}
impl SizeCache {
fn new(value: usize, constrained: bool) -> Self {
SizeCache {
value: value,
constrained: constrained,
}
}
fn accept(&self, size: usize) -> bool {
if size < self.value {
false
} else if size == self.value {
true
} else {
!self.constrained
}
}
}
fn cache_size(size: Vec2, req: Vec2) -> XY<SizeCache> {
XY::new(SizeCache::new(size.x, size.x == req.x),
SizeCache::new(size.y, size.y == req.y))
}
/// A simple view showing a fixed text
pub struct TextView {
content: String,
@ -17,7 +51,7 @@ pub struct TextView {
// ScrollBase make many scrolling-related things easier
scrollbase: ScrollBase,
last_size: Option<Vec2>,
last_size: Option<XY<SizeCache>>,
width: Option<usize>,
}
@ -87,13 +121,7 @@ impl TextView {
fn is_cache_valid(&self, size: Vec2) -> bool {
match self.last_size {
None => false,
Some(last) => {
if last.x != size.x {
false
} else {
(last.y < self.rows.len()) == (size.y < self.rows.len())
}
}
Some(ref last) => last.x.accept(size.x) && last.y.accept(size.y),
}
}
@ -116,7 +144,10 @@ impl TextView {
.max()
.map(|w| w + scrollbar);
self.last_size = Some(size);
// Our resulting size.
let my_size =
size.or_min((self.width.unwrap_or(0), self.rows.len()));
self.last_size = Some(cache_size(my_size, size));
}
}
@ -231,14 +262,10 @@ impl View for TextView {
}
fn needs_relayout(&self) -> bool {
self.last_size == None
self.last_size.is_none()
}
fn get_min_size(&mut self, size: Vec2) -> Vec2 {
// If we have no directive, ask for a single big line.
// TODO: what if the text has newlines??
// Don't _force_ the max width, but take it if we have to.
self.compute_rows(size);
Vec2::new(self.width.unwrap_or(0), self.rows.len())
}

48
src/xy.rs Normal file
View File

@ -0,0 +1,48 @@
use std::iter;
/// A generic structure with a value for each axis.
#[derive(Debug,Clone,Copy,PartialEq)]
pub struct XY<T> {
/// X-axis value
pub x: T,
/// Y-axis value
pub y: T,
}
impl <T> XY<T> {
/// Creates a new `XY` from the given values.
pub fn new(x: T, y: T) -> Self {
XY {
x: x,
y: y,
}
}
/// Destructure self into a pair.
pub fn pair(self) -> (T, T) {
(self.x, self.y)
}
/// Return a XY with references to this one's values.
pub fn as_ref(&self) -> XY<&T> {
XY::new(&self.x, &self.y)
}
/// Creates an iterator that returns references to `x`, then `y`.
pub fn iter(&self) -> iter::Chain<iter::Once<&T>, iter::Once<&T>> {
iter::once(&self.x).chain(iter::once(&self.y))
}
}
impl <T: Copy> XY<T> {
/// Creates a `XY` with both `x` and `y` set to `value`.
pub fn both(value: T) -> Self {
XY::new(value, value)
}
}
impl <T> From<(T, T)> for XY<T> {
fn from((x, y): (T, T)) -> Self {
XY::new(x, y)
}
}