Add screens to Cursive

This commit is contained in:
Alexandre Bury 2015-05-14 18:38:58 -07:00
parent 506c41c0f8
commit ec7b76c564
4 changed files with 112 additions and 12 deletions

View File

@ -10,19 +10,30 @@ mod text_view;
mod div; mod div;
use std::rc::Rc;
use std::collections::HashMap;
use view::View; use view::View;
use stack_view::StackView; use stack_view::StackView;
use event::EventResult; use event::{EventResult,Callback};
pub type ScreenId = usize;
/// Central part of the cursive library. /// Central part of the cursive library.
/// It initializes ncurses on creation and cleans up on drop. /// It initializes ncurses on creation and cleans up on drop.
/// To use it, you should populate it with views, layouts and callbacks, /// To use it, you should populate it with views, layouts and callbacks,
/// then start the event loop with run(). /// then start the event loop with run().
///
/// It uses a list of screen, with one screen active at a time.
pub struct Cursive { pub struct Cursive {
stacks: StackView, screens: Vec<StackView>,
active_screen: ScreenId,
running: bool, running: bool,
global_callbacks: HashMap<i32, Rc<Callback>>,
} }
impl Cursive { impl Cursive {
@ -32,10 +43,64 @@ impl Cursive {
ncurses::keypad(ncurses::stdscr, true); ncurses::keypad(ncurses::stdscr, true);
ncurses::noecho(); ncurses::noecho();
Cursive{ let mut res = Cursive {
stacks: StackView::new(), screens: Vec::new(),
active_screen: 0,
running: true, running: true,
global_callbacks: HashMap::new(),
};
res.screens.push(StackView::new());
res
}
/// Returns a mutable reference to the currently active screen.
pub fn screen_mut(&mut self) -> &mut StackView {
let id = self.active_screen;
self.screens.get_mut(id).unwrap()
}
/// Adds a new screen, and returns its ID.
pub fn add_screen(&mut self) -> ScreenId {
let res = self.screens.len();
self.screens.push(StackView::new());
res
}
/// Convenient method to create a new screen, and set it as active.
pub fn add_active_screen(&mut self) -> ScreenId {
let res = self.add_screen();
self.set_screen(res);
res
}
/// Sets the active screen. Panics if no such screen exist.
pub fn set_screen(&mut self, screen_id: ScreenId) {
if screen_id >= self.screens.len() {
panic!("Tried to set an invalid screen ID: {}, but only {} screens present.", screen_id, self.screens.len());
} }
self.active_screen = screen_id;
}
/// Adds a global callback, triggered on the given key press when no view catches it.
pub fn add_global_callback<F>(&mut self, key: i32, cb: F)
where F: Fn(&mut Cursive) + 'static
{
self.global_callbacks.insert(key, Rc::new(Box::new(cb)));
}
/// Convenient method to add a layer to the current screen.
pub fn add_layer<T: 'static + View>(&mut self, view: T) {
self.screen_mut().add_layer(view);
}
fn on_key_event(&mut self, ch: i32) {
let cb = match self.global_callbacks.get(&ch) {
None => return,
Some(cb) => cb.clone(),
};
cb(self);
} }
/// Runs the event loop. /// Runs the event loop.
@ -47,8 +112,9 @@ impl Cursive {
// Handle event // Handle event
let ch = ncurses::getch(); let ch = ncurses::getch();
match self.stacks.on_key_event(ch) {
EventResult::Ignored => (), match self.screen_mut().on_key_event(ch) {
EventResult::Ignored => self.on_key_event(ch),
EventResult::Consumed(None) => (), EventResult::Consumed(None) => (),
EventResult::Consumed(Some(cb)) => cb(self), EventResult::Consumed(Some(cb)) => cb(self),
} }

View File

@ -1,10 +1,15 @@
extern crate cursive; extern crate cursive;
use cursive::Cursive; use cursive::Cursive;
use cursive::view::TextView;
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
siv.add_layer(TextView::new("Hello World!"));
// We can quit by pressing q
siv.add_global_callback('q' as i32, |s| s.quit());
siv.run(); siv.run();
} }

View File

@ -1,7 +1,10 @@
use view::View; use std::cmp::max;
use ncurses;
use super::Size; use super::Size;
use ncurses; use view::{View,SizeRequest};
use event::EventResult;
/// Simple stack of views. /// Simple stack of views.
/// Only the top-most view is active and can receive input. /// Only the top-most view is active and can receive input.
@ -17,8 +20,14 @@ impl StackView {
} }
} }
pub fn add_layer(&mut self, view: Box<View>) { /// Add new view on top of the stack.
self.layers.push(view); pub fn add_layer<T: 'static + View>(&mut self, view: T) {
self.layers.push(Box::new(view));
}
/// Remove the top-most layer.
pub fn pop_layer(&mut self) {
self.layers.pop();
} }
} }
@ -30,4 +39,24 @@ impl View for StackView {
Some(v) => v.draw(win, size), Some(v) => v.draw(win, size),
} }
} }
fn on_key_event(&mut self, ch: i32) -> EventResult {
match self.layers.last_mut() {
None => EventResult::Ignored,
Some(v) => v.on_key_event(ch),
}
}
fn get_min_size(&self, size: SizeRequest) -> Size {
// The min size is the max of all children's
let mut s = Size::new(1,1);
for view in self.layers.iter() {
let vs = view.get_min_size(size);
s.w = max(s.w, vs.w);
s.h = max(s.h, vs.h);
}
s
}
} }

View File

@ -9,7 +9,7 @@ pub use text_view::TextView;
use super::Size; use super::Size;
/// Describe constraints on a view layout in one dimension. /// Describe constraints on a view layout in one dimension.
#[derive(PartialEq)] #[derive(PartialEq,Clone,Copy)]
pub enum DimensionRequest { pub enum DimensionRequest {
/// The view must use exactly the attached size. /// The view must use exactly the attached size.
Fixed(u32), Fixed(u32),
@ -20,7 +20,7 @@ pub enum DimensionRequest {
} }
/// Describes constraints on a view layout. /// Describes constraints on a view layout.
#[derive(PartialEq)] #[derive(PartialEq,Clone,Copy)]
pub struct SizeRequest { pub struct SizeRequest {
/// Restriction on the view width /// Restriction on the view width
pub w: DimensionRequest, pub w: DimensionRequest,