mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Add proper draw and layout to Dialog
This commit is contained in:
parent
68819c89bf
commit
04121414e1
10
src/event.rs
10
src/event.rs
@ -17,3 +17,13 @@ pub enum EventResult {
|
||||
/// The event was consumed. An optionnal callback to run is attached.
|
||||
Consumed(Option<Rc<Callback>>, ViewPath),
|
||||
}
|
||||
|
||||
impl EventResult {
|
||||
pub fn callback(cb: Rc<Callback>) -> Self {
|
||||
EventResult::Consumed(Some(cb), ViewPath::new())
|
||||
}
|
||||
|
||||
pub fn consume() -> Self {
|
||||
EventResult::Consumed(None, ViewPath::new())
|
||||
}
|
||||
}
|
||||
|
@ -34,4 +34,12 @@ impl Margins {
|
||||
pub fn combined(&self) -> Vec2 {
|
||||
Vec2::new(self.horizontal(), self.vertical())
|
||||
}
|
||||
|
||||
pub fn top_left(&self) -> Vec2 {
|
||||
Vec2::new(self.left, self.top)
|
||||
}
|
||||
|
||||
pub fn bot_right(&self) -> Vec2 {
|
||||
Vec2::new(self.right, self.bottom)
|
||||
}
|
||||
}
|
||||
|
29
src/vec.rs
29
src/vec.rs
@ -1,7 +1,7 @@
|
||||
//! 2D points.
|
||||
//! Points on the 2D character grid.
|
||||
|
||||
use std::ops::{Add, Sub, Mul, Div};
|
||||
use std::cmp::min;
|
||||
use std::cmp::{min,max};
|
||||
|
||||
/// Simple 2D size, in characters.
|
||||
#[derive(Clone,Copy)]
|
||||
@ -21,12 +21,27 @@ impl Vec2 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new Vec2 that is no larger than any input in both dimensions.
|
||||
pub fn min(a: Vec2, b: Vec2) -> Vec2 {
|
||||
Vec2 {
|
||||
x: min(a.x, b.x),
|
||||
y: min(a.y, b.y),
|
||||
/// Returns a new Vec2 that is a maximum per coordinate.
|
||||
pub fn max(a: Self, b: Self) -> Self {
|
||||
Vec2::new(max(a.x, b.x), max(a.y, b.y))
|
||||
}
|
||||
|
||||
/// Returns a new Vec2 that is no larger than any input in both dimensions.
|
||||
pub fn min(a: Self, b: Self) -> Self {
|
||||
Vec2::new(min(a.x, b.x), min(a.y, b.y))
|
||||
}
|
||||
|
||||
pub fn keep_x(&self) -> Self {
|
||||
Vec2::new(self.x, 0)
|
||||
}
|
||||
|
||||
pub fn keep_y(&self) -> Self {
|
||||
Vec2::new(0, self.y)
|
||||
}
|
||||
|
||||
/// Alias for Vec::new(0,0)
|
||||
pub fn zero() -> Self {
|
||||
Vec2::new(0,0)
|
||||
}
|
||||
}
|
||||
|
||||
|
44
src/view/button.rs
Normal file
44
src/view/button.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use ncurses;
|
||||
|
||||
use ::Cursive;
|
||||
use vec::Vec2;
|
||||
use view::{View,ViewPath,SizeRequest};
|
||||
use event::{Callback,EventResult};
|
||||
use printer::Printer;
|
||||
|
||||
/// Simple text label with a callback when ENTER is pressed.
|
||||
pub struct Button {
|
||||
label: String,
|
||||
callback: Rc<Callback>,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
pub fn new<F>(label: &str, cb: F) -> Self
|
||||
where F: Fn(&mut Cursive, &ViewPath) + 'static
|
||||
{
|
||||
Button {
|
||||
label: label.to_string(),
|
||||
callback: Rc::new(Box::new(cb)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl View for Button {
|
||||
|
||||
fn draw(&self, printer: &Printer) {
|
||||
printer.print(Vec2::zero(), &self.label);
|
||||
}
|
||||
|
||||
fn get_min_size(&self, req: SizeRequest) -> Vec2 {
|
||||
Vec2::new(self.label.len() as u32, 1)
|
||||
}
|
||||
|
||||
fn on_key_event(&mut self, ch: i32) -> EventResult {
|
||||
match ch {
|
||||
ncurses::KEY_ENTER => EventResult::callback(self.callback.clone()),
|
||||
_ => EventResult::Ignored,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,11 @@
|
||||
use ::Cursive;
|
||||
use std::cmp::max;
|
||||
|
||||
use ncurses;
|
||||
|
||||
use ::{Cursive,Margins};
|
||||
use event::EventResult;
|
||||
use view::{View,ViewPath,SizeRequest,DimensionRequest};
|
||||
use view::{Button,SizedView};
|
||||
use vec::Vec2;
|
||||
use printer::Printer;
|
||||
|
||||
@ -11,7 +17,12 @@ enum Focus {
|
||||
|
||||
pub struct Dialog {
|
||||
content: Box<View>,
|
||||
buttons: Vec<Box<View>>,
|
||||
|
||||
buttons: Vec<SizedView<Button>>,
|
||||
|
||||
padding: Margins,
|
||||
borders: Margins,
|
||||
|
||||
focus: Focus,
|
||||
}
|
||||
|
||||
@ -21,14 +32,19 @@ impl Dialog {
|
||||
content: Box::new(view),
|
||||
buttons: Vec::new(),
|
||||
focus: Focus::Nothing,
|
||||
padding: Margins::new(1,1,0,0),
|
||||
borders: Margins::new(1,1,1,1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn button<'a, F>(self, label: &'a str, cb: F) -> Self
|
||||
where F: Fn(&mut Cursive, &ViewPath)
|
||||
pub fn button<'a, F>(mut self, label: &'a str, cb: F) -> Self
|
||||
where F: Fn(&mut Cursive, &ViewPath) + 'static
|
||||
{
|
||||
self.buttons.push(SizedView::new(Button::new(label, cb)));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn offset_request(request: DimensionRequest, offset: i32) -> DimensionRequest {
|
||||
@ -42,15 +58,85 @@ fn offset_request(request: DimensionRequest, offset: i32) -> DimensionRequest {
|
||||
impl View for Dialog {
|
||||
fn draw(&self, printer: &Printer) {
|
||||
|
||||
let mut height = 0;
|
||||
let mut x = 0;
|
||||
for button in self.buttons.iter().rev() {
|
||||
// button.draw(&printer.sub_printer(),
|
||||
let size = button.size;
|
||||
let offset = printer.size - self.borders.bot_right() - self.padding.bot_right() - size - Vec2::new(x, 0);
|
||||
button.draw(&printer.sub_printer(offset, size));
|
||||
x += size.x + 1;
|
||||
height = max(height, size.y+1);
|
||||
}
|
||||
|
||||
fn get_min_size(&self, size: SizeRequest) -> Vec2 {
|
||||
let content_req = SizeRequest {
|
||||
w: offset_request(size.w, -2),
|
||||
h: offset_request(size.h, -2),
|
||||
};
|
||||
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));
|
||||
|
||||
printer.print(Vec2::new(0,0), "+");
|
||||
printer.print(Vec2::new(printer.size.x-1, 0), "+");
|
||||
printer.print(Vec2::new(0, printer.size.y-1), "+");
|
||||
printer.print(Vec2::new(printer.size.x-1, printer.size.y-1), "+");
|
||||
}
|
||||
|
||||
fn get_min_size(&self, req: SizeRequest) -> Vec2 {
|
||||
let content_req = req.reduced(self.padding.combined() + self.borders.combined());
|
||||
let content_size = self.content.get_min_size(content_req);
|
||||
|
||||
content_size + (2u32,2u32)
|
||||
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 {
|
||||
_ => EventResult::Ignored,
|
||||
},
|
||||
res => res,
|
||||
},
|
||||
Focus::Nothing => EventResult::Ignored,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,9 @@ mod text_view;
|
||||
mod key_event_view;
|
||||
mod view_path;
|
||||
mod dialog;
|
||||
mod button;
|
||||
mod sized_view;
|
||||
mod view_wrapper;
|
||||
|
||||
use std::any::Any;
|
||||
|
||||
@ -15,9 +18,12 @@ pub use self::box_view::BoxView;
|
||||
pub use self::stack_view::StackView;
|
||||
pub use self::text_view::TextView;
|
||||
pub use self::dialog::Dialog;
|
||||
pub use self::button::Button;
|
||||
pub use self::sized_view::SizedView;
|
||||
pub use self::view_wrapper::ViewWrapper;
|
||||
|
||||
use event::EventResult;
|
||||
use vec::Vec2;
|
||||
use vec::{Vec2,ToVec2};
|
||||
use printer::Printer;
|
||||
|
||||
/// Describe constraints on a view layout in one dimension.
|
||||
@ -31,6 +37,16 @@ pub enum DimensionRequest {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl DimensionRequest {
|
||||
pub fn reduced(self, offset: u32) -> Self {
|
||||
match self {
|
||||
DimensionRequest::Fixed(w) => DimensionRequest::Fixed(w - offset),
|
||||
DimensionRequest::AtMost(w) => DimensionRequest::AtMost(w - offset),
|
||||
DimensionRequest::Unknown => DimensionRequest::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes constraints on a view layout.
|
||||
#[derive(PartialEq,Clone,Copy)]
|
||||
pub struct SizeRequest {
|
||||
@ -40,6 +56,23 @@ pub struct SizeRequest {
|
||||
pub h: DimensionRequest,
|
||||
}
|
||||
|
||||
impl SizeRequest {
|
||||
pub fn reduced<T: ToVec2>(self, offset: T) -> Self {
|
||||
let ov = offset.to_vec2();
|
||||
SizeRequest {
|
||||
w: self.w.reduced(ov.x),
|
||||
h: self.h.reduced(ov.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dummy() -> Self {
|
||||
SizeRequest {
|
||||
w: DimensionRequest::Unknown,
|
||||
h: DimensionRequest::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Main trait defining a view behaviour.
|
||||
pub trait View {
|
||||
/// Called when a key was pressed. Default implementation just ignores it.
|
||||
|
34
src/view/sized_view.rs
Normal file
34
src/view/sized_view.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use view::View;
|
||||
use vec::Vec2;
|
||||
use view::ViewWrapper;
|
||||
|
||||
/// Wrapper around a view that remembers its size.
|
||||
pub struct SizedView <T: View> {
|
||||
pub view: T,
|
||||
pub size: Vec2,
|
||||
}
|
||||
|
||||
impl<T: View> SizedView<T> {
|
||||
/// Wraps the given view.
|
||||
pub fn new(view: T) -> Self {
|
||||
SizedView {
|
||||
view: view,
|
||||
size: Vec2::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: View> ViewWrapper for SizedView<T> {
|
||||
fn get_view(&self) -> &View {
|
||||
&self.view
|
||||
}
|
||||
|
||||
fn get_view_mut(&mut self) -> &mut View {
|
||||
&mut self.view
|
||||
}
|
||||
|
||||
fn wrap_layout(&mut self, size: Vec2) {
|
||||
self.view.layout(size);
|
||||
self.size = size;
|
||||
}
|
||||
}
|
43
src/view/view_wrapper.rs
Normal file
43
src/view/view_wrapper.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use vec::Vec2;
|
||||
use view::{View,SizeRequest};
|
||||
use printer::Printer;
|
||||
use event::EventResult;
|
||||
|
||||
pub trait ViewWrapper {
|
||||
fn get_view(&self) -> &View;
|
||||
fn get_view_mut(&mut self) -> &mut View;
|
||||
|
||||
fn wrap_draw(&self, printer: &Printer) {
|
||||
self.get_view().draw(printer);
|
||||
}
|
||||
|
||||
fn wrap_get_min_size(&self, req: SizeRequest) -> Vec2 {
|
||||
self.get_view().get_min_size(req)
|
||||
}
|
||||
|
||||
fn wrap_on_key_event(&mut self, ch: i32) -> EventResult {
|
||||
self.get_view_mut().on_key_event(ch)
|
||||
}
|
||||
|
||||
fn wrap_layout(&mut self, size: Vec2) {
|
||||
self.get_view_mut().layout(size);
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: ViewWrapper> View for T {
|
||||
fn draw(&self, printer: &Printer) {
|
||||
self.wrap_draw(printer);
|
||||
}
|
||||
|
||||
fn get_min_size(&self, req: SizeRequest) -> Vec2 {
|
||||
self.wrap_get_min_size(req)
|
||||
}
|
||||
|
||||
fn on_key_event(&mut self, ch: i32) -> EventResult {
|
||||
self.wrap_on_key_event(ch)
|
||||
}
|
||||
|
||||
fn layout(&mut self, size: Vec2) {
|
||||
self.wrap_layout(size);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user