Add focus system

May need to add a direction to take_focus
This commit is contained in:
Alexandre Bury 2015-05-19 15:54:11 -07:00
parent 7cb0a7b6d4
commit c8136c67e0
9 changed files with 76 additions and 65 deletions

View File

@ -171,7 +171,7 @@ impl Cursive {
offset: Vec2::new(0,0), offset: Vec2::new(0,0),
size: self.screen_size(), size: self.screen_size(),
}; };
self.screen_mut().draw(&printer); self.screen_mut().draw(&printer, true);
ncurses::wrefresh(ncurses::stdscr); ncurses::wrefresh(ncurses::stdscr);
} }

View File

@ -1,7 +1,5 @@
use event::EventResult;
use vec::{Vec2,ToVec2}; use vec::{Vec2,ToVec2};
use super::{View,SizeRequest}; use super::{View,ViewWrapper,SizeRequest};
use printer::Printer;
/// BoxView is a wrapper around an other view, with a given minimum size. /// BoxView is a wrapper around an other view, with a given minimum size.
pub struct BoxView { pub struct BoxView {
@ -27,20 +25,11 @@ impl BoxView {
} }
} }
impl View for BoxView { impl ViewWrapper for BoxView {
fn on_key_event(&mut self, ch: i32) -> EventResult {
self.content.on_key_event(ch)
}
fn draw(&self, printer: &Printer) { wrap_impl!(content);
self.content.draw(printer)
}
fn get_min_size(&self, _: SizeRequest) -> Vec2 { fn wrap_get_min_size(&self, _: SizeRequest) -> Vec2 {
self.size self.size
} }
fn layout(&mut self, size: Vec2) {
self.content.layout(size);
}
} }

View File

@ -1,7 +1,5 @@
use std::rc::Rc; use std::rc::Rc;
use ncurses;
use ::Cursive; use ::Cursive;
use vec::Vec2; use vec::Vec2;
use view::{View,ViewPath,SizeRequest}; use view::{View,ViewPath,SizeRequest};
@ -9,12 +7,14 @@ use event::{Callback,EventResult};
use printer::Printer; use printer::Printer;
/// Simple text label with a callback when ENTER is pressed. /// Simple text label with a callback when ENTER is pressed.
/// A button shows its content in a single line and has a fixed size.
pub struct Button { pub struct Button {
label: String, label: String,
callback: Rc<Callback>, callback: Rc<Callback>,
} }
impl Button { impl Button {
/// Creates a new button with the given content and callback.
pub fn new<F>(label: &str, cb: F) -> Self pub fn new<F>(label: &str, cb: F) -> Self
where F: Fn(&mut Cursive, &ViewPath) + 'static where F: Fn(&mut Cursive, &ViewPath) + 'static
{ {
@ -27,13 +27,17 @@ impl Button {
impl View for Button { impl View for Button {
fn draw(&self, printer: &Printer) { fn draw(&self, printer: &Printer, focused: bool) {
printer.print((1u32,0u32), &self.label); printer.print((1u32,0u32), &self.label);
printer.print((0u32,0u32), "<");
printer.print((printer.size.x-1,0), ">"); if focused {
printer.print((0u32,0u32), "<");
printer.print((printer.size.x-1,0), ">");
}
} }
fn get_min_size(&self, req: SizeRequest) -> Vec2 { fn get_min_size(&self, _: SizeRequest) -> Vec2 {
// Meh. Fixed size we are.
Vec2::new(2 + self.label.len() as u32, 1) Vec2::new(2 + self.label.len() as u32, 1)
} }
@ -44,4 +48,8 @@ impl View for Button {
_ => EventResult::Ignored, _ => EventResult::Ignored,
} }
} }
fn take_focus(&mut self) -> bool {
true
}
} }

View File

@ -13,7 +13,6 @@ use printer::Printer;
enum Focus { enum Focus {
Content, Content,
Button(usize), Button(usize),
Nothing,
} }
pub struct Dialog { pub struct Dialog {
@ -48,26 +47,16 @@ impl Dialog {
} }
fn offset_request(request: DimensionRequest, offset: i32) -> DimensionRequest {
match request {
DimensionRequest::Fixed(w) => DimensionRequest::Fixed((w as i32 + offset) as u32),
DimensionRequest::AtMost(w) => DimensionRequest::AtMost((w as i32 + offset) as u32),
DimensionRequest::Unknown => DimensionRequest::Unknown,
}
}
impl View for Dialog { impl View for Dialog {
fn draw(&self, printer: &Printer) { fn draw(&self, printer: &Printer, focused: bool) {
let mut height = 0; let mut height = 0;
let mut x = 0; let mut x = 0;
for (i,button) in self.buttons.iter().enumerate().rev() { for (i,button) in self.buttons.iter().enumerate().rev() {
let size = button.size; let size = button.size;
let offset = printer.size - self.borders.bot_right() - self.padding.bot_right() - size - Vec2::new(x, 0); let offset = printer.size - self.borders.bot_right() - self.padding.bot_right() - size - Vec2::new(x, 0);
if self.focus == Focus::Button(i) { // Add some special effect to the focused button
// Add some special effect to the focused button button.draw(&printer.sub_printer(offset, size), focused && (self.focus == Focus::Button(i)));
}
button.draw(&printer.sub_printer(offset, size));
x += size.x + 1; x += size.x + 1;
height = max(height, size.y+1); height = max(height, size.y+1);
} }
@ -77,7 +66,7 @@ impl View for Dialog {
- self.borders.combined() - self.borders.combined()
- self.padding.combined(); - self.padding.combined();
self.content.draw(&printer.sub_printer(self.borders.top_left() + self.padding.top_left(), inner_size)); self.content.draw(&printer.sub_printer(self.borders.top_left() + self.padding.top_left(), inner_size), focused && self.focus == Focus::Content);
printer.print(Vec2::new(0,0), "+"); printer.print(Vec2::new(0,0), "+");
printer.print(Vec2::new(printer.size.x-1, 0), "+"); printer.print(Vec2::new(printer.size.x-1, 0), "+");
@ -147,7 +136,16 @@ impl View for Dialog {
}, },
res => res, res => res,
}, },
Focus::Nothing => EventResult::Ignored, }
}
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()
} }
} }
} }

View File

@ -3,9 +3,7 @@ use std::rc::Rc;
use ::Cursive; use ::Cursive;
use event::{EventResult,Callback}; use event::{EventResult,Callback};
use vec::{Vec2}; use super::{View,ViewPath,ViewWrapper};
use super::{View,SizeRequest,ViewPath};
use printer::Printer;
/// A simple wrapper view that catches some ignored event from its child. /// A simple wrapper view that catches some ignored event from its child.
/// ///
@ -34,26 +32,18 @@ impl KeyEventView {
} }
} }
impl View for KeyEventView { impl ViewWrapper for KeyEventView {
fn on_key_event(&mut self, ch: i32) -> EventResult {
wrap_impl!(content);
fn wrap_on_key_event(&mut self, ch: i32) -> EventResult {
match self.content.on_key_event(ch) { match self.content.on_key_event(ch) {
EventResult::Ignored => match self.callbacks.get(&ch) { EventResult::Ignored => match self.callbacks.get(&ch) {
None => EventResult::Ignored, None => EventResult::Ignored,
Some(cb) => EventResult::Consumed(Some(cb.clone()), ViewPath::new()), Some(cb) => EventResult::Consumed(Some(cb.clone()), ViewPath::new()),
}, },
EventResult::Consumed(cb, path) => EventResult::Consumed(cb, path), res => res,
} }
} }
fn draw(&self, printer: &Printer) {
self.content.draw(printer)
}
fn get_min_size(&self, req: SizeRequest) -> Vec2 {
self.content.get_min_size(req)
}
fn layout(&mut self, size: Vec2) {
self.content.layout(size);
}
} }

View File

@ -1,5 +1,6 @@
//! Defines various views to use when creating the layout. //! Defines various views to use when creating the layout.
#[macro_use] mod view_wrapper;
mod box_view; mod box_view;
mod stack_view; mod stack_view;
mod text_view; mod text_view;
@ -8,7 +9,6 @@ mod view_path;
mod dialog; mod dialog;
mod button; mod button;
mod sized_view; mod sized_view;
mod view_wrapper;
use std::any::Any; use std::any::Any;
@ -85,11 +85,14 @@ pub trait View {
/// propagate the information to its children. /// propagate the information to its children.
fn layout(&mut self, Vec2) { } fn layout(&mut self, Vec2) { }
/// Draws the view within the given bounds. /// Draws the view with the given printer (includes bounds) and focus.
fn draw(&self, &Printer); fn draw(&self, printer: &Printer, focused: bool);
/// Finds the view pointed to by the given path. /// Finds the view pointed to by the given path.
/// Returns None if the path doesn't lead to a view. /// Returns None if the path doesn't lead to a view.
fn find(&mut self, &ViewPath) -> Option<&mut Any> { None } fn find(&mut self, &ViewPath) -> Option<&mut Any> { None }
/// This view is offered focus. Will it take it?
fn take_focus(&mut self) -> bool { false }
} }

View File

@ -40,14 +40,14 @@ impl StackView {
impl View for StackView { impl View for StackView {
fn draw(&self, printer: &Printer) { fn draw(&self, printer: &Printer, focused: bool) {
match self.layers.last() { match self.layers.last() {
None => (), None => (),
Some(v) => { Some(v) => {
// Center the view // Center the view
let view_size = Vec2::min(printer.size, v.size); let view_size = Vec2::min(printer.size, v.size);
let offset = (printer.size - view_size) / 2; let offset = (printer.size - view_size) / 2;
v.view.draw(&printer.sub_printer(offset, v.size)); v.view.draw(&printer.sub_printer(offset, v.size), focused);
}, },
} }
} }
@ -82,4 +82,11 @@ impl View for StackView {
s s
} }
fn take_focus(&mut self) -> bool {
match self.layers.last_mut() {
None => false,
Some(mut v) => v.view.take_focus()
}
}
} }

View File

@ -112,7 +112,9 @@ impl <'a> Iterator for LinesIterator<'a> {
} }
impl View for TextView { impl View for TextView {
fn draw(&self, printer: &Printer) { fn draw(&self, printer: &Printer, _: bool) {
// We don't have a focused view
let lines = self.content.split("\n") let lines = self.content.split("\n")
.flat_map(|line| LinesIterator::new(line, printer.size.x as usize)); .flat_map(|line| LinesIterator::new(line, printer.size.x as usize));
for (i, line) in lines.enumerate() { for (i, line) in lines.enumerate() {

View File

@ -7,8 +7,8 @@ pub trait ViewWrapper {
fn get_view(&self) -> &View; fn get_view(&self) -> &View;
fn get_view_mut(&mut self) -> &mut View; fn get_view_mut(&mut self) -> &mut View;
fn wrap_draw(&self, printer: &Printer) { fn wrap_draw(&self, printer: &Printer, focused: bool) {
self.get_view().draw(printer); self.get_view().draw(printer, focused);
} }
fn wrap_get_min_size(&self, req: SizeRequest) -> Vec2 { fn wrap_get_min_size(&self, req: SizeRequest) -> Vec2 {
@ -25,8 +25,8 @@ pub trait ViewWrapper {
} }
impl <T: ViewWrapper> View for T { impl <T: ViewWrapper> View for T {
fn draw(&self, printer: &Printer) { fn draw(&self, printer: &Printer, focused: bool) {
self.wrap_draw(printer); self.wrap_draw(printer, focused);
} }
fn get_min_size(&self, req: SizeRequest) -> Vec2 { fn get_min_size(&self, req: SizeRequest) -> Vec2 {
@ -41,3 +41,17 @@ impl <T: ViewWrapper> View for T {
self.wrap_layout(size); self.wrap_layout(size);
} }
} }
#[macro_export]
macro_rules! wrap_impl {
($v:ident) => {
fn get_view(&self) -> &View {
&*self.$v
}
fn get_view_mut(&mut self) -> &mut View {
&mut *self.$v
}
};
}