mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Add XY generic struct
This commit is contained in:
parent
f35aebd66e
commit
7f530e34b7
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
38
src/vec.rs
38
src/vec.rs
@ -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()
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
48
src/xy.rs
Normal 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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user