Add position parameter to stack view layers

Can be centered, absolute or relative to the previous layer.
Can be set independently for each axis.
This commit is contained in:
Alexandre Bury 2016-07-01 19:19:43 -07:00
parent 985009e51c
commit 87cd1ce23f
4 changed files with 100 additions and 18 deletions

View File

@ -1,18 +1,21 @@
extern crate cursive; extern crate cursive;
use cursive::Cursive; use cursive::Cursive;
use cursive::view::{IdView, TextView, Dialog, KeyEventView}; use cursive::view::{IdView, TextView, Dialog, KeyEventView, Position, Offset};
fn show_popup(siv: &mut Cursive) { fn show_popup(siv: &mut Cursive) {
siv.add_layer(Dialog::new(TextView::new("Tak!")) // Let's center the popup horizontally, but offset it down a few rows
.button("Change", |s| { siv.screen_mut()
// Look for a view tagged "text". We _know_ it's there, so unwrap it. .add_layer_at(Position::new(Offset::Center, Offset::Parent(3)),
let view = s.find_id::<TextView>("text").unwrap(); Dialog::new(TextView::new("Tak!"))
let content: String = view.get_content().chars().rev().collect(); .button("Change", |s| {
view.set_content(&content); // Look for a view tagged "text". We _know_ it's there, so unwrap it.
}) let view = s.find_id::<TextView>("text").unwrap();
.dismiss_button("Ok")); let content: String = view.get_content().chars().rev().collect();
view.set_content(&content);
})
.dismiss_button("Ok"));
} }

View File

@ -2,6 +2,15 @@
#[macro_use]mod view_wrapper; #[macro_use]mod view_wrapper;
// Essentials components
mod position;
mod request;
mod view_path;
// Helper bases
mod scroll;
// Views
mod box_view; mod box_view;
mod button; mod button;
mod dialog; mod dialog;
@ -10,14 +19,11 @@ mod full_view;
mod id_view; mod id_view;
mod key_event_view; mod key_event_view;
mod linear_layout; mod linear_layout;
mod request;
mod shadow_view; mod shadow_view;
mod scroll;
mod select_view; mod select_view;
mod sized_view; mod sized_view;
mod stack_view; mod stack_view;
mod text_view; mod text_view;
mod view_path;
use std::any::Any; use std::any::Any;
@ -26,6 +32,8 @@ use event::{Event, EventResult};
use vec::Vec2; use vec::Vec2;
use printer::Printer; use printer::Printer;
pub use self::position::{Position, Offset};
pub use self::request::{DimensionRequest, SizeRequest}; pub use self::request::{DimensionRequest, SizeRequest};
pub use self::scroll::ScrollBase; pub use self::scroll::ScrollBase;

57
src/view/position.rs Normal file
View File

@ -0,0 +1,57 @@
use ::vec::{ToVec2, Vec2};
/// Location of the view on screen
pub struct Position {
pub x: Offset,
pub y: Offset,
}
impl Position {
pub fn new(x: Offset, y: Offset) -> Self {
Position {
x: x,
y: y,
}
}
pub fn center() -> Self {
Position::new(Offset::Center, Offset::Center)
}
pub fn absolute<T: ToVec2>(offset: T) -> Self {
let offset = offset.to_vec2();
Position::new(Offset::Absolute(offset.x), Offset::Absolute(offset.y))
}
pub fn parent<T: ToVec2>(offset: T) -> Self {
let offset = offset.to_vec2();
Position::new(Offset::Parent(offset.x), Offset::Parent(offset.y))
}
pub fn compute_offset(&self, size: Vec2, available: Vec2, parent: Vec2) -> Vec2 {
Vec2::new(self.x.compute_offset(size.x, available.x, parent.x),
self.y.compute_offset(size.y, available.y, parent.y))
}
}
pub enum Offset {
/// In the center of the screen
Center,
/// Place top-left corner at the given absolute coordinates
Absolute(usize),
/// Place top-left corner at the given offset from the previous layer's top-left corner.
///
/// If this is the first layer, behaves like `Absolute`.
Parent(usize), // TODO: use a signed vec for negative offset?
}
impl Offset {
pub fn compute_offset(&self, size: usize, available: usize, parent: usize) -> usize {
match *self {
Offset::Center => (available - size) / 2,
Offset::Absolute(offset) => offset,
Offset::Parent(offset) => parent + offset,
}
}
}

View File

@ -1,7 +1,7 @@
use std::any::Any; use std::any::Any;
use vec::Vec2; use vec::Vec2;
use view::{DimensionRequest, Selector, ShadowView, SizeRequest, View}; use view::{DimensionRequest, Selector, ShadowView, SizeRequest, View, Position};
use event::{Event, EventResult}; use event::{Event, EventResult};
use printer::Printer; use printer::Printer;
use theme::ColorStyle; use theme::ColorStyle;
@ -15,6 +15,7 @@ pub struct StackView {
struct Layer { struct Layer {
view: Box<View>, view: Box<View>,
size: Vec2, size: Vec2,
position: Position,
// Has it received the gift yet? // Has it received the gift yet?
virgin: bool, virgin: bool,
} }
@ -31,11 +32,17 @@ impl StackView {
StackView { layers: Vec::new() } StackView { layers: Vec::new() }
} }
/// Add new view on top of the stack. /// Adds new view on top of the stack in the center of the screen.
pub fn add_layer<T: 'static + View>(&mut self, view: T) { pub fn add_layer<T: 'static + View>(&mut self, view: T) {
self.add_layer_at(Position::center(), view);
}
/// Adds a view on top of the stack.
pub fn add_layer_at<T: 'static + View>(&mut self, position: Position, view: T) {
self.layers.push(Layer { self.layers.push(Layer {
view: Box::new(ShadowView::new(view)), view: Box::new(ShadowView::new(view)),
size: Vec2::new(0, 0), size: Vec2::new(0, 0),
position: position,
virgin: true, virgin: true,
}); });
} }
@ -49,13 +56,14 @@ impl StackView {
impl View for StackView { impl View for StackView {
fn draw(&mut self, printer: &Printer) { fn draw(&mut self, printer: &Printer) {
let last = self.layers.len(); let last = self.layers.len();
let mut previous = Vec2::zero();
printer.with_color(ColorStyle::Primary, |printer| { printer.with_color(ColorStyle::Primary, |printer| {
for (i, v) in self.layers.iter_mut().enumerate() { for (i, v) in self.layers.iter_mut().enumerate() {
// Place the view
// Center the view // Center the view
let size = v.size; let offset = v.position.compute_offset(v.size, printer.size, previous);
let offset = (printer.size - size) / 2; previous = offset;
// TODO: only draw focus for the top view v.view.draw(&printer.sub_printer(offset, v.size, i + 1 == last));
v.view.draw(&printer.sub_printer(offset, size, i + 1 == last));
} }
}); });
} }
@ -68,14 +76,20 @@ impl View for StackView {
} }
fn layout(&mut self, size: Vec2) { fn layout(&mut self, size: Vec2) {
// The call has been made, we can't ask for more space anymore.
// Let's make do with what we have.
let req = SizeRequest { let req = SizeRequest {
w: DimensionRequest::AtMost(size.x), w: DimensionRequest::AtMost(size.x),
h: DimensionRequest::AtMost(size.y), h: DimensionRequest::AtMost(size.y),
}; };
for layer in &mut self.layers { for layer in &mut self.layers {
// Give each guy what he asks for, within the budget constraints.
layer.size = Vec2::min(size, layer.view.get_min_size(req)); layer.size = Vec2::min(size, layer.view.get_min_size(req));
layer.view.layout(layer.size); layer.view.layout(layer.size);
// We do it here instead of when adding a new layer because...? // We do it here instead of when adding a new layer because...?
// (TODO: try to make it during layer addition)
if layer.virgin { if layer.virgin {
layer.view.take_focus(); layer.view.take_focus();
layer.virgin = false; layer.virgin = false;