mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-27 11:16:03 +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?
|
// This probably doesn't need to be public?
|
||||||
mod menubar;
|
mod menubar;
|
||||||
|
mod xy;
|
||||||
|
|
||||||
mod div;
|
mod div;
|
||||||
mod utf8;
|
mod utf8;
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
|
|
||||||
|
pub use xy::XY;
|
||||||
|
|
||||||
use backend::{Backend, NcursesBackend};
|
use backend::{Backend, NcursesBackend};
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
@ -125,7 +125,7 @@ impl Printer {
|
|||||||
/// # let printer = Printer::new((6,4), theme::load_default());
|
/// # let printer = Printer::new((6,4), theme::load_default());
|
||||||
/// printer.print_box((0,0), (6,4));
|
/// 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 start_v = start.into();
|
||||||
let size_v = size.into() - (1, 1);
|
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.
|
//! Points on the 2D character grid.
|
||||||
|
use XY;
|
||||||
|
|
||||||
use std::ops::{Add, Div, Mul, Sub};
|
use std::ops::{Add, Div, Mul, Sub};
|
||||||
use std::cmp::{Ordering, max, min};
|
use std::cmp::{Ordering, max, min};
|
||||||
|
|
||||||
/// Simple 2D size, in characters.
|
/// Simple 2D size, in characters.
|
||||||
#[derive(Clone,Copy,PartialEq,Debug)]
|
pub type Vec2 = XY<usize>;
|
||||||
pub struct Vec2 {
|
|
||||||
/// X coordinate (column), from left to right.
|
|
||||||
pub x: usize,
|
|
||||||
/// Y coordinate (row), from top to bottom.
|
|
||||||
pub y: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Vec2 {
|
impl PartialOrd for Vec2 {
|
||||||
fn partial_cmp(&self, other: &Vec2) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Vec2) -> Option<Ordering> {
|
||||||
@ -27,21 +22,30 @@ impl PartialOrd for Vec2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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.
|
/// 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))
|
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.
|
/// 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))
|
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.
|
/// Returns a vector with the X component of self, and y=0.
|
||||||
pub fn keep_x(&self) -> Self {
|
pub fn keep_x(&self) -> Self {
|
||||||
Vec2::new(self.x, 0)
|
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 {
|
impl From<(i32, i32)> for Vec2 {
|
||||||
fn from((x, y): (i32, i32)) -> Self {
|
fn from((x, y): (i32, i32)) -> Self {
|
||||||
(x as usize, y as usize).into()
|
(x as usize, y as usize).into()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
|
use XY;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use super::{View, ViewWrapper};
|
use super::{View, ViewWrapper};
|
||||||
|
|
||||||
@ -13,8 +14,7 @@ use super::{View, ViewWrapper};
|
|||||||
/// let view = BoxView::fixed_size((20,4), TextView::new("Hello!"));
|
/// let view = BoxView::fixed_size((20,4), TextView::new("Hello!"));
|
||||||
/// ```
|
/// ```
|
||||||
pub struct BoxView<T: View> {
|
pub struct BoxView<T: View> {
|
||||||
width: Option<usize>,
|
size: XY<Option<usize>>,
|
||||||
height: Option<usize>,
|
|
||||||
view: T,
|
view: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,8 +31,7 @@ impl<T: View> BoxView<T> {
|
|||||||
/// `None` values will use the wrapped view's preferences.
|
/// `None` values will use the wrapped view's preferences.
|
||||||
pub fn new(width: Option<usize>, height: Option<usize>, view: T) -> Self {
|
pub fn new(width: Option<usize>, height: Option<usize>, view: T) -> Self {
|
||||||
BoxView {
|
BoxView {
|
||||||
width: width,
|
size: (width, height).into(),
|
||||||
height: height,
|
|
||||||
view: view,
|
view: view,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,15 +59,15 @@ impl<T: View> ViewWrapper for BoxView<T> {
|
|||||||
|
|
||||||
fn wrap_get_min_size(&mut self, req: Vec2) -> Vec2 {
|
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)
|
Vec2::new(w, h)
|
||||||
} else {
|
} else {
|
||||||
let req = Vec2::new(min(req.x, self.width),
|
let req = Vec2::new(min(req.x, self.size.x),
|
||||||
min(req.y, self.height));
|
min(req.y, self.size.y));
|
||||||
let child_size = self.view.get_min_size(req);
|
let child_size = self.view.get_min_size(req);
|
||||||
|
|
||||||
Vec2::new(self.width.unwrap_or(child_size.x),
|
Vec2::new(self.size.x.unwrap_or(child_size.x),
|
||||||
self.height.unwrap_or(child_size.y))
|
self.size.y.unwrap_or(child_size.y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,11 @@
|
|||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
use XY;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
|
|
||||||
/// Location of the view on screen
|
/// Location of the view on screen
|
||||||
#[derive(PartialEq,Debug,Clone)]
|
pub type Position = XY<Offset>;
|
||||||
pub struct Position {
|
|
||||||
/// Horizontal offset
|
|
||||||
pub x: Offset,
|
|
||||||
/// Vertical offset
|
|
||||||
pub y: Offset,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Position {
|
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.
|
/// Returns a position centered on both axis.
|
||||||
pub fn center() -> Self {
|
pub fn center() -> Self {
|
||||||
Position::new(Offset::Center, Offset::Center)
|
Position::new(Offset::Center, Offset::Center)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use XY;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::View;
|
use view::View;
|
||||||
use printer::Printer;
|
use printer::Printer;
|
||||||
@ -8,6 +9,39 @@ use super::scroll::ScrollBase;
|
|||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
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
|
/// A simple view showing a fixed text
|
||||||
pub struct TextView {
|
pub struct TextView {
|
||||||
content: String,
|
content: String,
|
||||||
@ -17,7 +51,7 @@ pub struct TextView {
|
|||||||
|
|
||||||
// ScrollBase make many scrolling-related things easier
|
// ScrollBase make many scrolling-related things easier
|
||||||
scrollbase: ScrollBase,
|
scrollbase: ScrollBase,
|
||||||
last_size: Option<Vec2>,
|
last_size: Option<XY<SizeCache>>,
|
||||||
width: Option<usize>,
|
width: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,13 +121,7 @@ impl TextView {
|
|||||||
fn is_cache_valid(&self, size: Vec2) -> bool {
|
fn is_cache_valid(&self, size: Vec2) -> bool {
|
||||||
match self.last_size {
|
match self.last_size {
|
||||||
None => false,
|
None => false,
|
||||||
Some(last) => {
|
Some(ref last) => last.x.accept(size.x) && last.y.accept(size.y),
|
||||||
if last.x != size.x {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
(last.y < self.rows.len()) == (size.y < self.rows.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +144,10 @@ impl TextView {
|
|||||||
.max()
|
.max()
|
||||||
.map(|w| w + scrollbar);
|
.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 {
|
fn needs_relayout(&self) -> bool {
|
||||||
self.last_size == None
|
self.last_size.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_min_size(&mut self, size: Vec2) -> Vec2 {
|
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);
|
self.compute_rows(size);
|
||||||
Vec2::new(self.width.unwrap_or(0), self.rows.len())
|
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