diff --git a/src/lib.rs b/src/lib.rs index 15f886c..ade5a2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,19 +10,30 @@ mod text_view; mod div; +use std::rc::Rc; +use std::collections::HashMap; + use view::View; use stack_view::StackView; -use event::EventResult; +use event::{EventResult,Callback}; + +pub type ScreenId = usize; /// Central part of the cursive library. /// It initializes ncurses on creation and cleans up on drop. /// To use it, you should populate it with views, layouts and callbacks, /// then start the event loop with run(). +/// +/// It uses a list of screen, with one screen active at a time. pub struct Cursive { - stacks: StackView, + screens: Vec, + + active_screen: ScreenId, running: bool, + + global_callbacks: HashMap>, } impl Cursive { @@ -32,10 +43,64 @@ impl Cursive { ncurses::keypad(ncurses::stdscr, true); ncurses::noecho(); - Cursive{ - stacks: StackView::new(), + let mut res = Cursive { + screens: Vec::new(), + active_screen: 0, 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(&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(&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. @@ -47,8 +112,9 @@ impl Cursive { // Handle event 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(Some(cb)) => cb(self), } diff --git a/src/main.rs b/src/main.rs index 00621e0..5a6e345 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,15 @@ extern crate cursive; use cursive::Cursive; +use cursive::view::TextView; fn main() { 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(); } diff --git a/src/stack_view.rs b/src/stack_view.rs index b0eaebb..86d4979 100644 --- a/src/stack_view.rs +++ b/src/stack_view.rs @@ -1,7 +1,10 @@ -use view::View; +use std::cmp::max; + +use ncurses; use super::Size; -use ncurses; +use view::{View,SizeRequest}; +use event::EventResult; /// Simple stack of views. /// 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) { - self.layers.push(view); + /// Add new view on top of the stack. + pub fn add_layer(&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), } } + + 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 + } } diff --git a/src/view.rs b/src/view.rs index 4dfb9c3..c361505 100644 --- a/src/view.rs +++ b/src/view.rs @@ -9,7 +9,7 @@ pub use text_view::TextView; use super::Size; /// Describe constraints on a view layout in one dimension. -#[derive(PartialEq)] +#[derive(PartialEq,Clone,Copy)] pub enum DimensionRequest { /// The view must use exactly the attached size. Fixed(u32), @@ -20,7 +20,7 @@ pub enum DimensionRequest { } /// Describes constraints on a view layout. -#[derive(PartialEq)] +#[derive(PartialEq,Clone,Copy)] pub struct SizeRequest { /// Restriction on the view width pub w: DimensionRequest,