Allow negative relative offset in Position

Was causing crashes when popup-SelectViews were larger than their
parent.
Made some methods on Vec2 more generic to XY<T: ...>
This commit is contained in:
Alexandre Bury 2017-01-10 17:39:21 -08:00
parent ddff15b6cf
commit 14f971e2bc
7 changed files with 90 additions and 65 deletions

View File

@ -17,6 +17,7 @@ version = "0.6"
[dependencies] [dependencies]
chan = "0.1.18" chan = "0.1.18"
num = "0.1"
chan-signal = "0.1" chan-signal = "0.1"
odds = "0.2" odds = "0.2"
toml = "0.2" toml = "0.2"

View File

@ -16,6 +16,7 @@
//! Its actual direction depends on the orientation. //! Its actual direction depends on the orientation.
use vec::Vec2; use vec::Vec2;
use XY;
/// Describes a vertical or horizontal orientation for a view. /// Describes a vertical or horizontal orientation for a view.
#[derive(Clone,Copy,Debug,PartialEq)] #[derive(Clone,Copy,Debug,PartialEq)]
@ -31,8 +32,8 @@ impl Orientation {
/// ///
/// (`Horizontal` will return the x value, /// (`Horizontal` will return the x value,
/// and `Vertical` will return the y value.) /// and `Vertical` will return the y value.)
pub fn get(&self, v: &Vec2) -> usize { pub fn get<T: Clone>(&self, v: &XY<T>) -> T {
*v.get(*self) v.get(*self).clone()
} }
/// Returns the other orientation. /// Returns the other orientation.
@ -45,7 +46,7 @@ impl Orientation {
/// Returns a mutable reference to the component of the given vector /// Returns a mutable reference to the component of the given vector
/// corresponding to this orientation. /// corresponding to this orientation.
pub fn get_ref<'a, 'b>(&'a self, v: &'b mut Vec2) -> &'b mut usize { pub fn get_ref<'a, 'b, T>(&'a self, v: &'b mut XY<T>) -> &'b mut T {
match *self { match *self {
Orientation::Horizontal => &mut v.x, Orientation::Horizontal => &mut v.x,
Orientation::Vertical => &mut v.y, Orientation::Vertical => &mut v.y,

View File

@ -63,6 +63,7 @@ extern crate toml;
extern crate unicode_segmentation; extern crate unicode_segmentation;
extern crate unicode_width; extern crate unicode_width;
extern crate odds; extern crate odds;
extern crate num;
#[macro_use] #[macro_use]
extern crate chan; extern crate chan;

View File

@ -2,6 +2,7 @@
use XY; use XY;
use direction::Orientation; use direction::Orientation;
use num::traits::Zero;
use std::cmp::{Ordering, max, min}; use std::cmp::{Ordering, max, min};
use std::ops::{Add, Div, Mul, Sub}; use std::ops::{Add, Div, Mul, Sub};
@ -30,81 +31,75 @@ impl PartialOrd for XY<usize> {
} }
} }
impl XY<usize> { impl<T: Ord> XY<T> {
/// Returns `true` if `self` could fit inside `other`.
///
/// Shortcut for `self.x <= other.x && self.y <= other.y`.
pub fn fits_in<O: Into<Self>>(&self, other: O) -> bool {
let other = other.into();
self.x <= other.x && self.y <= other.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: Into<Vec2>, B: Into<Vec2>>(a: A, b: B) -> Self { pub fn max<A: Into<XY<T>>, B: Into<XY<T>>>(a: A, b: B) -> Self {
let a = a.into(); let a = a.into();
let b = b.into(); let b = b.into();
a.zip_map(b, max) a.zip_map(b, max)
} }
/// 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: Into<Vec2>, B: Into<Vec2>>(a: A, b: B) -> Self { pub fn min<A: Into<XY<T>>, B: Into<XY<T>>>(a: A, b: B) -> Self {
let a = a.into(); let a = a.into();
let b = b.into(); let b = b.into();
a.zip_map(b, min) a.zip_map(b, min)
} }
/// Returns the minimum of `self` and `other`. /// Returns the minimum of `self` and `other`.
pub fn or_min<T: Into<Vec2>>(self, other: T) -> Self { pub fn or_min<O: Into<XY<T>>>(self, other: O) -> Self {
Vec2::min(self, other) Self::min(self, other)
} }
/// Returns the maximum of `self` and `other`. /// Returns the maximum of `self` and `other`.
pub fn or_max<T: Into<Vec2>>(self, other: T) -> Self { pub fn or_max<O: Into<XY<T>>>(self, other: O) -> Self {
Vec2::max(self, other) Self::max(self, other)
}
}
impl<T: Ord + Add<Output = T> + Clone> XY<T> {
/// Returns (max(self.x,other.x), self.y+other.y)
pub fn stack_vertical(&self, other: &Self) -> Self {
Self::new(max(self.x.clone(), other.x.clone()),
self.y.clone() + other.y.clone())
} }
/// Returns (self.x+other.x, max(self.y,other.y))
pub fn stack_horizontal(&self, other: &Self) -> Self {
Self::new(self.x.clone() + other.x.clone(),
max(self.y.clone(), other.y.clone()))
}
}
impl<T: Zero + Clone> XY<T> {
/// 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) Self::new(self.x.clone(), T::zero())
} }
/// Returns a vector with the Y component of self, and x=0. /// Returns a vector with the Y component of self, and x=0.
pub fn keep_y(&self) -> Self { pub fn keep_y(&self) -> Self {
Vec2::new(0, self.y) Self::new(T::zero(), self.y.clone())
} }
/// Alias for `Vec2::new(0,0)`. /// Alias for `Self::new(0,0)`.
pub fn zero() -> Self { pub fn zero() -> Self {
Vec2::new(0, 0) Self::new(T::zero(), T::zero())
} }
}
/// Returns (max(self.x,other.x), self.y+other.y) impl <T: Into<XY<usize>>> From<T> for XY<isize> {
pub fn stack_vertical(&self, other: &Vec2) -> Vec2 { fn from(t: T) -> Self {
Vec2::new(max(self.x, other.x), self.y + other.y) let other = t.into();
} Self::new(other.x as isize, other.y as isize)
/// Returns (self.x+other.x, max(self.y,other.y))
pub fn stack_horizontal(&self, other: &Vec2) -> Vec2 {
Vec2::new(self.x + other.x, max(self.y, other.y))
}
/// Returns `true` if `self` could fit inside `other`.
///
/// Shortcut for `self.x <= other.x && self.y <= other.y`.
pub fn fits_in<T: Into<Vec2>>(&self, other: T) -> bool {
let other = other.into();
self.x <= other.x && self.y <= other.y
}
/// Returns a new `Vec2` with the axis `o` set to `value`.
pub fn with_axis(&self, o: Orientation, value: usize) -> Self {
let mut new = *self;
*o.get_ref(&mut new) = value;
new
}
/// Returns a new `Vec2` with the axis `o` set to the value from `other`.
pub fn with_axis_from(&self, o: Orientation, other: &Vec2) -> Self {
let mut new = *self;
new.set_axis_from(o, other);
new
}
/// Sets the axis `o` on `self` to the value from `other`.
pub fn set_axis_from(&mut self, o: Orientation, other: &Vec2) {
*o.get_ref(self) = o.get(other);
} }
} }
@ -121,27 +116,27 @@ impl From<(u32, u32)> for XY<usize> {
} }
impl<T: Into<Vec2>> Add<T> for XY<usize> { impl<T: Add<Output=T>, O: Into<XY<T>>> Add<O> for XY<T> {
type Output = Vec2; type Output = Self;
fn add(self, other: T) -> Vec2 { fn add(self, other: O) -> Self {
self.zip_map(other.into(), Add::add) self.zip_map(other.into(), Add::add)
} }
} }
impl<T: Into<Vec2>> Sub<T> for XY<usize> { impl<T: Sub<Output=T>, O: Into<XY<T>>> Sub<O> for XY<T> {
type Output = Vec2; type Output = Self;
fn sub(self, other: T) -> Vec2 { fn sub(self, other: O) -> Self {
self.zip_map(other.into(), Sub::sub) self.zip_map(other.into(), Sub::sub)
} }
} }
impl Div<usize> for XY<usize> { impl <T: Clone + Div<Output=T>> Div<T> for XY<T> {
type Output = Vec2; type Output = Self;
fn div(self, other: usize) -> Vec2 { fn div(self, other: T) -> Self {
self.map(|s| s / other) self.map(|s| s / other.clone())
} }
} }

View File

@ -18,7 +18,7 @@ impl Position {
} }
/// Returns a position relative to the parent on both axis. /// Returns a position relative to the parent on both axis.
pub fn parent<T: Into<Vec2>>(offset: T) -> Self { pub fn parent<T: Into<XY<isize>>>(offset: T) -> Self {
let offset = offset.into(); let offset = offset.into();
Position::new(Offset::Parent(offset.x), Offset::Parent(offset.y)) Position::new(Offset::Parent(offset.x), Offset::Parent(offset.y))
} }
@ -55,7 +55,7 @@ pub enum Offset {
/// Offset from the previous layer's top-left corner. /// Offset from the previous layer's top-left corner.
/// ///
/// If this is the first layer, behaves like `Absolute`. /// If this is the first layer, behaves like `Absolute`.
Parent(usize), // TODO: use a signed vec for negative offset? Parent(isize),
} }
impl Offset { impl Offset {
@ -70,7 +70,7 @@ impl Offset {
Offset::Center => (available - size) / 2, Offset::Center => (available - size) / 2,
Offset::Absolute(offset) => min(offset, available - size), Offset::Absolute(offset) => min(offset, available - size),
Offset::Parent(offset) => { Offset::Parent(offset) => {
min(parent + offset, available - size) min((parent as isize + offset) as usize, available - size)
} }
} }
} }

View File

@ -1,6 +1,7 @@
use Cursive; use Cursive;
use Printer; use Printer;
use With; use With;
use XY;
use align::{Align, HAlign, VAlign}; use align::{Align, HAlign, VAlign};
use direction::Direction; use direction::Direction;
use event::{Callback, Event, EventResult, Key}; use event::{Callback, Event, EventResult, Key};
@ -425,8 +426,13 @@ impl<T: 'static> View for SelectView<T> {
// This is the offset for the label text. // This is the offset for the label text.
// We'll want to show the popup so that the text matches. // We'll want to show the popup so that the text matches.
// It'll be soo cool. // It'll be soo cool.
let text_offset = let item_length = self.items[focus].label.len();
(self.last_size.x - self.items[focus].label.len()) / 2; let text_offset = if self.last_size.x >= item_length {
(self.last_size.x - item_length) / 2
} else {
// We were too small to show the entire item last time.
0
};
// The total offset for the window is: // The total offset for the window is:
// * the last absolute offset at which we drew this view // * the last absolute offset at which we drew this view
// * shifted to the top of the focus (so the line matches) // * shifted to the top of the focus (so the line matches)
@ -444,7 +450,7 @@ impl<T: 'static> View for SelectView<T> {
// A nice effect is that window resizes will keep both // A nice effect is that window resizes will keep both
// layers together. // layers together.
let current_offset = s.screen().offset(); let current_offset = s.screen().offset();
let offset = offset - current_offset; let offset = XY::<isize>::from(offset) - current_offset;
// And finally, put the view in view! // And finally, put the view in view!
s.screen_mut() s.screen_mut()
.add_layer_at(Position::parent(offset), .add_layer_at(Position::parent(offset),

View File

@ -56,6 +56,27 @@ impl<T> XY<T> {
} }
} }
impl <T: Clone> XY<T> {
/// Returns a new `XY` with the axis `o` set to `value`.
pub fn with_axis(&self, o: Orientation, value: T) -> Self {
let mut new = self.clone();
*o.get_ref(&mut new) = value;
new
}
/// Returns a new `XY` with the axis `o` set to the value from `other`.
pub fn with_axis_from(&self, o: Orientation, other: &Self) -> Self {
let mut new = self.clone();
new.set_axis_from(o, other);
new
}
/// Sets the axis `o` on `self` to the value from `other`.
pub fn set_axis_from(&mut self, o: Orientation, other: &Self) {
*o.get_ref(self) = o.get(other);
}
}
impl<T> XY<Option<T>> { impl<T> XY<Option<T>> {
/// Returns a new `XY` by calling `unwrap_or` on each axis. /// Returns a new `XY` by calling `unwrap_or` on each axis.
pub fn unwrap_or(self, other: XY<T>) -> XY<T> { pub fn unwrap_or(self, other: XY<T>) -> XY<T> {