cursive/src/view/dialog.rs

169 lines
5.2 KiB
Rust
Raw Normal View History

2015-05-19 02:41:35 +00:00
use std::cmp::max;
use ncurses;
use ::{Cursive,Margins};
use event::EventResult;
2015-05-16 21:02:15 +00:00
use view::{View,ViewPath,SizeRequest,DimensionRequest};
2015-05-19 02:41:35 +00:00
use view::{Button,SizedView};
2015-05-18 22:31:55 +00:00
use vec::Vec2;
2015-05-16 21:02:15 +00:00
use printer::Printer;
#[derive(PartialEq)]
2015-05-16 21:02:15 +00:00
enum Focus {
Content,
Button(usize),
}
2015-05-20 00:31:52 +00:00
/// Popup-like view with a main content, and optional buttons under it.
///
/// # Examples
///
/// ```
/// let dialog = Dialog::new(TextView::new("Hello!")).button("Ok", |s,_| s.quit());
/// ```
2015-05-16 21:02:15 +00:00
pub struct Dialog {
content: Box<View>,
2015-05-19 02:41:35 +00:00
buttons: Vec<SizedView<Button>>,
padding: Margins,
borders: Margins,
2015-05-16 21:02:15 +00:00
focus: Focus,
}
impl Dialog {
2015-05-20 00:31:52 +00:00
/// Creates a new Dialog with the given content.
2015-05-16 21:02:15 +00:00
pub fn new<V: View + 'static>(view: V) -> Self {
Dialog {
content: Box::new(view),
buttons: Vec::new(),
focus: Focus::Content,
2015-05-19 02:41:35 +00:00
padding: Margins::new(1,1,0,0),
borders: Margins::new(1,1,1,1),
2015-05-16 21:02:15 +00:00
}
}
2015-05-20 00:31:52 +00:00
/// Adds a button to the dialog with the given label and callback.
///
/// Consumes and returns self for easy chaining.
2015-05-19 02:41:35 +00:00
pub fn button<'a, F>(mut self, label: &'a str, cb: F) -> Self
where F: Fn(&mut Cursive, &ViewPath) + 'static
2015-05-16 21:02:15 +00:00
{
2015-05-19 02:41:35 +00:00
self.buttons.push(SizedView::new(Button::new(label, cb)));
2015-05-16 21:02:15 +00:00
self
}
2015-05-19 02:41:35 +00:00
2015-05-16 21:02:15 +00:00
}
impl View for Dialog {
fn draw(&self, printer: &Printer, focused: bool) {
2015-05-19 02:41:35 +00:00
let mut height = 0;
let mut x = 0;
for (i,button) in self.buttons.iter().enumerate().rev() {
2015-05-19 02:41:35 +00:00
let size = button.size;
let offset = printer.size - self.borders.bot_right() - self.padding.bot_right() - size - Vec2::new(x, 0);
// Add some special effect to the focused button
button.draw(&printer.sub_printer(offset, size), focused && (self.focus == Focus::Button(i)));
2015-05-19 02:41:35 +00:00
x += size.x + 1;
height = max(height, size.y+1);
}
let inner_size = printer.size
- Vec2::new(0, height)
- self.borders.combined()
- self.padding.combined();
self.content.draw(&printer.sub_printer(self.borders.top_left() + self.padding.top_left(), inner_size), focused && self.focus == Focus::Content);
2015-05-19 02:41:35 +00:00
printer.print_box(Vec2::new(0,0), printer.size, '+', '-', '|');
2015-05-16 21:02:15 +00:00
}
2015-05-19 02:41:35 +00:00
fn get_min_size(&self, req: SizeRequest) -> Vec2 {
let content_req = req.reduced(self.padding.combined() + self.borders.combined());
2015-05-16 21:02:15 +00:00
let content_size = self.content.get_min_size(content_req);
2015-05-19 02:41:35 +00:00
let mut buttons_size = Vec2::new(0,0);
for button in self.buttons.iter() {
let s = button.view.get_min_size(req);
buttons_size.x += s.x + 1;
buttons_size.y = max(buttons_size.y, s.y + 1);
}
let inner_size = Vec2::new(
max(content_size.x, buttons_size.x),
content_size.y + buttons_size.y);
inner_size + self.padding.combined() + self.borders.combined()
}
fn layout(&mut self, mut size: Vec2) {
// First layout the buttons
size = size - (self.borders.combined() + self.padding.combined());
let req = SizeRequest {
w: DimensionRequest::AtMost(size.x),
h: DimensionRequest::AtMost(size.y),
};
let mut buttons_height = 0;
for button in self.buttons.iter_mut().rev() {
let size = button.get_min_size(req);
buttons_height = max(buttons_height, size.y+1);
button.layout(size);
}
self.content.layout(size - Vec2::new(0, buttons_height));
}
fn on_key_event(&mut self, ch: i32) -> EventResult {
match self.focus {
Focus::Content => match self.content.on_key_event(ch) {
EventResult::Ignored if !self.buttons.is_empty() => match ch {
ncurses::KEY_DOWN => {
self.focus = Focus::Button(0);
EventResult::Consumed(None, ViewPath::new())
},
_ => EventResult::Ignored,
},
res => res,
},
Focus::Button(i) => match self.buttons[i].on_key_event(ch) {
EventResult::Ignored => match ch {
ncurses::KEY_UP => {
if self.content.take_focus() {
self.focus = Focus::Content;
EventResult::consume()
} else {
EventResult::Ignored
}
},
ncurses::KEY_RIGHT if i+1 < self.buttons.len() => {
self.focus = Focus::Button(i+1);
EventResult::consume()
},
ncurses::KEY_LEFT if i > 0 => {
self.focus = Focus::Button(i-1);
EventResult::consume()
},
2015-05-19 02:41:35 +00:00
_ => EventResult::Ignored,
},
res => res,
},
}
}
fn take_focus(&mut self) -> bool {
if !self.buttons.is_empty() {
self.focus = Focus::Button(0);
true
} else {
self.focus = Focus::Content;
self.content.take_focus()
2015-05-19 02:41:35 +00:00
}
2015-05-16 21:02:15 +00:00
}
}