This commit is contained in:
Alexandre Bury 2017-10-12 16:38:55 -07:00
parent 74612c65e5
commit a4ca7bbf1e
59 changed files with 1323 additions and 926 deletions

View File

@ -1,8 +1,10 @@
fn main() {
extern crate skeptic;
skeptic::generate_doc_tests(&["Readme.md",
"doc/tutorial_1.md",
"doc/tutorial_2.md",
"doc/tutorial_3.md" ]);
skeptic::generate_doc_tests(&[
"Readme.md",
"doc/tutorial_1.md",
"doc/tutorial_2.md",
"doc/tutorial_3.md",
]);
}

View File

@ -1,7 +1,7 @@
extern crate cursive;
use cursive::{Cursive, Printer};
use cursive::theme::{ColorStyle, Color};
use cursive::theme::{Color, ColorStyle};
use cursive::view::Boxable;
use cursive::views::Canvas;
@ -16,16 +16,19 @@ fn main() {
}
fn front_color(x: u8, y: u8, x_max: u8, y_max: u8) -> Color {
Color::Rgb(x * (255 / x_max),
y * (255 / y_max),
(x + 2 * y) * (255 / (x_max + 2 * y_max)))
Color::Rgb(
x * (255 / x_max),
y * (255 / y_max),
(x + 2 * y) * (255 / (x_max + 2 * y_max)),
)
}
fn back_color(x: u8, y: u8, x_max: u8, y_max: u8) -> Color {
Color::Rgb(128 + (2 * y_max + x - 2 * y) * (128 / (x_max + 2 * y_max)),
255 - y * (255 / y_max),
255 - x * (255 / x_max))
Color::Rgb(
128 + (2 * y_max + x - 2 * y) * (128 / (x_max + 2 * y_max)),
255 - y * (255 / y_max),
255 - x * (255 / x_max),
)
}
fn draw(_: &(), p: &Printer) {
@ -39,7 +42,9 @@ fn draw(_: &(), p: &Printer) {
back: back_color(x, y, x_max, y_max),
};
p.with_color(style, |printer| { printer.print((x, y), "+"); });
p.with_color(style, |printer| {
printer.print((x, y), "+");
});
}
}
}

View File

@ -8,9 +8,11 @@ fn main() {
let mut siv = Cursive::new();
// Creates a dialog with a single "Quit" button
siv.add_layer(Dialog::around(TextView::new("Hello Dialog!"))
.title("Cursive")
.button("Quit", |s| s.quit()));
siv.add_layer(
Dialog::around(TextView::new("Hello Dialog!"))
.title("Cursive")
.button("Quit", |s| s.quit()),
);
// Starts the event loop.
siv.run();

View File

@ -10,19 +10,24 @@ fn main() {
// Create a dialog with an edit text and a button.
// The user can either hit the <Ok> button,
// or press Enter on the edit text.
siv.add_layer(Dialog::new()
.title("Enter your name")
.padding((1, 1, 1, 0))
.content(EditView::new()
.on_submit(show_popup)
.with_id("name")
.fixed_width(20))
.button("Ok", |s| {
let name =
s.call_on_id("name", |view: &mut EditView| view.get_content())
.unwrap();
show_popup(s, &name);
}));
siv.add_layer(
Dialog::new()
.title("Enter your name")
.padding((1, 1, 1, 0))
.content(
EditView::new()
.on_submit(show_popup)
.with_id("name")
.fixed_width(20),
)
.button("Ok", |s| {
let name = s.call_on_id(
"name",
|view: &mut EditView| view.get_content(),
).unwrap();
show_popup(s, &name);
}),
);
siv.run();
}
@ -33,7 +38,9 @@ fn show_popup(s: &mut Cursive, name: &str) {
} else {
let content = format!("Hello {}!", name);
s.pop_layer();
s.add_layer(Dialog::around(TextView::new(content))
.button("Quit", |s| s.quit()));
s.add_layer(
Dialog::around(TextView::new(content))
.button("Quit", |s| s.quit()),
);
}
}

View File

@ -9,8 +9,10 @@ fn main() {
// We can quit by pressing `q`
siv.add_global_callback('q', Cursive::quit);
siv.add_layer(TextView::new("Hello World!\n\
Press q to quit the application."));
siv.add_layer(TextView::new(
"Hello World!\n\
Press q to quit the application.",
));
siv.run();
}

View File

@ -1,8 +1,8 @@
extern crate cursive;
use cursive::{Cursive, Printer};
use cursive::traits::*;
use cursive::event::{Event, EventResult};
use cursive::traits::*;
fn main() {
let mut siv = Cursive::new();

View File

@ -1,9 +1,9 @@
extern crate cursive;
use cursive::Cursive;
use cursive::views::{Dialog, LinearLayout, TextView};
use cursive::align::HAlign;
use cursive::traits::*;
use cursive::views::{Dialog, LinearLayout, TextView};
fn main() {
let mut siv = Cursive::new();
@ -14,16 +14,19 @@ fn main() {
has a fixed width, and the title is centered horizontally.";
// We'll create a dialog with a TextView serving as a title
siv.add_layer(Dialog::around(LinearLayout::vertical()
siv.add_layer(
Dialog::around(
LinearLayout::vertical()
.child(TextView::new("Title").h_align(HAlign::Center))
// Box the textview, so it doesn't get too wide.
// A 0 height value means it will be unconstrained.
.child(TextView::new(text).scrollable(false).fixed_width(30))
.child(TextView::new(text).fixed_width(30))
.child(TextView::new(text).fixed_width(30))
.child(TextView::new(text).fixed_width(30)))
.button("Quit", |s| s.quit())
.h_align(HAlign::Center));
.child(TextView::new(text).fixed_width(30)),
).button("Quit", |s| s.quit())
.h_align(HAlign::Center),
);
siv.run();
}

View File

@ -8,44 +8,61 @@ use cursive::views::{Checkbox, Dialog, EditView, LinearLayout, ListView,
fn main() {
let mut siv = Cursive::new();
siv.add_layer(Dialog::new()
.title("Please fill out this form")
.button("Ok", |s| s.quit())
.content(ListView::new()
.child("Name", EditView::new().fixed_width(10))
.child("Receive spam?",
Checkbox::new()
.on_change(|s, checked| for name in &["email1",
"email2"] {
s.call_on_id(name, |view: &mut EditView| {
view.set_enabled(checked)
});
if checked {
s.focus_id("email1").unwrap();
}
}))
.child("Email",
LinearLayout::horizontal()
.child(EditView::new()
.disabled()
.with_id("email1")
.fixed_width(15))
.child(TextView::new("@"))
.child(EditView::new()
.disabled()
.with_id("email2")
.fixed_width(10)))
.delimiter()
.child("Age",
SelectView::new()
.popup()
.item_str("0-18")
.item_str("19-30")
.item_str("31-40")
.item_str("41+"))
.with(|list| for i in 0..50 {
list.add_child(&format!("Item {}", i), EditView::new());
})));
siv.add_layer(
Dialog::new()
.title("Please fill out this form")
.button("Ok", |s| s.quit())
.content(
ListView::new()
.child("Name", EditView::new().fixed_width(10))
.child(
"Receive spam?",
Checkbox::new().on_change(
|s, checked| for name in &["email1", "email2"] {
s.call_on_id(name, |view: &mut EditView| {
view.set_enabled(checked)
});
if checked {
s.focus_id("email1").unwrap();
}
},
),
)
.child(
"Email",
LinearLayout::horizontal()
.child(
EditView::new()
.disabled()
.with_id("email1")
.fixed_width(15),
)
.child(TextView::new("@"))
.child(
EditView::new()
.disabled()
.with_id("email2")
.fixed_width(10),
),
)
.delimiter()
.child(
"Age",
SelectView::new()
.popup()
.item_str("0-18")
.item_str("19-30")
.item_str("31-40")
.item_str("41+"),
)
.with(|list| for i in 0..50 {
list.add_child(
&format!("Item {}", i),
EditView::new(),
);
}),
),
);
siv.run();
}

View File

@ -4,7 +4,6 @@ use cursive::{Cursive, Printer};
use cursive::traits::*;
use cursive::vec::Vec2;
use std::collections::VecDeque;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
@ -21,7 +20,9 @@ fn main() {
let (tx, rx) = mpsc::channel();
// Generate data in a separate thread.
thread::spawn(move || { generate_logs(&tx); });
thread::spawn(move || {
generate_logs(&tx);
});
// And sets the view to read from the other end of the channel.
siv.add_layer(BufferView::new(200, rx).full_screen());
@ -82,11 +83,9 @@ impl View for BufferView {
fn draw(&self, printer: &Printer) {
// Print the end of the buffer
for (i, line) in self.buffer
.iter()
.rev()
.take(printer.size.y)
.enumerate() {
for (i, line) in
self.buffer.iter().rev().take(printer.size.y).enumerate()
{
printer.print((0, printer.size.y - 1 - i), line);
}
}

View File

@ -1,9 +1,9 @@
extern crate cursive;
use cursive::Cursive;
use cursive::align::HAlign;
use cursive::view::Boxable;
use cursive::views::{Dialog, TextView};
use cursive::align::HAlign;
fn main() {
// Read some long text from a file.
@ -16,13 +16,17 @@ fn main() {
// The text is too long to fit on a line, so the view will wrap lines,
// and will adapt to the terminal size.
siv.add_fullscreen_layer(Dialog::around(TextView::new(content))
.h_align(HAlign::Center)
.button("Quit", |s| s.quit())
.full_screen());
siv.add_fullscreen_layer(
Dialog::around(TextView::new(content))
.h_align(HAlign::Center)
.button("Quit", |s| s.quit())
.full_screen(),
);
// Show a popup on top of the view.
siv.add_layer(Dialog::info("Try resizing the terminal!\n(Press 'q' to \
quit when you're done.)"));
siv.add_layer(Dialog::info(
"Try resizing the terminal!\n(Press 'q' to \
quit when you're done.)",
));
siv.run();
}

View File

@ -5,11 +5,9 @@ use cursive::event::Key;
use cursive::menu::MenuTree;
use cursive::traits::*;
use cursive::views::Dialog;
use std::sync::atomic::{Ordering, AtomicUsize};
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let mut siv = Cursive::new();
// We'll use a counter to name new files.

View File

@ -1,12 +1,11 @@
extern crate cursive;
use cursive::Cursive;
use cursive::views::{Dialog, OnEventView, TextView};
use cursive::view::{Offset, Position};
use cursive::traits::*;
use cursive::view::{Offset, Position};
use cursive::views::{Dialog, OnEventView, TextView};
fn show_popup(siv: &mut Cursive) {
// Let's center the popup horizontally, but offset it down a few rows
siv.screen_mut()
.add_layer_at(Position::new(Offset::Center, Offset::Parent(3)),
@ -19,7 +18,6 @@ fn show_popup(siv: &mut Cursive) {
});
})
.dismiss_button("Ok"));
}
fn main() {
@ -33,8 +31,10 @@ fn main() {
// Let's wrap the view to give it a recognizable ID, so we can look for it.
// We add the P callback on the textview only (and not globally),
// so that we can't call it when the popup is already visible.
siv.add_layer(OnEventView::new(TextView::new(content).with_id("text"))
.on_event('p', |s| show_popup(s)));
siv.add_layer(
OnEventView::new(TextView::new(content).with_id("text"))
.on_event('p', |s| show_popup(s)),
);
siv.run();

View File

@ -1,25 +1,25 @@
extern crate cursive;
extern crate rand;
use rand::Rng;
use cursive::Cursive;
use cursive::traits::*;
use cursive::views::{Button, Dialog, LinearLayout, ProgressBar, TextView};
use cursive::views::Counter;
use cursive::traits::*;
use std::thread;
use rand::Rng;
use std::cmp::min;
use std::thread;
use std::time::Duration;
fn main() {
let mut siv = Cursive::new();
// We'll start slowly with a simple start button...
siv.add_layer(Dialog::new()
.title("Progress bar example")
.padding((0, 0, 1, 1))
.content(Button::new("Start", phase_1)));
siv.add_layer(
Dialog::new()
.title("Progress bar example")
.padding((0, 0, 1, 1))
.content(Button::new("Start", phase_1)),
);
// Auto-refresh is currently required for animated views
siv.set_fps(30);
@ -46,25 +46,29 @@ fn phase_1(s: &mut Cursive) {
let cb = s.cb_sink().clone();
s.pop_layer();
s.add_layer(Dialog::around(ProgressBar::new()
.range(0, n_max)
.with_task(move |counter| {
// This closure will be called in a separate thread.
fake_load(n_max, &counter);
s.add_layer(Dialog::around(
ProgressBar::new()
.range(0, n_max)
.with_task(move |counter| {
// This closure will be called in a separate thread.
fake_load(n_max, &counter);
// When we're done, send a callback through the channel
cb.send(Box::new(coffee_break)).unwrap();
})
.full_width()));
// When we're done, send a callback through the channel
cb.send(Box::new(coffee_break)).unwrap();
})
.full_width(),
));
}
fn coffee_break(s: &mut Cursive) {
// A little break before things get serious.
s.pop_layer();
s.add_layer(Dialog::new()
.title("Preparation complete")
.content(TextView::new("Now, the real deal!").center())
.button("Again??", phase_2));
s.add_layer(
Dialog::new()
.title("Preparation complete")
.content(TextView::new("Now, the real deal!").center())
.button("Again??", phase_2),
);
}
fn phase_2(s: &mut Cursive) {
@ -75,8 +79,9 @@ fn phase_2(s: &mut Cursive) {
// Each task will have its own shiny counter
let counters: Vec<_> = (0..n_bars).map(|_| Counter::new(0)).collect();
// To make things more interesting, we'll give a random speed to each bar
let speeds: Vec<_> =
(0..n_bars).map(|_| rand::thread_rng().gen_range(50, 150)).collect();
let speeds: Vec<_> = (0..n_bars)
.map(|_| rand::thread_rng().gen_range(50, 150))
.collect();
let n_max = 100000;
let cb = s.cb_sink().clone();
@ -84,9 +89,7 @@ fn phase_2(s: &mut Cursive) {
// Let's prepare the progress bars...
let mut linear = LinearLayout::vertical();
for c in &counters {
linear.add_child(ProgressBar::new()
.max(n_max)
.with_value(c.clone()));
linear.add_child(ProgressBar::new().max(n_max).with_value(c.clone()));
}
s.pop_layer();
@ -116,10 +119,15 @@ fn phase_2(s: &mut Cursive) {
fn final_step(s: &mut Cursive) {
// A little break before things get serious.
s.pop_layer();
s.add_layer(Dialog::new()
.title("Report")
.content(TextView::new("Time travel was a success!\n\
We went forward a few seconds!!")
.center())
.button("That's it?", |s| s.quit()));
s.add_layer(
Dialog::new()
.title("Report")
.content(
TextView::new(
"Time travel was a success!\n\
We went forward a few seconds!!",
).center(),
)
.button("That's it?", |s| s.quit()),
);
}

View File

@ -10,7 +10,8 @@ fn main() {
let mut color_group: RadioGroup<String> = RadioGroup::new();
let mut size_group: RadioGroup<u32> = RadioGroup::new();
siv.add_layer(Dialog::new()
siv.add_layer(
Dialog::new()
.title("Make your selection")
// We'll have two columns side-by-side
.content(LinearLayout::horizontal()
@ -37,7 +38,8 @@ fn main() {
color,
size))
.button("Ok", |s| s.quit()));
}));
}),
);
siv.run();
}

View File

@ -2,19 +2,22 @@ extern crate cursive;
use cursive::Cursive;
use cursive::view::{Boxable, Identifiable};
use cursive::views::{LinearLayout, EditView, TextView, Dialog};
use cursive::views::{Dialog, EditView, LinearLayout, TextView};
fn main() {
let mut siv = Cursive::new();
// Create a dialog with 2 edit fields, and a text view.
// The text view indicates when the 2 fields content match.
siv.add_layer(Dialog::around(LinearLayout::vertical()
.child(EditView::new().on_edit(on_edit).with_id("1"))
.child(EditView::new().on_edit(on_edit).with_id("2"))
.child(TextView::new("match").with_id("match"))
.fixed_width(10))
.button("Quit", Cursive::quit));
siv.add_layer(
Dialog::around(
LinearLayout::vertical()
.child(EditView::new().on_edit(on_edit).with_id("1"))
.child(EditView::new().on_edit(on_edit).with_id("2"))
.child(TextView::new("match").with_id("match"))
.fixed_width(10),
).button("Quit", Cursive::quit),
);
siv.run();
}
@ -31,9 +34,7 @@ fn on_edit(siv: &mut Cursive, _content: &str, _cursor: usize) {
// Directly compare references to edit_1 and edit_2.
let matches = edit_1.get_content() == edit_2.get_content();
siv.call_on_id("match", |v: &mut TextView| v.set_content(if matches {
"match"
} else {
"no match"
}));
siv.call_on_id("match", |v: &mut TextView| {
v.set_content(if matches { "match" } else { "no match" })
});
}

View File

@ -10,19 +10,27 @@ fn main() {
// Let's add a simple slider in a dialog.
// Moving the slider will update the dialog's title.
// And pressing "Enter" will show a new dialog.
siv.add_layer(Dialog::around(SliderView::horizontal(15)
.value(7)
.on_change(|s, v| {
let title = format!("[ {} ]", v);
s.call_on_id("dialog", |view: &mut Dialog| view.set_title(title));
})
.on_enter(|s, v| {
s.pop_layer();
s.add_layer(Dialog::text(format!("Lucky number {}!", v))
.button("Ok", Cursive::quit));
}))
.title("[ 7 ]")
.with_id("dialog"));
siv.add_layer(
Dialog::around(
SliderView::horizontal(15)
.value(7)
.on_change(|s, v| {
let title = format!("[ {} ]", v);
s.call_on_id(
"dialog",
|view: &mut Dialog| view.set_title(title),
);
})
.on_enter(|s, v| {
s.pop_layer();
s.add_layer(
Dialog::text(format!("Lucky number {}!", v))
.button("Ok", Cursive::quit),
);
}),
).title("[ 7 ]")
.with_id("dialog"),
);
siv.run();
}

View File

@ -20,8 +20,10 @@ fn main() {
siv.add_global_callback('q', Cursive::quit);
siv.set_theme(theme);
siv.add_layer(TextView::new("Hello World with default terminal background color!\n\
Press q to quit the application."));
siv.add_layer(TextView::new(
"Hello World with default terminal background color!\n\
Press q to quit the application.",
));
siv.run();
}

View File

@ -1,18 +1,19 @@
extern crate cursive;
use cursive::Cursive;
use cursive::views::{Dialog, TextArea};
use cursive::traits::*;
use cursive::views::{Dialog, TextArea};
fn main() {
let mut siv = Cursive::new();
siv.add_layer(Dialog::new()
.title("Describe your issue")
.padding((1, 1, 1, 0))
.content(TextArea::new()
.with_id("text"))
.button("Ok", Cursive::quit));
siv.add_layer(
Dialog::new()
.title("Describe your issue")
.padding((1, 1, 1, 0))
.content(TextArea::new().with_id("text"))
.button("Ok", Cursive::quit),
);
siv.run();
}

View File

@ -11,10 +11,13 @@ fn main() {
// Or you can directly load it from a string for easy deployment.
// siv.load_theme(include_str!("../assets/style.toml")).unwrap();
siv.add_layer(Dialog::around(TextView::new("This application uses a \
custom theme!"))
.title("Themed dialog")
.button("Quit", |s| s.quit()));
siv.add_layer(
Dialog::around(TextView::new(
"This application uses a \
custom theme!",
)).title("Themed dialog")
.button("Quit", |s| s.quit()),
);
siv.run();
}

View File

@ -1,33 +1,37 @@
extern crate cursive;
use cursive::Cursive;
use cursive::theme::{ColorStyle, BaseColor, Color, BorderStyle};
use cursive::views::{EditView, LinearLayout, Dialog, TextView};
use cursive::theme::{BaseColor, BorderStyle, Color, ColorStyle};
use cursive::views::{Dialog, EditView, LinearLayout, TextView};
fn main() {
let mut siv = Cursive::new();
let layout = LinearLayout::vertical()
.child(TextView::new("This is a dynamic theme example!"))
.child(EditView::new().content("Woo! colors!").style(ColorStyle::Custom {
front: Color::Rgb(200, 150, 150),
back: Color::Dark(BaseColor::Blue),
}));
.child(EditView::new().content("Woo! colors!").style(
ColorStyle::Custom {
front: Color::Rgb(200, 150, 150),
back: Color::Dark(BaseColor::Blue),
},
));
siv.add_layer(Dialog::around(layout)
.button("Change", |s| {
let mut theme = s.current_theme().clone();
siv.add_layer(
Dialog::around(layout)
.button("Change", |s| {
let mut theme = s.current_theme().clone();
theme.shadow = !theme.shadow;
theme.borders = match theme.borders {
BorderStyle::Simple => BorderStyle::Outset,
BorderStyle::Outset => BorderStyle::None,
BorderStyle::None => BorderStyle::Simple,
};
theme.shadow = !theme.shadow;
theme.borders = match theme.borders {
BorderStyle::Simple => BorderStyle::Outset,
BorderStyle::Outset => BorderStyle::None,
BorderStyle::None => BorderStyle::Simple,
};
s.set_theme(theme);
})
.button("Quit", Cursive::quit));
s.set_theme(theme);
})
.button("Quit", Cursive::quit),
);
siv.run();
}

View File

@ -41,13 +41,11 @@ impl backend::Backend for Concrete {
// BLT itself doesn't do this kind of thing,
// we'd need the colours in our position,
// but `f()` can do whatever
Effect::Reverse => {
terminal::with_colors(
BltColor::from_rgb(0, 0, 0),
BltColor::from_rgb(255, 255, 255),
f,
)
}
Effect::Reverse => terminal::with_colors(
BltColor::from_rgb(0, 0, 0),
BltColor::from_rgb(255, 255, 255),
f,
),
}
}
@ -138,111 +136,363 @@ fn colour_to_blt_colour(clr: Color, role: ColorRole) -> BltColor {
Color::Light(BaseColor::White) => (255, 255, 255),
Color::Rgb(r, g, b) => (r, g, b),
Color::RgbLowRes(r, g, b) => {
(
(r as f32 / 5.0 * 255.0) as u8,
(g as f32 / 5.0 * 255.0) as u8,
(b as f32 / 5.0 * 255.0) as u8,
)
}
Color::RgbLowRes(r, g, b) => (
(r as f32 / 5.0 * 255.0) as u8,
(g as f32 / 5.0 * 255.0) as u8,
(b as f32 / 5.0 * 255.0) as u8,
),
};
BltColor::from_rgb(r, g, b)
}
fn blt_keycode_to_ev(kc: KeyCode, shift: bool, ctrl: bool) -> Event {
match kc {
KeyCode::F1 | KeyCode::F2 | KeyCode::F3 | KeyCode::F4 |
KeyCode::F5 | KeyCode::F6 | KeyCode::F7 | KeyCode::F8 |
KeyCode::F9 | KeyCode::F10 | KeyCode::F11 | KeyCode::F12 |
KeyCode::NumEnter | KeyCode::Enter | KeyCode::Escape |
KeyCode::Backspace | KeyCode::Tab | KeyCode::Pause |
KeyCode::Insert | KeyCode::Home | KeyCode::PageUp |
KeyCode::Delete | KeyCode::End | KeyCode::PageDown |
KeyCode::Right | KeyCode::Left | KeyCode::Down | KeyCode::Up => {
match (shift, ctrl) {
(true, true) => Event::CtrlShift(blt_keycode_to_key(kc)),
(true, false) => Event::Shift(blt_keycode_to_key(kc)),
(false, true) => Event::Ctrl(blt_keycode_to_key(kc)),
(false, false) => Event::Key(blt_keycode_to_key(kc)),
}
}
KeyCode::F1 |
KeyCode::F2 |
KeyCode::F3 |
KeyCode::F4 |
KeyCode::F5 |
KeyCode::F6 |
KeyCode::F7 |
KeyCode::F8 |
KeyCode::F9 |
KeyCode::F10 |
KeyCode::F11 |
KeyCode::F12 |
KeyCode::NumEnter |
KeyCode::Enter |
KeyCode::Escape |
KeyCode::Backspace |
KeyCode::Tab |
KeyCode::Pause |
KeyCode::Insert |
KeyCode::Home |
KeyCode::PageUp |
KeyCode::Delete |
KeyCode::End |
KeyCode::PageDown |
KeyCode::Right |
KeyCode::Left |
KeyCode::Down |
KeyCode::Up => match (shift, ctrl) {
(true, true) => Event::CtrlShift(blt_keycode_to_key(kc)),
(true, false) => Event::Shift(blt_keycode_to_key(kc)),
(false, true) => Event::Ctrl(blt_keycode_to_key(kc)),
(false, false) => Event::Key(blt_keycode_to_key(kc)),
},
// TODO: mouse support
KeyCode::MouseLeft | KeyCode::MouseRight | KeyCode::MouseMiddle |
KeyCode::MouseFourth | KeyCode::MouseFifth => Event::Refresh,
KeyCode::A | KeyCode::B | KeyCode::C | KeyCode::D | KeyCode::E |
KeyCode::F | KeyCode::G | KeyCode::H | KeyCode::I | KeyCode::J |
KeyCode::K | KeyCode::L | KeyCode::M | KeyCode::N | KeyCode::O |
KeyCode::P | KeyCode::Q | KeyCode::R | KeyCode::S | KeyCode::T |
KeyCode::U | KeyCode::V | KeyCode::W | KeyCode::X | KeyCode::Y |
KeyCode::Z | KeyCode::Row1 | KeyCode::Row2 | KeyCode::Row3 |
KeyCode::Row4 | KeyCode::Row5 | KeyCode::Row6 | KeyCode::Row7 |
KeyCode::Row8 | KeyCode::Row9 | KeyCode::Row0 | KeyCode::Grave |
KeyCode::Minus | KeyCode::Equals | KeyCode::LeftBracket |
KeyCode::RightBracket | KeyCode::Backslash | KeyCode::Semicolon |
KeyCode::Apostrophe | KeyCode::Comma | KeyCode::Period |
KeyCode::Slash | KeyCode::Space | KeyCode::NumDivide |
KeyCode::NumMultiply | KeyCode::NumMinus | KeyCode::NumPlus |
KeyCode::NumPeriod | KeyCode::Num1 | KeyCode::Num2 |
KeyCode::Num3 | KeyCode::Num4 | KeyCode::Num5 | KeyCode::Num6 |
KeyCode::Num7 | KeyCode::Num8 | KeyCode::Num9 | KeyCode::Num0 => {
if ctrl {
Event::CtrlChar(blt_keycode_to_char(kc, shift))
} else {
Event::Char(blt_keycode_to_char(kc, shift))
}
}
KeyCode::MouseLeft |
KeyCode::MouseRight |
KeyCode::MouseMiddle |
KeyCode::MouseFourth |
KeyCode::MouseFifth => Event::Refresh,
KeyCode::A |
KeyCode::B |
KeyCode::C |
KeyCode::D |
KeyCode::E |
KeyCode::F |
KeyCode::G |
KeyCode::H |
KeyCode::I |
KeyCode::J |
KeyCode::K |
KeyCode::L |
KeyCode::M |
KeyCode::N |
KeyCode::O |
KeyCode::P |
KeyCode::Q |
KeyCode::R |
KeyCode::S |
KeyCode::T |
KeyCode::U |
KeyCode::V |
KeyCode::W |
KeyCode::X |
KeyCode::Y |
KeyCode::Z |
KeyCode::Row1 |
KeyCode::Row2 |
KeyCode::Row3 |
KeyCode::Row4 |
KeyCode::Row5 |
KeyCode::Row6 |
KeyCode::Row7 |
KeyCode::Row8 |
KeyCode::Row9 |
KeyCode::Row0 |
KeyCode::Grave |
KeyCode::Minus |
KeyCode::Equals |
KeyCode::LeftBracket |
KeyCode::RightBracket |
KeyCode::Backslash |
KeyCode::Semicolon |
KeyCode::Apostrophe |
KeyCode::Comma |
KeyCode::Period |
KeyCode::Slash |
KeyCode::Space |
KeyCode::NumDivide |
KeyCode::NumMultiply |
KeyCode::NumMinus |
KeyCode::NumPlus |
KeyCode::NumPeriod |
KeyCode::Num1 |
KeyCode::Num2 |
KeyCode::Num3 |
KeyCode::Num4 |
KeyCode::Num5 |
KeyCode::Num6 |
KeyCode::Num7 |
KeyCode::Num8 |
KeyCode::Num9 |
KeyCode::Num0 => if ctrl {
Event::CtrlChar(blt_keycode_to_char(kc, shift))
} else {
Event::Char(blt_keycode_to_char(kc, shift))
},
}
}
fn blt_keycode_to_char(kc: KeyCode, shift: bool) -> char {
match kc {
KeyCode::A => if shift { 'A' } else { 'a' },
KeyCode::B => if shift { 'B' } else { 'b' },
KeyCode::C => if shift { 'C' } else { 'c' },
KeyCode::D => if shift { 'D' } else { 'd' },
KeyCode::E => if shift { 'E' } else { 'e' },
KeyCode::F => if shift { 'F' } else { 'f' },
KeyCode::G => if shift { 'G' } else { 'g' },
KeyCode::H => if shift { 'H' } else { 'h' },
KeyCode::I => if shift { 'I' } else { 'i' },
KeyCode::J => if shift { 'J' } else { 'j' },
KeyCode::K => if shift { 'K' } else { 'k' },
KeyCode::L => if shift { 'L' } else { 'l' },
KeyCode::M => if shift { 'M' } else { 'm' },
KeyCode::N => if shift { 'N' } else { 'n' },
KeyCode::O => if shift { 'O' } else { 'o' },
KeyCode::P => if shift { 'P' } else { 'p' },
KeyCode::Q => if shift { 'Q' } else { 'q' },
KeyCode::R => if shift { 'R' } else { 'r' },
KeyCode::S => if shift { 'S' } else { 's' },
KeyCode::T => if shift { 'T' } else { 't' },
KeyCode::U => if shift { 'U' } else { 'u' },
KeyCode::V => if shift { 'V' } else { 'v' },
KeyCode::W => if shift { 'W' } else { 'w' },
KeyCode::X => if shift { 'X' } else { 'x' },
KeyCode::Y => if shift { 'Y' } else { 'y' },
KeyCode::Z => if shift { 'Z' } else { 'z' },
KeyCode::Row1 => if shift { '!' } else { '1' },
KeyCode::Row2 => if shift { '@' } else { '2' },
KeyCode::Row3 => if shift { '#' } else { '3' },
KeyCode::Row4 => if shift { '$' } else { '4' },
KeyCode::Row5 => if shift { '%' } else { '5' },
KeyCode::Row6 => if shift { '^' } else { '6' },
KeyCode::Row7 => if shift { '&' } else { '7' },
KeyCode::Row8 => if shift { '*' } else { '8' },
KeyCode::Row9 => if shift { '(' } else { '9' },
KeyCode::Row0 => if shift { ')' } else { '0' },
KeyCode::Grave => if shift { '~' } else { '`' },
KeyCode::Minus => if shift { '_' } else { '-' },
KeyCode::Equals => if shift { '+' } else { '=' },
KeyCode::LeftBracket => if shift { '{' } else { '[' },
KeyCode::RightBracket => if shift { '}' } else { ']' },
KeyCode::Backslash => if shift { '|' } else { '\\' },
KeyCode::Semicolon => if shift { ':' } else { ';' },
KeyCode::Apostrophe => if shift { '"' } else { '\'' },
KeyCode::Comma => if shift { '<' } else { ',' },
KeyCode::Period => if shift { '>' } else { '.' },
KeyCode::Slash => if shift { '?' } else { '/' },
KeyCode::A => if shift {
'A'
} else {
'a'
},
KeyCode::B => if shift {
'B'
} else {
'b'
},
KeyCode::C => if shift {
'C'
} else {
'c'
},
KeyCode::D => if shift {
'D'
} else {
'd'
},
KeyCode::E => if shift {
'E'
} else {
'e'
},
KeyCode::F => if shift {
'F'
} else {
'f'
},
KeyCode::G => if shift {
'G'
} else {
'g'
},
KeyCode::H => if shift {
'H'
} else {
'h'
},
KeyCode::I => if shift {
'I'
} else {
'i'
},
KeyCode::J => if shift {
'J'
} else {
'j'
},
KeyCode::K => if shift {
'K'
} else {
'k'
},
KeyCode::L => if shift {
'L'
} else {
'l'
},
KeyCode::M => if shift {
'M'
} else {
'm'
},
KeyCode::N => if shift {
'N'
} else {
'n'
},
KeyCode::O => if shift {
'O'
} else {
'o'
},
KeyCode::P => if shift {
'P'
} else {
'p'
},
KeyCode::Q => if shift {
'Q'
} else {
'q'
},
KeyCode::R => if shift {
'R'
} else {
'r'
},
KeyCode::S => if shift {
'S'
} else {
's'
},
KeyCode::T => if shift {
'T'
} else {
't'
},
KeyCode::U => if shift {
'U'
} else {
'u'
},
KeyCode::V => if shift {
'V'
} else {
'v'
},
KeyCode::W => if shift {
'W'
} else {
'w'
},
KeyCode::X => if shift {
'X'
} else {
'x'
},
KeyCode::Y => if shift {
'Y'
} else {
'y'
},
KeyCode::Z => if shift {
'Z'
} else {
'z'
},
KeyCode::Row1 => if shift {
'!'
} else {
'1'
},
KeyCode::Row2 => if shift {
'@'
} else {
'2'
},
KeyCode::Row3 => if shift {
'#'
} else {
'3'
},
KeyCode::Row4 => if shift {
'$'
} else {
'4'
},
KeyCode::Row5 => if shift {
'%'
} else {
'5'
},
KeyCode::Row6 => if shift {
'^'
} else {
'6'
},
KeyCode::Row7 => if shift {
'&'
} else {
'7'
},
KeyCode::Row8 => if shift {
'*'
} else {
'8'
},
KeyCode::Row9 => if shift {
'('
} else {
'9'
},
KeyCode::Row0 => if shift {
')'
} else {
'0'
},
KeyCode::Grave => if shift {
'~'
} else {
'`'
},
KeyCode::Minus => if shift {
'_'
} else {
'-'
},
KeyCode::Equals => if shift {
'+'
} else {
'='
},
KeyCode::LeftBracket => if shift {
'{'
} else {
'['
},
KeyCode::RightBracket => if shift {
'}'
} else {
']'
},
KeyCode::Backslash => if shift {
'|'
} else {
'\\'
},
KeyCode::Semicolon => if shift {
':'
} else {
';'
},
KeyCode::Apostrophe => if shift {
'"'
} else {
'\''
},
KeyCode::Comma => if shift {
'<'
} else {
','
},
KeyCode::Period => if shift {
'>'
} else {
'.'
},
KeyCode::Slash => if shift {
'?'
} else {
'/'
},
KeyCode::Space => ' ',
KeyCode::NumDivide => '/',
KeyCode::NumMultiply => '*',
@ -259,9 +509,7 @@ fn blt_keycode_to_char(kc: KeyCode, shift: bool) -> char {
KeyCode::Num8 => '8',
KeyCode::Num9 => '9',
KeyCode::Num0 => '0',
_ => {
unreachable!("Found unknown input: {:?}", kc)
}
_ => unreachable!("Found unknown input: {:?}", kc),
}
}

View File

@ -77,12 +77,18 @@ impl Concrete {
{
// eprintln!("{:032b}", mevent.bstate);
// Currently unused
let _shift = (mevent.bstate & ncurses::BUTTON_SHIFT as ncurses::mmask_t) != 0;
let _alt = (mevent.bstate & ncurses::BUTTON_ALT as ncurses::mmask_t) != 0;
let _ctrl = (mevent.bstate & ncurses::BUTTON_CTRL as ncurses::mmask_t) != 0;
let _shift = (mevent.bstate
& ncurses::BUTTON_SHIFT as ncurses::mmask_t)
!= 0;
let _alt =
(mevent.bstate & ncurses::BUTTON_ALT as ncurses::mmask_t) != 0;
let _ctrl = (mevent.bstate
& ncurses::BUTTON_CTRL as ncurses::mmask_t)
!= 0;
mevent.bstate &= !(ncurses::BUTTON_SHIFT | ncurses::BUTTON_ALT
| ncurses::BUTTON_CTRL) as ncurses::mmask_t;
| ncurses::BUTTON_CTRL)
as ncurses::mmask_t;
let make_event = |event| {
Event::Mouse {
@ -92,7 +98,9 @@ impl Concrete {
}
};
if mevent.bstate == ncurses::REPORT_MOUSE_POSITION as ncurses::mmask_t {
if mevent.bstate
== ncurses::REPORT_MOUSE_POSITION as ncurses::mmask_t
{
// The event is either a mouse drag event,
// or a weird double-release event. :S
self.last_mouse_button

View File

@ -3,7 +3,7 @@ extern crate pancurses;
use self::super::find_closest;
use backend;
use event::{Event, Key};
use std::cell::{RefCell, Cell};
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use theme::{Color, ColorPair, Effect};
use utf8;
@ -16,10 +16,9 @@ pub struct Concrete {
impl Concrete {
/// Save a new color pair.
fn insert_color(&self, pairs: &mut HashMap<ColorPair, i32>,
pair: ColorPair)
-> i32 {
fn insert_color(
&self, pairs: &mut HashMap<ColorPair, i32>, pair: ColorPair
) -> i32 {
let n = 1 + pairs.len() as i32;
// TODO: when COLORS_PAIRS is available...
@ -34,15 +33,16 @@ impl Concrete {
target
};
pairs.insert(pair, target);
pancurses::init_pair(target as i16,
find_closest(&pair.front),
find_closest(&pair.back));
pancurses::init_pair(
target as i16,
find_closest(&pair.front),
find_closest(&pair.back),
);
target
}
/// Checks the pair in the cache, or re-define a color if needed.
fn get_or_create(&self, pair: ColorPair) -> i32 {
let mut pairs = self.pairs.borrow_mut();
// Find if we have this color in stock
@ -55,7 +55,6 @@ impl Concrete {
}
fn set_colors(&self, pair: ColorPair) {
let i = self.get_or_create(pair);
self.current_style.set(pair);
@ -121,9 +120,9 @@ impl backend::Backend for Concrete {
fn clear(&self, color: Color) {
let id = self.get_or_create(ColorPair {
front: color,
back: color,
});
front: color,
back: color,
});
self.window.bkgd(pancurses::ColorPair(id as u8));
self.window.clear();
}
@ -150,36 +149,33 @@ impl backend::Backend for Concrete {
}
pancurses::Input::Character('\u{9}') => Event::Key(Key::Tab),
pancurses::Input::Character('\u{1b}') => Event::Key(Key::Esc),
pancurses::Input::Character(c) if 32 <= (c as u32) &&
(c as u32) <= 255 => {
Event::Char(utf8::read_char(c as u8, || {
self.window
.getch()
.and_then(|i| match i {
pancurses::Input::Character(c) => {
Some(c as u8)
}
_ => None,
})
})
.unwrap())
pancurses::Input::Character(c)
if 32 <= (c as u32) && (c as u32) <= 255 =>
{
Event::Char(
utf8::read_char(c as u8, || {
self.window.getch().and_then(|i| match i {
pancurses::Input::Character(c) => {
Some(c as u8)
}
_ => None,
})
}).unwrap(),
)
}
pancurses::Input::Character(c) => {
let mut bytes = [0u8; 4];
Event::Unknown(c.encode_utf8(&mut bytes)
.as_bytes()
.to_vec())
Event::Unknown(
c.encode_utf8(&mut bytes).as_bytes().to_vec(),
)
}
// TODO: Some key combos are not recognized by pancurses,
// but are sent as Unknown. We could still parse them here.
pancurses::Input::Unknown(other) => {
Event::Unknown((0..4)
.map(|i| {
((other >> (8 * i)) & 0xFF) as
u8
})
.collect())
}
pancurses::Input::Unknown(other) => Event::Unknown(
(0..4)
.map(|i| ((other >> (8 * i)) & 0xFF) as u8)
.collect(),
),
// TODO: I honestly have no fucking idea what KeyCodeYes is
pancurses::Input::KeyCodeYes => Event::Refresh,
pancurses::Input::KeyBreak => Event::Key(Key::PauseBreak),

View File

@ -15,7 +15,6 @@ use event::{Event, Key};
use std::cell::Cell;
use std::io::Write;
use std::thread;
use theme;
pub struct Concrete {
@ -60,16 +59,16 @@ impl backend::Backend for Concrete {
let resize = chan_signal::notify(&[chan_signal::Signal::WINCH]);
let terminal = AlternateScreen::from(::std::io::stdout()
.into_raw_mode()
.unwrap());
let terminal = AlternateScreen::from(
::std::io::stdout().into_raw_mode().unwrap(),
);
let (sender, receiver) = chan::async();
thread::spawn(move || for key in ::std::io::stdin().events() {
if let Ok(key) = key {
sender.send(map_key(key))
}
});
if let Ok(key) = key {
sender.send(map_key(key))
}
});
let backend = Concrete {
terminal: terminal,
@ -84,10 +83,12 @@ impl backend::Backend for Concrete {
fn finish(&mut self) {
print!("{}{}", termion::cursor::Show, termion::cursor::Goto(1, 1));
print!("{}[49m{}[39m{}",
27 as char,
27 as char,
termion::clear::All);
print!(
"{}[49m{}[39m{}",
27 as char,
27 as char,
termion::clear::All
);
}
fn with_color<F: FnOnce()>(&self, color: theme::ColorPair, f: F) {
@ -135,9 +136,11 @@ impl backend::Backend for Concrete {
}
fn print_at(&self, (x, y): (usize, usize), text: &str) {
print!("{}{}",
termion::cursor::Goto(1 + x as u16, 1 + y as u16),
text);
print!(
"{}{}",
termion::cursor::Goto(1 + x as u16, 1 + y as u16),
text
);
}
fn set_refresh_rate(&mut self, fps: u32) {
@ -189,47 +192,39 @@ fn map_key(event: TEvent) -> Event {
TEvent::Key(TKey::Alt(c)) => Event::AltChar(c),
_ => Event::Unknown(vec![]),
}
}
fn with_color<F, R>(clr: &theme::Color, f: F) -> R
where F: FnOnce(&tcolor::Color) -> R
where
F: FnOnce(&tcolor::Color) -> R,
{
match *clr {
theme::Color::TerminalDefault => f(&tcolor::Reset),
theme::Color::Dark(theme::BaseColor::Black) => f(&tcolor::Black),
theme::Color::Dark(theme::BaseColor::Red) => f(&tcolor::Red),
theme::Color::Dark(theme::BaseColor::Green) => f(&tcolor::Green),
theme::Color::Dark(theme::BaseColor::Yellow) => f(&tcolor::Yellow),
theme::Color::Dark(theme::BaseColor::Blue) => f(&tcolor::Blue),
theme::Color::Dark(theme::BaseColor::Magenta) => f(&tcolor::Magenta),
theme::Color::Dark(theme::BaseColor::Cyan) => f(&tcolor::Cyan),
theme::Color::Dark(theme::BaseColor::White) => f(&tcolor::White),
theme::Color::TerminalDefault => f(&tcolor::Reset),
theme::Color::Dark(theme::BaseColor::Black) => f(&tcolor::Black),
theme::Color::Dark(theme::BaseColor::Red) => f(&tcolor::Red),
theme::Color::Dark(theme::BaseColor::Green) => f(&tcolor::Green),
theme::Color::Dark(theme::BaseColor::Yellow) => f(&tcolor::Yellow),
theme::Color::Dark(theme::BaseColor::Blue) => f(&tcolor::Blue),
theme::Color::Dark(theme::BaseColor::Magenta) => f(&tcolor::Magenta),
theme::Color::Dark(theme::BaseColor::Cyan) => f(&tcolor::Cyan),
theme::Color::Dark(theme::BaseColor::White) => f(&tcolor::White),
theme::Color::Light(theme::BaseColor::Black) => {
f(&tcolor::LightBlack)
}
theme::Color::Light(theme::BaseColor::Red) => f(&tcolor::LightRed),
theme::Color::Light(theme::BaseColor::Green) => {
f(&tcolor::LightGreen)
}
theme::Color::Light(theme::BaseColor::Yellow) => {
f(&tcolor::LightYellow)
}
theme::Color::Light(theme::BaseColor::Blue) => f(&tcolor::LightBlue),
theme::Color::Light(theme::BaseColor::Magenta) => {
f(&tcolor::LightMagenta)
}
theme::Color::Light(theme::BaseColor::Cyan) => f(&tcolor::LightCyan),
theme::Color::Light(theme::BaseColor::White) => {
f(&tcolor::LightWhite)
}
theme::Color::Light(theme::BaseColor::Black) => f(&tcolor::LightBlack),
theme::Color::Light(theme::BaseColor::Red) => f(&tcolor::LightRed),
theme::Color::Light(theme::BaseColor::Green) => f(&tcolor::LightGreen),
theme::Color::Light(theme::BaseColor::Yellow) => {
f(&tcolor::LightYellow)
}
theme::Color::Light(theme::BaseColor::Blue) => f(&tcolor::LightBlue),
theme::Color::Light(theme::BaseColor::Magenta) => {
f(&tcolor::LightMagenta)
}
theme::Color::Light(theme::BaseColor::Cyan) => f(&tcolor::LightCyan),
theme::Color::Light(theme::BaseColor::White) => f(&tcolor::LightWhite),
theme::Color::Rgb(r, g, b) => f(&tcolor::Rgb(r, g, b)),
theme::Color::RgbLowRes(r, g, b) => {
f(&tcolor::AnsiValue::rgb(r, g, b))
}
}
theme::Color::Rgb(r, g, b) => f(&tcolor::Rgb(r, g, b)),
theme::Color::RgbLowRes(r, g, b) => {
f(&tcolor::AnsiValue::rgb(r, g, b))
}
}
}

View File

@ -1,6 +1,8 @@
use backend;
use backend::Backend;
use direction;
use event::{Callback, Event, EventResult};
use printer::Printer;
use std::any::Any;
use std::collections::HashMap;
use std::path::Path;
@ -9,8 +11,6 @@ use theme;
use vec::Vec2;
use view::{self, Finder, View};
use views;
use printer::Printer;
use direction;
/// Identifies a screen in the cursive root.
pub type ScreenId = usize;

View File

@ -19,7 +19,7 @@ use XY;
use vec::Vec2;
/// Describes a vertical or horizontal orientation for a view.
#[derive(Clone,Copy,Debug,PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Orientation {
/// Horizontal orientation
Horizontal,

View File

@ -109,7 +109,7 @@ mod utf8;
pub mod backend;
pub use cursive::{Cursive, ScreenId};
pub use printer::Printer;
pub use with::With;
pub use xy::XY;
pub use cursive::{ScreenId, Cursive};

View File

@ -42,8 +42,9 @@ impl MenuItem {
pub fn label(&self) -> &str {
match *self {
MenuItem::Delimiter => "",
MenuItem::Leaf(ref label, _) |
MenuItem::Subtree(ref label, _) => label,
MenuItem::Leaf(ref label, _) | MenuItem::Subtree(ref label, _) => {
label
}
}
}
@ -93,8 +94,9 @@ impl MenuTree {
/// Adds a actionnable leaf to the end of this tree.
pub fn add_leaf<S, F>(&mut self, title: S, cb: F)
where S: Into<String>,
F: 'static + Fn(&mut Cursive)
where
S: Into<String>,
F: 'static + Fn(&mut Cursive),
{
let i = self.children.len();
self.insert_leaf(i, title, cb);
@ -102,25 +104,29 @@ impl MenuTree {
/// Inserts a leaf at the given position.
pub fn insert_leaf<S, F>(&mut self, i: usize, title: S, cb: F)
where S: Into<String>,
F: 'static + Fn(&mut Cursive)
where
S: Into<String>,
F: 'static + Fn(&mut Cursive),
{
let title = title.into();
self.children.insert(i, MenuItem::Leaf(title, Callback::from_fn(cb)));
self.children
.insert(i, MenuItem::Leaf(title, Callback::from_fn(cb)));
}
/// Adds a actionnable leaf to the end of this tree - chainable variant.
pub fn leaf<S, F>(self, title: S, cb: F) -> Self
where S: Into<String>,
F: 'static + Fn(&mut Cursive)
where
S: Into<String>,
F: 'static + Fn(&mut Cursive),
{
self.with(|menu| menu.add_leaf(title, cb))
}
/// Inserts a subtree at the given position.
pub fn insert_subtree<S>(&mut self, i: usize, title: S, tree: MenuTree)
where S: Into<String>
where
S: Into<String>,
{
let title = title.into();
let tree = MenuItem::Subtree(title, Rc::new(tree));
@ -129,7 +135,8 @@ impl MenuTree {
/// Adds a submenu to the end of this tree.
pub fn add_subtree<S>(&mut self, title: S, tree: MenuTree)
where S: Into<String>
where
S: Into<String>,
{
let i = self.children.len();
self.insert_subtree(i, title, tree);
@ -137,7 +144,8 @@ impl MenuTree {
/// Adds a submenu to the end of this tree - chainable variant.
pub fn subtree<S>(self, title: S, tree: MenuTree) -> Self
where S: Into<String>
where
S: Into<String>,
{
self.with(|menu| menu.add_subtree(title, tree))
}
@ -165,13 +173,13 @@ impl MenuTree {
/// Returns `None` if the given title was not found,
/// or if it wasn't a subtree.
pub fn find_subtree(&mut self, title: &str) -> Option<&mut MenuTree> {
self.find_item(title)
.and_then(|item| if let MenuItem::Subtree(_, ref mut tree) =
*item {
self.find_item(title).and_then(
|item| if let MenuItem::Subtree(_, ref mut tree) = *item {
Some(Rc::make_mut(tree))
} else {
None
})
},
)
}
/// Removes the item at the given position.

View File

@ -5,10 +5,8 @@ use backend::{self, Backend};
use std::cell::Cell;
use std::cmp::min;
use std::rc::Rc;
use theme::{BorderStyle, ColorStyle, Effect, Theme};
use unicode_segmentation::UnicodeSegmentation;
use utils::prefix;
use vec::Vec2;
@ -34,9 +32,9 @@ impl<'a> Printer<'a> {
///
/// But nobody needs to know that.
#[doc(hidden)]
pub fn new<T: Into<Vec2>>(size: T, theme: &'a Theme,
backend: &'a backend::Concrete)
-> Self {
pub fn new<T: Into<Vec2>>(
size: T, theme: &'a Theme, backend: &'a backend::Concrete
) -> Self {
Printer {
offset: Vec2::zero(),
size: size.into(),
@ -130,7 +128,8 @@ impl<'a> Printer<'a> {
/// });
/// ```
pub fn with_color<F>(&self, c: ColorStyle, f: F)
where F: FnOnce(&Printer)
where
F: FnOnce(&Printer),
{
self.backend.with_color(c.resolve(self.theme), || f(self));
}
@ -140,7 +139,8 @@ impl<'a> Printer<'a> {
///
/// Will probably use a cursive enum some day.
pub fn with_effect<F>(&self, effect: Effect, f: F)
where F: FnOnce(&Printer)
where
F: FnOnce(&Printer),
{
self.backend.with_effect(effect, || f(self));
}
@ -161,8 +161,9 @@ impl<'a> Printer<'a> {
/// # let printer = Printer::new((6,4), &t, &b);
/// printer.print_box((0,0), (6,4), false);
/// ```
pub fn print_box<T: Into<Vec2>, S: Into<Vec2>>(&self, start: T, size: S,
invert: bool) {
pub fn print_box<T: Into<Vec2>, S: Into<Vec2>>(
&self, start: T, size: S, invert: bool
) {
self.new.set(false);
let start = start.into();
@ -195,7 +196,8 @@ impl<'a> Printer<'a> {
/// use `ColorStyle::Tertiary`.
/// * Otherwise, use `ColorStyle::Primary`.
pub fn with_high_border<F>(&self, invert: bool, f: F)
where F: FnOnce(&Printer)
where
F: FnOnce(&Printer),
{
let color = match self.theme.borders {
BorderStyle::None => return,
@ -213,7 +215,8 @@ impl<'a> Printer<'a> {
/// use `ColorStyle::Tertiary`.
/// * Otherwise, use `ColorStyle::Primary`.
pub fn with_low_border<F>(&self, invert: bool, f: F)
where F: FnOnce(&Printer)
where
F: FnOnce(&Printer),
{
let color = match self.theme.borders {
BorderStyle::None => return,
@ -232,16 +235,18 @@ impl<'a> Printer<'a> {
/// uses `ColorStyle::Highlight`.
/// * Otherwise, uses `ColorStyle::HighlightInactive`.
pub fn with_selection<F: FnOnce(&Printer)>(&self, selection: bool, f: F) {
self.with_color(if selection {
if self.focused {
ColorStyle::Highlight
} else {
ColorStyle::HighlightInactive
}
} else {
ColorStyle::Primary
},
f);
self.with_color(
if selection {
if self.focused {
ColorStyle::Highlight
} else {
ColorStyle::HighlightInactive
}
} else {
ColorStyle::Primary
},
f,
);
}
/// Prints a horizontal delimiter with side border `├` and `┤`.
@ -253,9 +258,9 @@ impl<'a> Printer<'a> {
}
/// Returns a printer on a subset of this one's area.
pub fn sub_printer<S: Into<Vec2>, T: Into<Vec2>>(&'a self, offset: S,
size: T, focused: bool)
-> Printer<'a> {
pub fn sub_printer<S: Into<Vec2>, T: Into<Vec2>>(
&'a self, offset: S, size: T, focused: bool
) -> Printer<'a> {
let size = size.into();
let offset = offset.into().or_min(self.size);
let available = if !offset.fits_in(self.size) {

View File

@ -118,7 +118,6 @@ use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
use toml;
/// Text effect
@ -203,10 +202,9 @@ impl ColorStyle {
pub fn resolve(&self, theme: &Theme) -> ColorPair {
let c = &theme.colors;
let (front, back) = match *self {
ColorStyle::TerminalDefault => (
Color::TerminalDefault,
Color::TerminalDefault,
),
ColorStyle::TerminalDefault => {
(Color::TerminalDefault, Color::TerminalDefault)
}
ColorStyle::Background => (c.view, c.background),
ColorStyle::Shadow => (c.shadow, c.shadow),
ColorStyle::Primary => (c.primary, c.view),
@ -503,7 +501,6 @@ impl Color {
fn parse_special(value: &str) -> Option<Color> {
if value.starts_with('#') {
let value = &value[1..];
// Compute per-color length, and amplitude
let (l, multiplier) = match value.len() {

View File

@ -10,6 +10,5 @@
#[doc(no_inline)]
pub use With;
#[doc(no_inline)]
pub use view::{Boxable, Finder, Identifiable, View};

View File

@ -8,7 +8,8 @@ use std::char::from_u32;
/// Returns an error if the stream is invalid utf-8.
#[allow(dead_code)]
pub fn read_char<F>(first: u8, next: F) -> Result<char, String>
where F: Fn() -> Option<u8>
where
F: Fn() -> Option<u8>,
{
if first < 0x80 {
return Ok(first as char);
@ -32,9 +33,11 @@ pub fn read_char<F>(first: u8, next: F) -> Result<char, String>
let byte =
try!(next().ok_or_else(|| "Missing UTF-8 byte".to_string()));
if byte & 0xC0 != 0x80 {
return Err(format!("Found non-continuation byte after leading: \
{}",
byte));
return Err(format!(
"Found non-continuation byte after leading: \
{}",
byte
));
}
// We have 6 fresh new bits to read, make room.
res <<= 6;

View File

@ -42,9 +42,11 @@ pub struct Prefix {
/// prefix(my_text.graphemes(true), 5, "");
/// # }
/// ```
pub fn prefix<'a, I>(iter: I, available_width: usize, delimiter: &str)
-> Prefix
where I: Iterator<Item = &'a str>
pub fn prefix<'a, I>(
iter: I, available_width: usize, delimiter: &str
) -> Prefix
where
I: Iterator<Item = &'a str>,
{
let delimiter_width = delimiter.width();
let delimiter_len = delimiter.len();
@ -53,17 +55,16 @@ pub fn prefix<'a, I>(iter: I, available_width: usize, delimiter: &str)
// before the next token, including any space.
let mut current_width = 0;
let sum: usize = iter.take_while(|token| {
let width = token.width();
if current_width + width > available_width {
false
} else {
// Include the delimiter after this token.
current_width += width;
current_width += delimiter_width;
true
}
})
.map(|token| token.len() + delimiter_len)
let width = token.width();
if current_width + width > available_width {
false
} else {
// Include the delimiter after this token.
current_width += width;
current_width += delimiter_width;
true
}
}).map(|token| token.len() + delimiter_len)
.sum();
// We counted delimiter once too many times,
@ -88,7 +89,8 @@ pub fn prefix<'a, I>(iter: I, available_width: usize, delimiter: &str)
///
/// This is a shortcut for `prefix_length(iter.rev(), width, delimiter)`
pub fn suffix<'a, I>(iter: I, width: usize, delimiter: &str) -> Prefix
where I: DoubleEndedIterator<Item = &'a str>
where
I: DoubleEndedIterator<Item = &'a str>,
{
prefix(iter.rev(), width, delimiter)
}

View File

@ -1,5 +1,4 @@
use std::io::{self, Read};
use views::Counter;
/// Wrapper around a `Read` that reports the progress made.

View File

@ -14,7 +14,7 @@ use std::ops::{Add, Div, Mul, Sub};
/// [`XY`]: ../struct.XY.html
pub type Vec2 = XY<usize>;
impl <T: PartialOrd> PartialOrd for XY<T> {
impl<T: PartialOrd> PartialOrd for XY<T> {
/// `a < b` <=> `a.x < b.x && a.y < b.y`
fn partial_cmp(&self, other: &XY<T>) -> Option<Ordering> {
if self == other {

View File

@ -7,8 +7,9 @@ use views::BoxView;
/// [`BoxView`]: ../views/struct.BoxView.html
pub trait Boxable: View + Sized {
/// Wraps `self` in a `BoxView` with the given size constraints.
fn boxed(self, width: SizeConstraint, height: SizeConstraint)
-> BoxView<Self> {
fn boxed(
self, width: SizeConstraint, height: SizeConstraint
) -> BoxView<Self> {
BoxView::new(width, height, self)
}

View File

@ -52,24 +52,19 @@ mod boxable;
pub use self::boxable::Boxable;
pub use self::identifiable::Identifiable;
pub use self::position::{Offset, Position};
pub use self::scroll::{ScrollBase, ScrollStrategy};
pub use self::size_cache::SizeCache;
pub use self::size_constraint::SizeConstraint;
pub use self::view_path::ViewPath;
pub use self::view_wrapper::ViewWrapper;
use Printer;
use direction::Direction;
use event::{Event, EventResult};
use std::any::Any;
use vec::Vec2;
use views::IdView;
use std::any::Any;
/// Main trait defining a view behaviour.
pub trait View {
/// Called when a key was pressed.
@ -161,13 +156,15 @@ pub trait Finder {
/// If the view is not found, or if it is not of the asked type,
/// it returns None.
fn call_on<V, F, R>(&mut self, sel: &Selector, callback: F) -> Option<R>
where V: View + Any,
F: FnOnce(&mut V) -> R;
where
V: View + Any,
F: FnOnce(&mut V) -> R;
/// Convenient method to use `call_on` with a `view::Selector::Id`.
fn find_id<V, F, R>(&mut self, id: &str, callback: F) -> Option<R>
where V: View + Any,
F: FnOnce(&mut V) -> R
where
V: View + Any,
F: FnOnce(&mut V) -> R,
{
self.call_on(&Selector::Id(id), callback)
}
@ -175,8 +172,9 @@ pub trait Finder {
impl<T: View> Finder for T {
fn call_on<V, F, R>(&mut self, sel: &Selector, callback: F) -> Option<R>
where V: View + Any,
F: FnOnce(&mut V) -> R
where
V: View + Any,
F: FnOnce(&mut V) -> R,
{
let mut result = None;
{
@ -184,7 +182,8 @@ impl<T: View> Finder for T {
let mut callback = Some(callback);
let callback = |v: &mut Any| if let Some(callback) =
callback.take() {
callback.take()
{
if v.is::<V>() {
*result_ref = v.downcast_mut::<V>().map(|v| callback(v));
} else if v.is::<IdView<V>>() {

View File

@ -29,23 +29,27 @@ impl Position {
/// and a parent with the absolute coordinates `parent`, drawing the
/// child with its top-left corner at the returned coordinates will
/// position him appropriately.
pub fn compute_offset<S, A, P>(&self, size: S, available: A, parent: P)
-> Vec2
where S: Into<Vec2>,
A: Into<Vec2>,
P: Into<Vec2>
pub fn compute_offset<S, A, P>(
&self, size: S, available: A, parent: P
) -> Vec2
where
S: Into<Vec2>,
A: Into<Vec2>,
P: Into<Vec2>,
{
let available = available.into();
let size = size.into();
let parent = parent.into();
Vec2::new(self.x.compute_offset(size.x, available.x, parent.x),
self.y.compute_offset(size.y, available.y, parent.y))
Vec2::new(
self.x.compute_offset(size.x, available.x, parent.x),
self.y.compute_offset(size.y, available.y, parent.y),
)
}
}
/// Single-dimensional offset policy.
#[derive(PartialEq,Debug,Clone)]
#[derive(PartialEq, Debug, Clone)]
pub enum Offset {
/// In the center of the screen
Center,
@ -60,9 +64,9 @@ pub enum Offset {
impl Offset {
/// Computes a single-dimension offset requred to draw a view.
pub fn compute_offset(&self, size: usize, available: usize,
parent: usize)
-> usize {
pub fn compute_offset(
&self, size: usize, available: usize, parent: usize
) -> usize {
if size > available {
0
} else {

View File

@ -49,7 +49,9 @@ impl SizeCache {
/// * `size` must fit inside `req`.
/// * for each dimension, `constrained = (size == req)`
pub fn build(size: Vec2, req: Vec2) -> XY<Self> {
XY::new(SizeCache::new(size.x, size.x >= req.x),
SizeCache::new(size.y, size.y >= req.y))
XY::new(
SizeCache::new(size.x, size.x >= req.x),
SizeCache::new(size.y, size.y >= req.y),
)
}
}

View File

@ -29,8 +29,9 @@ impl SizeConstraint {
SizeConstraint::Full |
SizeConstraint::AtLeast(_) => available,
// If the available space is too small, always give in.
SizeConstraint::Fixed(value) |
SizeConstraint::AtMost(value) => min(value, available),
SizeConstraint::Fixed(value) | SizeConstraint::AtMost(value) => {
min(value, available)
}
}
}

View File

@ -27,6 +27,8 @@ pub trait ToPath {
impl<'a> ToPath for &'a [usize] {
fn to_path(self) -> ViewPath {
ViewPath { path: self.to_owned() }
ViewPath {
path: self.to_owned(),
}
}
}

View File

@ -1,5 +1,4 @@
use Printer;
use direction::Direction;
use event::{Event, EventResult};
use std::any::Any;
@ -48,9 +47,8 @@ pub trait ViewWrapper {
/// Wraps the `on_event` method.
fn wrap_on_event(&mut self, ch: Event) -> EventResult {
self.with_view_mut(|v| v.on_event(ch)).unwrap_or(
EventResult::Ignored,
)
self.with_view_mut(|v| v.on_event(ch))
.unwrap_or(EventResult::Ignored)
}
/// Wraps the `layout` method.
@ -60,21 +58,21 @@ pub trait ViewWrapper {
/// Wraps the `take_focus` method.
fn wrap_take_focus(&mut self, source: Direction) -> bool {
self.with_view_mut(|v| v.take_focus(source)).unwrap_or(
false,
)
self.with_view_mut(|v| v.take_focus(source))
.unwrap_or(false)
}
/// Wraps the `find` method.
fn wrap_call_on_any<'a>(&mut self, selector: &Selector, callback: Box<FnMut(&mut Any) + 'a>) {
fn wrap_call_on_any<'a>(
&mut self, selector: &Selector, callback: Box<FnMut(&mut Any) + 'a>
) {
self.with_view_mut(|v| v.call_on_any(selector, callback));
}
/// Wraps the `focus_view` method.
fn wrap_focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
self.with_view_mut(|v| v.focus_view(selector)).unwrap_or(
Err(()),
)
self.with_view_mut(|v| v.focus_view(selector))
.unwrap_or(Err(()))
}
/// Wraps the `needs_relayout` method.
@ -126,7 +124,9 @@ impl<T: ViewWrapper> View for T {
self.wrap_take_focus(source)
}
fn call_on_any<'a>(&mut self, selector: &Selector, callback: Box<FnMut(&mut Any) + 'a>) {
fn call_on_any<'a>(
&mut self, selector: &Selector, callback: Box<FnMut(&mut Any) + 'a>
) {
self.wrap_call_on_any(selector, callback)
}

View File

@ -38,8 +38,9 @@ impl<T: View> BoxView<T> {
/// Creates a new `BoxView` with the given width and height requirements.
///
/// `None` values will use the wrapped view's preferences.
pub fn new(width: SizeConstraint, height: SizeConstraint, view: T)
-> Self {
pub fn new(
width: SizeConstraint, height: SizeConstraint, view: T
) -> Self {
BoxView {
size: (width, height).into(),
squishable: false,
@ -64,9 +65,11 @@ impl<T: View> BoxView<T> {
pub fn with_fixed_size<S: Into<Vec2>>(size: S, view: T) -> Self {
let size = size.into();
BoxView::new(SizeConstraint::Fixed(size.x),
SizeConstraint::Fixed(size.y),
view)
BoxView::new(
SizeConstraint::Fixed(size.x),
SizeConstraint::Fixed(size.y),
view,
)
}
/// Wraps `view` in a new `BoxView` with fixed width.
@ -98,54 +101,66 @@ impl<T: View> BoxView<T> {
pub fn with_max_size<S: Into<Vec2>>(size: S, view: T) -> Self {
let size = size.into();
BoxView::new(SizeConstraint::AtMost(size.x),
SizeConstraint::AtMost(size.y),
view)
BoxView::new(
SizeConstraint::AtMost(size.x),
SizeConstraint::AtMost(size.y),
view,
)
}
/// Wraps `view` in a `BoxView` which will enforce a maximum width.
///
/// The resulting width will never be more than `max_width`.
pub fn with_max_width(max_width: usize, view: T) -> Self {
BoxView::new(SizeConstraint::AtMost(max_width),
SizeConstraint::Free,
view)
BoxView::new(
SizeConstraint::AtMost(max_width),
SizeConstraint::Free,
view,
)
}
/// Wraps `view` in a `BoxView` which will enforce a maximum height.
///
/// The resulting height will never be more than `max_height`.
pub fn with_max_height(max_height: usize, view: T) -> Self {
BoxView::new(SizeConstraint::Free,
SizeConstraint::AtMost(max_height),
view)
BoxView::new(
SizeConstraint::Free,
SizeConstraint::AtMost(max_height),
view,
)
}
/// Wraps `view` in a `BoxView` which will never be smaller than `size`.
pub fn with_min_size<S: Into<Vec2>>(size: S, view: T) -> Self {
let size = size.into();
BoxView::new(SizeConstraint::AtLeast(size.x),
SizeConstraint::AtLeast(size.y),
view)
BoxView::new(
SizeConstraint::AtLeast(size.x),
SizeConstraint::AtLeast(size.y),
view,
)
}
/// Wraps `view` in a `BoxView` which will enforce a minimum width.
///
/// The resulting width will never be less than `min_width`.
pub fn with_min_width(min_width: usize, view: T) -> Self {
BoxView::new(SizeConstraint::AtLeast(min_width),
SizeConstraint::Free,
view)
BoxView::new(
SizeConstraint::AtLeast(min_width),
SizeConstraint::Free,
view,
)
}
/// Wraps `view` in a `BoxView` which will enforce a minimum height.
///
/// The resulting height will never be less than `min_height`.
pub fn with_min_height(min_height: usize, view: T) -> Self {
BoxView::new(SizeConstraint::Free,
SizeConstraint::AtLeast(min_height),
view)
BoxView::new(
SizeConstraint::Free,
SizeConstraint::AtLeast(min_height),
view,
)
}
}
@ -153,7 +168,6 @@ impl<T: View> ViewWrapper for BoxView<T> {
wrap_impl!(self.view: T);
fn wrap_required_size(&mut self, req: Vec2) -> Vec2 {
let req = self.size.zip_map(req, SizeConstraint::available);
let child_size = self.view.required_size(req);
@ -165,16 +179,18 @@ impl<T: View> ViewWrapper for BoxView<T> {
if self.squishable {
// We respect the request if we're less or equal.
let respect_req = result.zip_map(req, |res, req| res <= req);
result.zip_map(respect_req.zip(child_size),
|res, (respect, child)| {
if respect {
// If we respect the request, keep the result
res
} else {
// Otherwise, take the child as squish attempt.
child
}
})
result.zip_map(
respect_req.zip(child_size),
|res, (respect, child)| {
if respect {
// If we respect the request, keep the result
res
} else {
// Otherwise, take the child as squish attempt.
child
}
},
)
} else {
result
}

View File

@ -19,7 +19,7 @@ pub struct Canvas<T> {
needs_relayout: Box<Fn(&T) -> bool>,
}
impl <T: 'static + View> Canvas<T> {
impl<T: 'static + View> Canvas<T> {
/// Creates a new Canvas around the given view.
///
/// By default, forwards all calls to the inner view.
@ -65,7 +65,8 @@ impl<T> Canvas<T> {
/// Sets the closure for `draw(&Printer)`.
pub fn set_draw<F>(&mut self, f: F)
where F: 'static + Fn(&T, &Printer)
where
F: 'static + Fn(&T, &Printer),
{
self.draw = Box::new(f);
}
@ -74,14 +75,16 @@ impl<T> Canvas<T> {
///
/// Chainable variant.
pub fn with_draw<F>(self, f: F) -> Self
where F: 'static + Fn(&T, &Printer)
where
F: 'static + Fn(&T, &Printer),
{
self.with(|s| s.set_draw(f))
}
/// Sets the closure for `on_event(Event)`.
pub fn set_on_event<F>(&mut self, f: F)
where F: 'static + FnMut(&mut T, Event) -> EventResult
where
F: 'static + FnMut(&mut T, Event) -> EventResult,
{
self.on_event = Box::new(f);
}
@ -90,14 +93,16 @@ impl<T> Canvas<T> {
///
/// Chainable variant.
pub fn with_on_event<F>(self, f: F) -> Self
where F: 'static + FnMut(&mut T, Event) -> EventResult
where
F: 'static + FnMut(&mut T, Event) -> EventResult,
{
self.with(|s| s.set_on_event(f))
}
/// Sets the closure for `required_size(Vec2)`.
pub fn set_required_size<F>(&mut self, f: F)
where F: 'static + FnMut(&mut T, Vec2) -> Vec2
where
F: 'static + FnMut(&mut T, Vec2) -> Vec2,
{
self.required_size = Box::new(f);
}
@ -106,14 +111,16 @@ impl<T> Canvas<T> {
///
/// Chainable variant.
pub fn with_required_size<F>(self, f: F) -> Self
where F: 'static + FnMut(&mut T, Vec2) -> Vec2
where
F: 'static + FnMut(&mut T, Vec2) -> Vec2,
{
self.with(|s| s.set_required_size(f))
}
/// Sets the closure for `layout(Vec2)`.
pub fn set_layout<F>(&mut self, f: F)
where F: 'static + FnMut(&mut T, Vec2)
where
F: 'static + FnMut(&mut T, Vec2),
{
self.layout = Box::new(f);
}
@ -122,14 +129,16 @@ impl<T> Canvas<T> {
///
/// Chainable variant.
pub fn with_layout<F>(self, f: F) -> Self
where F: 'static + FnMut(&mut T, Vec2)
where
F: 'static + FnMut(&mut T, Vec2),
{
self.with(|s| s.set_layout(f))
}
/// Sets the closure for `take_focus(Direction)`.
pub fn set_take_focus<F>(&mut self, f: F)
where F: 'static + FnMut(&mut T, Direction) -> bool
where
F: 'static + FnMut(&mut T, Direction) -> bool,
{
self.take_focus = Box::new(f);
}
@ -138,14 +147,16 @@ impl<T> Canvas<T> {
///
/// Chainable variant.
pub fn with_take_focus<F>(self, f: F) -> Self
where F: 'static + FnMut(&mut T, Direction) -> bool
where
F: 'static + FnMut(&mut T, Direction) -> bool,
{
self.with(|s| s.set_take_focus(f))
}
/// Sets the closure for `needs_relayout()`.
pub fn set_needs_relayout<F>(&mut self, f: F)
where F: 'static + Fn(&T) -> bool
where
F: 'static + Fn(&T) -> bool,
{
self.needs_relayout = Box::new(f);
}
@ -155,7 +166,8 @@ impl<T> Canvas<T> {
///
/// Chainable variant.
pub fn with_needs_relayout<F>(self, f: F) -> Self
where F: 'static + Fn(&T) -> bool
where
F: 'static + Fn(&T) -> bool,
{
self.with(|s| s.set_needs_relayout(f))
}

View File

@ -4,7 +4,6 @@ use {Cursive, Printer, With};
use direction::Direction;
use event::{Callback, Event, EventResult, Key};
use std::cell::RefCell;
use std::rc::Rc;
use theme::{ColorStyle, Effect};
use unicode_segmentation::UnicodeSegmentation;
@ -195,7 +194,8 @@ impl EditView {
/// If you don't need a mutable closure but want the possibility of
/// recursive calls, see [`set_on_edit`](#method.set_on_edit).
pub fn set_on_edit_mut<F>(&mut self, callback: F)
where F: FnMut(&mut Cursive, &str, usize) + 'static
where
F: FnMut(&mut Cursive, &str, usize) + 'static,
{
let callback = RefCell::new(callback);
// Here's the weird trick: if we're already borrowed,
@ -220,7 +220,8 @@ impl EditView {
/// If you need a mutable closure and don't care about the recursive
/// aspect, see [`set_on_edit_mut`](#method.set_on_edit_mut).
pub fn set_on_edit<F>(&mut self, callback: F)
where F: Fn(&mut Cursive, &str, usize) + 'static
where
F: Fn(&mut Cursive, &str, usize) + 'static,
{
self.on_edit = Some(Rc::new(callback));
}
@ -229,7 +230,8 @@ impl EditView {
///
/// Chainable variant. See [`set_on_edit_mut`](#method.set_on_edit_mut).
pub fn on_edit_mut<F>(self, callback: F) -> Self
where F: FnMut(&mut Cursive, &str, usize) + 'static
where
F: FnMut(&mut Cursive, &str, usize) + 'static,
{
self.with(|v| v.set_on_edit_mut(callback))
}
@ -238,7 +240,8 @@ impl EditView {
///
/// Chainable variant. See [`set_on_edit`](#method.set_on_edit).
pub fn on_edit<F>(self, callback: F) -> Self
where F: Fn(&mut Cursive, &str, usize) + 'static
where
F: Fn(&mut Cursive, &str, usize) + 'static,
{
self.with(|v| v.set_on_edit(callback))
}
@ -253,16 +256,18 @@ impl EditView {
/// If you don't need a mutable closure but want the possibility of
/// recursive calls, see [`set_on_submit`](#method.set_on_submit).
pub fn set_on_submit_mut<F>(&mut self, callback: F)
where F: FnMut(&mut Cursive, &str) + 'static
where
F: FnMut(&mut Cursive, &str) + 'static,
{
// TODO: don't duplicate all those methods.
// Instead, have some generic function immutify()
// or something that wraps a FnMut closure.
let callback = RefCell::new(callback);
self.set_on_submit(move |s, text| if let Ok(mut f) =
callback.try_borrow_mut() {
(&mut *f)(s, text);
});
self.set_on_submit(
move |s, text| if let Ok(mut f) = callback.try_borrow_mut() {
(&mut *f)(s, text);
},
);
}
/// Sets a callback to be called when `<Enter>` is pressed.
@ -275,7 +280,8 @@ impl EditView {
/// If you need a mutable closure and don't care about the recursive
/// aspect, see [`set_on_submit_mut`](#method.set_on_submit_mut).
pub fn set_on_submit<F>(&mut self, callback: F)
where F: Fn(&mut Cursive, &str) + 'static
where
F: Fn(&mut Cursive, &str) + 'static,
{
self.on_submit = Some(Rc::new(callback));
}
@ -284,7 +290,8 @@ impl EditView {
///
/// Chainable variant.
pub fn on_submit_mut<F>(self, callback: F) -> Self
where F: FnMut(&mut Cursive, &str) + 'static
where
F: FnMut(&mut Cursive, &str) + 'static,
{
self.with(|v| v.set_on_submit_mut(callback))
}
@ -293,7 +300,8 @@ impl EditView {
///
/// Chainable variant.
pub fn on_submit<F>(self, callback: F) -> Self
where F: Fn(&mut Cursive, &str) + 'static
where
F: Fn(&mut Cursive, &str) + 'static,
{
self.with(|v| v.set_on_submit(callback))
}
@ -381,16 +389,15 @@ impl EditView {
// Look at the content before the cursor (we will print its tail).
// From the end, count the length until we reach `available`.
// Then sum the byte lengths.
let suffix_length = simple_suffix(&self.content[self.offset..
self.cursor],
available)
.length;
let suffix_length = simple_suffix(
&self.content[self.offset..self.cursor],
available,
).length;
assert!(suffix_length <= self.cursor);
self.offset = self.cursor - suffix_length;
// Make sure the cursor is in view
assert!(self.cursor >= self.offset);
}
// If we have too much space
@ -416,10 +423,13 @@ fn make_small_stars(length: usize) -> &'static str {
impl View for EditView {
fn draw(&self, printer: &Printer) {
assert_eq!(printer.size.x, self.last_length,
"Was promised {}, received {}",
self.last_length,
printer.size.x);
assert_eq!(
printer.size.x,
self.last_length,
"Was promised {}, received {}",
self.last_length,
printer.size.x
);
let width = self.content.width();
printer.with_color(self.style, |printer| {
@ -437,16 +447,24 @@ impl View for EditView {
} else {
printer.print((0, 0), &self.content);
}
let filler_len = (printer.size.x - width) / self.filler.width();
printer.print_hline((width, 0),
filler_len,
self.filler.as_str());
let filler_len =
(printer.size.x - width) / self.filler.width();
printer.print_hline(
(width, 0),
filler_len,
self.filler.as_str(),
);
} else {
let content = &self.content[self.offset..];
let display_bytes = content.graphemes(true)
let display_bytes = content
.graphemes(true)
.scan(0, |w, g| {
*w += g.width();
if *w > self.last_length { None } else { Some(g) }
if *w > self.last_length {
None
} else {
Some(g)
}
})
.map(|g| g.len())
.fold(0, |a, b| a + b);
@ -461,10 +479,13 @@ impl View for EditView {
}
if width < self.last_length {
let filler_len = (self.last_length - width) / self.filler.width();
printer.print_hline((width, 0),
filler_len,
self.filler.as_str());
let filler_len =
(self.last_length - width) / self.filler.width();
printer.print_hline(
(width, 0),
filler_len,
self.filler.as_str(),
);
}
}
});
@ -478,9 +499,11 @@ impl View for EditView {
let selected = self.content[self.cursor..]
.graphemes(true)
.next()
.expect(&format!("Found no char at cursor {} in {}",
self.cursor,
&self.content));
.expect(&format!(
"Found no char at cursor {} in {}",
self.cursor,
&self.content
));
if self.secret {
make_small_stars(selected.width())
} else {
@ -503,7 +526,6 @@ impl View for EditView {
}
fn on_event(&mut self, event: Event) -> EventResult {
match event {
Event::Char(ch) => self.insert(ch),
// TODO: handle ctrl-key?
@ -545,7 +567,9 @@ impl View for EditView {
Event::Key(Key::Enter) if self.on_submit.is_some() => {
let cb = self.on_submit.clone().unwrap();
let content = self.content.clone();
return EventResult::with_cb(move |s| { cb(s, &content); });
return EventResult::with_cb(move |s| {
cb(s, &content);
});
}
_ => return EventResult::Ignored,
}
@ -553,12 +577,13 @@ impl View for EditView {
self.keep_cursor_in_view();
let cb = self.on_edit.clone().map(|cb| {
// Get a new Rc on the content
let content = self.content.clone();
let cursor = self.cursor;
Callback::from_fn(move |s| { cb(s, &content, cursor); })
Callback::from_fn(move |s| {
cb(s, &content, cursor);
})
});
EventResult::Consumed(cb)
}

View File

@ -1,6 +1,5 @@
use owning_ref::{RcRef, OwningHandle};
use owning_ref::{OwningHandle, RcRef};
use std::any::Any;
use std::cell::{RefCell, RefMut};
use std::rc::Rc;
use view::{Selector, View, ViewWrapper};
@ -60,14 +59,15 @@ impl<T: View + 'static> ViewWrapper for IdView<T> {
fn wrap_call_on_any<'a>(
&mut self, selector: &Selector,
mut callback: Box<for<'b> FnMut(&'b mut Any) + 'a>
mut callback: Box<for<'b> FnMut(&'b mut Any) + 'a>,
) {
match selector {
&Selector::Id(id) if id == self.id => callback(self),
s => {
self.view.try_borrow_mut().ok().map(|mut v| {
v.call_on_any(s, callback)
});
self.view
.try_borrow_mut()
.ok()
.map(|mut v| v.call_on_any(s, callback));
}
}
}
@ -75,13 +75,10 @@ impl<T: View + 'static> ViewWrapper for IdView<T> {
fn wrap_focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
match selector {
&Selector::Id(id) if id == self.id => Ok(()),
s => {
self.view.try_borrow_mut().map_err(|_| ()).and_then(
|mut v| {
v.focus_view(s)
},
)
}
s => self.view
.try_borrow_mut()
.map_err(|_| ())
.and_then(|mut v| v.focus_view(s)),
}
}
}

View File

@ -3,7 +3,6 @@ use With;
use XY;
use direction;
use event::{Event, EventResult, Key};
use std::any::Any;
use std::cmp::min;
use vec::Vec2;
@ -108,8 +107,9 @@ impl LinearLayout {
Some(ref cache) => {
// Is our cache even valid?
// Also, is any child invalidating the layout?
if cache.zip_map(req, SizeCache::accept).both() &&
self.children_are_sleeping() {
if cache.zip_map(req, SizeCache::accept).both()
&& self.children_are_sleeping()
{
Some(cache.map(|s| s.value))
} else {
None
@ -126,10 +126,9 @@ impl LinearLayout {
}
/// Returns a cyclic mutable iterator starting with the child in focus
fn iter_mut<'a>(&'a mut self, from_focus: bool,
source: direction::Relative)
-> Box<Iterator<Item = (usize, &mut Child)> + 'a> {
fn iter_mut<'a>(
&'a mut self, from_focus: bool, source: direction::Relative
) -> Box<Iterator<Item = (usize, &mut Child)> + 'a> {
match source {
direction::Relative::Front => {
let start = if from_focus { self.focus } else { 0 };
@ -148,9 +147,8 @@ impl LinearLayout {
}
fn move_focus(&mut self, source: direction::Direction) -> EventResult {
let i = if let Some(i) = source.relative(self.orientation)
.and_then(|rel| {
let i = if let Some(i) =
source.relative(self.orientation).and_then(|rel| {
// The iterator starts at the focused element.
// We don't want that one.
self.iter_mut(true, rel)
@ -167,8 +165,9 @@ impl LinearLayout {
}
}
fn try_focus((i, child): (usize, &mut Child), source: direction::Direction)
-> Option<usize> {
fn try_focus(
(i, child): (usize, &mut Child), source: direction::Direction
) -> Option<usize> {
if child.view.take_focus(source) {
Some(i)
} else {
@ -187,8 +186,8 @@ impl View for LinearLayout {
// On the axis given by the orientation,
// add the child size to the offset.
*self.orientation.get_ref(&mut offset) += self.orientation
.get(&child.size);
*self.orientation.get_ref(&mut offset) +=
self.orientation.get(&child.size);
}
}
@ -257,10 +256,12 @@ impl View for LinearLayout {
if !desperate.fits_in(req) {
// Just give up...
// TODO: hard-cut
cap(self.children
cap(
self.children
.iter_mut()
.map(|c| c.size.get_mut(orientation)),
*req.get(self.orientation));
*req.get(self.orientation),
);
// TODO: print some error message or something
debug!("Seriously? {:?} > {:?}???", desperate, req);
@ -276,7 +277,8 @@ impl View for LinearLayout {
// Here, we have to make a compromise between the ideal
// and the desperate solutions.
let mut overweight: Vec<(usize, usize)> = sizes.iter()
let mut overweight: Vec<(usize, usize)> = sizes
.iter()
.map(|v| self.orientation.get(v))
.zip(min_sizes.iter().map(|v| self.orientation.get(v)))
.map(|(a, b)| a.saturating_sub(b))
@ -305,7 +307,8 @@ impl View for LinearLayout {
debug!("Allocations: {:?}", allocations);
// Final lengths are the minimum ones + generous allocations
let final_lengths: Vec<Vec2> = min_sizes.iter()
let final_lengths: Vec<Vec2> = min_sizes
.iter()
.map(|v| self.orientation.get(v))
.zip(allocations.iter())
.map(|(a, b)| a + b)
@ -335,11 +338,12 @@ impl View for LinearLayout {
// In what order will we iterate on the children?
let rel = source.relative(self.orientation);
// We activate from_focus only if coming from the "sides".
let i = if let Some(i) = self.iter_mut(rel.is_none(),
rel.unwrap_or(direction::Relative::Front))
.filter_map(|p| try_focus(p, source))
.next() {
let i = if let Some(i) = self.iter_mut(
rel.is_none(),
rel.unwrap_or(direction::Relative::Front),
).filter_map(|p| try_focus(p, source))
.next()
{
// ... we can't update `self.focus` here,
// because rustc thinks we still borrow `self`.
// :(
@ -354,51 +358,53 @@ impl View for LinearLayout {
fn on_event(&mut self, event: Event) -> EventResult {
match self.children[self.focus].view.on_event(event.clone()) {
EventResult::Ignored => {
match event {
Event::Shift(Key::Tab) if self.focus > 0 => {
self.move_focus(direction::Direction::back())
}
Event::Key(Key::Tab) if self.focus + 1 <
self.children.len() => {
self.move_focus(direction::Direction::front())
}
Event::Key(Key::Left)
if self.orientation ==
direction::Orientation::Horizontal &&
self.focus > 0 => {
self.move_focus(direction::Direction::right())
}
Event::Key(Key::Up) if self.orientation ==
direction::Orientation::Vertical &&
self.focus > 0 => {
self.move_focus(direction::Direction::down())
}
Event::Key(Key::Right)
if self.orientation ==
direction::Orientation::Horizontal &&
self.focus + 1 <
self.children.len() => {
self.move_focus(direction::Direction::left())
}
Event::Key(Key::Down)
if self.orientation ==
direction::Orientation::Vertical &&
self.focus + 1 <
self.children.len() => {
self.move_focus(direction::Direction::up())
}
_ => EventResult::Ignored,
EventResult::Ignored => match event {
Event::Shift(Key::Tab) if self.focus > 0 => {
self.move_focus(direction::Direction::back())
}
}
Event::Key(Key::Tab)
if self.focus + 1 < self.children.len() =>
{
self.move_focus(direction::Direction::front())
}
Event::Key(Key::Left)
if self.orientation == direction::Orientation::Horizontal
&& self.focus > 0 =>
{
self.move_focus(direction::Direction::right())
}
Event::Key(Key::Up)
if self.orientation == direction::Orientation::Vertical
&& self.focus > 0 =>
{
self.move_focus(direction::Direction::down())
}
Event::Key(Key::Right)
if self.orientation == direction::Orientation::Horizontal
&& self.focus + 1 < self.children.len() =>
{
self.move_focus(direction::Direction::left())
}
Event::Key(Key::Down)
if self.orientation == direction::Orientation::Vertical
&& self.focus + 1 < self.children.len() =>
{
self.move_focus(direction::Direction::up())
}
_ => EventResult::Ignored,
},
res => res,
}
}
fn call_on_any<'a>(&mut self, selector: &Selector,
mut callback: Box<FnMut(&mut Any) + 'a>) {
fn call_on_any<'a>(
&mut self, selector: &Selector,
mut callback: Box<FnMut(&mut Any) + 'a>,
) {
for child in &mut self.children {
child.view.call_on_any(selector, Box::new(|any| callback(any)));
child
.view
.call_on_any(selector, Box::new(|any| callback(any)));
}
}

View File

@ -3,10 +3,8 @@ use Printer;
use With;
use direction;
use event::{Callback, Event, EventResult, Key};
use std::any::Any;
use std::rc::Rc;
use unicode_width::UnicodeWidthStr;
use vec::Vec2;
use view::ScrollBase;
@ -96,7 +94,8 @@ impl ListView {
/// Adds a view to the end of the list.
pub fn add_child<V: View + 'static>(&mut self, label: &str, mut view: V) {
view.take_focus(direction::Direction::none());
self.children.push(ListChild::Row(label.to_string(), Box::new(view)));
self.children
.push(ListChild::Row(label.to_string(), Box::new(view)));
}
/// Removes all children from this view.
@ -126,7 +125,8 @@ impl ListView {
/// Sets a callback to be used when an item is selected.
pub fn set_on_select<F>(&mut self, cb: F)
where F: Fn(&mut Cursive, &String) + 'static
where
F: Fn(&mut Cursive, &String) + 'static,
{
self.on_select = Some(Rc::new(cb));
}
@ -135,7 +135,8 @@ impl ListView {
///
/// Chainable variant.
pub fn on_select<F>(self, cb: F) -> Self
where F: Fn(&mut Cursive, &String) + 'static
where
F: Fn(&mut Cursive, &String) + 'static,
{
self.with(|s| s.set_on_select(cb))
}
@ -147,18 +148,14 @@ impl ListView {
self.focus
}
fn iter_mut<'a>(&'a mut self, from_focus: bool,
source: direction::Relative)
-> Box<Iterator<Item = (usize, &mut ListChild)> + 'a> {
fn iter_mut<'a>(
&'a mut self, from_focus: bool, source: direction::Relative
) -> Box<Iterator<Item = (usize, &mut ListChild)> + 'a> {
match source {
direction::Relative::Front => {
let start = if from_focus { self.focus } else { 0 };
Box::new(self.children
.iter_mut()
.enumerate()
.skip(start))
Box::new(self.children.iter_mut().enumerate().skip(start))
}
direction::Relative::Back => {
let end = if from_focus {
@ -171,19 +168,20 @@ impl ListView {
}
}
fn move_focus(&mut self, n: usize, source: direction::Direction)
-> EventResult {
let i = if let Some(i) =
source.relative(direction::Orientation::Vertical)
.and_then(|rel| {
// The iterator starts at the focused element.
// We don't want that one.
self.iter_mut(true, rel)
.skip(1)
.filter_map(|p| try_focus(p, source))
.take(n)
.last()
}) {
fn move_focus(
&mut self, n: usize, source: direction::Direction
) -> EventResult {
let i = if let Some(i) = source
.relative(direction::Orientation::Vertical)
.and_then(|rel| {
// The iterator starts at the focused element.
// We don't want that one.
self.iter_mut(true, rel)
.skip(1)
.filter_map(|p| try_focus(p, source))
.take(n)
.last()
}) {
i
} else {
return EventResult::Ignored;
@ -199,19 +197,16 @@ impl ListView {
}
}
fn try_focus((i, child): (usize, &mut ListChild),
source: direction::Direction)
-> Option<usize> {
fn try_focus(
(i, child): (usize, &mut ListChild), source: direction::Direction
) -> Option<usize> {
match *child {
ListChild::Delimiter => None,
ListChild::Row(_, ref mut view) => {
if view.take_focus(source) {
Some(i)
} else {
None
}
}
ListChild::Row(_, ref mut view) => if view.take_focus(source) {
Some(i)
} else {
None
},
}
}
@ -229,13 +224,14 @@ impl View for ListView {
.unwrap_or(0) + 1;
debug!("Offset: {}", offset);
self.scrollbase.draw(printer, |printer, i| match self.children[i] {
ListChild::Row(ref label, ref view) => {
printer.print((0, 0), label);
view.draw(&printer.offset((offset, 0), i == self.focus));
}
ListChild::Delimiter => (),
});
self.scrollbase
.draw(printer, |printer, i| match self.children[i] {
ListChild::Row(ref label, ref view) => {
printer.print((0, 0), label);
view.draw(&printer.offset((offset, 0), i == self.focus));
}
ListChild::Delimiter => (),
});
}
fn required_size(&mut self, req: Vec2) -> Vec2 {
@ -275,8 +271,8 @@ impl View for ListView {
let spacing = 1;
let scrollbar_width = if self.children.len() > size.y { 2 } else { 0 };
let available = size.x.saturating_sub(label_width + spacing +
scrollbar_width);
let available = size.x
.saturating_sub(label_width + spacing + scrollbar_width);
debug!("Available: {}", available);
@ -310,15 +306,17 @@ impl View for ListView {
Event::Key(Key::PageDown) => {
self.move_focus(10, direction::Direction::up())
}
Event::Key(Key::Home) |
Event::Ctrl(Key::Home) => {
self.move_focus(usize::max_value(),
direction::Direction::back())
Event::Key(Key::Home) | Event::Ctrl(Key::Home) => {
self.move_focus(
usize::max_value(),
direction::Direction::back(),
)
}
Event::Key(Key::End) |
Event::Ctrl(Key::End) => {
self.move_focus(usize::max_value(),
direction::Direction::front())
Event::Key(Key::End) | Event::Ctrl(Key::End) => {
self.move_focus(
usize::max_value(),
direction::Direction::front(),
)
}
Event::Key(Key::Tab) => {
self.move_focus(1, direction::Direction::front())
@ -332,11 +330,12 @@ impl View for ListView {
fn take_focus(&mut self, source: direction::Direction) -> bool {
let rel = source.relative(direction::Orientation::Vertical);
let i = if let Some(i) =
self.iter_mut(rel.is_none(),
rel.unwrap_or(direction::Relative::Front))
.filter_map(|p| try_focus(p, source))
.next() {
let i = if let Some(i) = self.iter_mut(
rel.is_none(),
rel.unwrap_or(direction::Relative::Front),
).filter_map(|p| try_focus(p, source))
.next()
{
i
} else {
// No one wants to be in focus
@ -347,8 +346,10 @@ impl View for ListView {
true
}
fn call_on_any<'a>(&mut self, selector: &Selector,
mut callback: Box<FnMut(&mut Any) + 'a>) {
fn call_on_any<'a>(
&mut self, selector: &Selector,
mut callback: Box<FnMut(&mut Any) + 'a>,
) {
for view in self.children.iter_mut().filter_map(ListChild::view) {
view.call_on_any(selector, Box::new(|any| callback(any)));
}
@ -356,13 +357,12 @@ impl View for ListView {
fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
if let Some(i) = self.children
.iter_mut()
.enumerate()
.filter_map(|(i, v)| v.view().map(|v| (i, v)))
.filter_map(|(i, v)| {
v.focus_view(selector).ok().map(|_| i)
})
.next() {
.iter_mut()
.enumerate()
.filter_map(|(i, v)| v.view().map(|v| (i, v)))
.filter_map(|(i, v)| v.focus_view(selector).ok().map(|_| i))
.next()
{
self.focus = i;
Ok(())
} else {

View File

@ -8,7 +8,6 @@ use event::{Callback, Event, EventResult, Key};
use menu::{MenuItem, MenuTree};
use std::cmp::min;
use std::rc::Rc;
use unicode_width::UnicodeWidthStr;
use vec::Vec2;
use view::{Position, ScrollBase, View};
@ -139,31 +138,32 @@ impl MenuPopup {
fn make_subtree_cb(&self, tree: &Rc<MenuTree>) -> EventResult {
let tree = tree.clone();
let max_width = 4 +
self.menu
.children
.iter()
.map(Self::item_width)
.max()
.unwrap_or(1);
let max_width = 4
+ self.menu
.children
.iter()
.map(Self::item_width)
.max()
.unwrap_or(1);
let offset = Vec2::new(max_width, self.focus);
let action_cb = self.on_action.clone();
EventResult::with_cb(move |s| {
let action_cb = action_cb.clone();
s.screen_mut()
.add_layer_at(Position::parent(offset),
OnEventView::new(MenuPopup::new(tree.clone())
.on_action(move |s| {
// This will happen when the subtree popup
// activates something;
// First, remove ourselve.
s.pop_layer();
if let Some(ref action_cb) = action_cb {
action_cb.clone()(s);
}
}))
.on_event(Key::Left, |s| s.pop_layer()));
s.screen_mut().add_layer_at(
Position::parent(offset),
OnEventView::new(
MenuPopup::new(tree.clone()).on_action(move |s| {
// This will happen when the subtree popup
// activates something;
// First, remove ourselve.
s.pop_layer();
if let Some(ref action_cb) = action_cb {
action_cb.clone()(s);
}
}),
).on_event(Key::Left, |s| s.pop_layer()),
);
})
}
}
@ -211,20 +211,19 @@ impl View for MenuPopup {
printer.print((2, 0), label);
}
}
});
});
}
fn required_size(&mut self, req: Vec2) -> Vec2 {
// We can't really shrink our items here, so it's not flexible.
let w = 4 +
self.menu
.children
.iter()
.map(Self::item_width)
.max()
.unwrap_or(1);
let w = 4
+ self.menu
.children
.iter()
.map(Self::item_width)
.max()
.unwrap_or(1);
let h = 2 + self.menu.children.len();
@ -253,45 +252,42 @@ impl View for MenuPopup {
Event::Key(Key::Home) => self.focus = 0,
Event::Key(Key::End) => {
self.focus = self.menu
.children
.len()
.saturating_sub(1)
self.focus = self.menu.children.len().saturating_sub(1)
}
Event::Key(Key::Right) if self.menu.children[self.focus]
.is_subtree() => {
Event::Key(Key::Right)
if self.menu.children[self.focus].is_subtree() =>
{
return match self.menu.children[self.focus] {
MenuItem::Subtree(_, ref tree) => {
self.make_subtree_cb(tree)
}
_ => panic!("Not a subtree???"),
};
MenuItem::Subtree(_, ref tree) => {
self.make_subtree_cb(tree)
}
_ => panic!("Not a subtree???"),
};
}
Event::Key(Key::Enter) if !self.menu.children[self.focus]
.is_delimiter() => {
Event::Key(Key::Enter)
if !self.menu.children[self.focus].is_delimiter() =>
{
return match self.menu.children[self.focus] {
MenuItem::Leaf(_, ref cb) => {
let cb = cb.clone();
let action_cb = self.on_action.clone();
EventResult::with_cb(move |s| {
// Remove ourselves from the face of the earth
s.pop_layer();
// If we had prior orders, do it now.
if let Some(ref action_cb) = action_cb {
action_cb.clone()(s);
}
// And transmit his last words.
cb.clone()(s);
})
}
MenuItem::Subtree(_, ref tree) => {
self.make_subtree_cb(tree)
}
_ => panic!("No delimiter here"),
};
MenuItem::Leaf(_, ref cb) => {
let cb = cb.clone();
let action_cb = self.on_action.clone();
EventResult::with_cb(move |s| {
// Remove ourselves from the face of the earth
s.pop_layer();
// If we had prior orders, do it now.
if let Some(ref action_cb) = action_cb {
action_cb.clone()(s);
}
// And transmit his last words.
cb.clone()(s);
})
}
MenuItem::Subtree(_, ref tree) => {
self.make_subtree_cb(tree)
}
_ => panic!("No delimiter here"),
};
}
_ => return EventResult::Ignored,
@ -303,7 +299,7 @@ impl View for MenuPopup {
}
fn layout(&mut self, size: Vec2) {
self.scrollbase.set_heights(size.y.saturating_sub(2),
self.menu.children.len());
self.scrollbase
.set_heights(size.y.saturating_sub(2), self.menu.children.len());
}
}

View File

@ -3,14 +3,12 @@ use Printer;
use direction;
use event::*;
use menu::MenuTree;
use std::rc::Rc;
use theme::ColorStyle;
use unicode_width::UnicodeWidthStr;
use vec::Vec2;
use view::{Position, View};
use views::{OnEventView, MenuPopup};
use views::{MenuPopup, OnEventView};
/// Current state of the menubar
#[derive(PartialEq, Debug)]
@ -82,8 +80,9 @@ impl Menubar {
}
/// Insert a new item at the given position.
pub fn insert_subtree(&mut self, i: usize, title: &str, menu: MenuTree)
-> &mut Self {
pub fn insert_subtree(
&mut self, i: usize, title: &str, menu: MenuTree
) -> &mut Self {
self.menus.insert(i, (title.to_string(), Rc::new(menu)));
self
}
@ -108,7 +107,9 @@ impl Menubar {
///
/// Returns `None` if `i > self.len()`
pub fn get_subtree(&mut self, i: usize) -> Option<&mut MenuTree> {
self.menus.get_mut(i).map(|&mut (_, ref mut tree)| Rc::make_mut(tree))
self.menus
.get_mut(i)
.map(|&mut (_, ref mut tree)| Rc::make_mut(tree))
}
/// Looks for an item with the given label.
@ -143,37 +144,35 @@ fn show_child(s: &mut Cursive, offset: (usize, usize), menu: Rc<MenuTree>) {
// (If the view itself listens for a `left` or `right` press, it will
// consume it before our OnEventView. This means sub-menus can properly
// be entered.)
s.screen_mut()
.add_layer_at(Position::absolute(offset),
OnEventView::new(MenuPopup::new(menu)
.on_dismiss(|s| {
s.select_menubar()
})
.on_action(|s| {
s.menubar().state =
State::Inactive
}))
.on_event(Key::Right, |s| {
s.screen_mut().add_layer_at(
Position::absolute(offset),
OnEventView::new(
MenuPopup::new(menu)
.on_dismiss(|s| s.select_menubar())
.on_action(|s| s.menubar().state = State::Inactive),
).on_event(Key::Right, |s| {
s.pop_layer();
s.select_menubar();
// Act as if we sent "Right" then "Down"
s.menubar().on_event(Event::Key(Key::Right)).process(s);
if let EventResult::Consumed(Some(cb)) =
s.menubar().on_event(Event::Key(Key::Down)) {
s.menubar().on_event(Event::Key(Key::Down))
{
cb(s);
}
})
.on_event(Key::Left, |s| {
s.pop_layer();
s.select_menubar();
// Act as if we sent "Left" then "Down"
s.menubar().on_event(Event::Key(Key::Left)).process(s);
if let EventResult::Consumed(Some(cb)) =
s.menubar().on_event(Event::Key(Key::Down)) {
cb(s);
}
}));
.on_event(Key::Left, |s| {
s.pop_layer();
s.select_menubar();
// Act as if we sent "Left" then "Down"
s.menubar().on_event(Event::Key(Key::Left)).process(s);
if let EventResult::Consumed(Some(cb)) =
s.menubar().on_event(Event::Key(Key::Down))
{
cb(s);
}
}),
);
}
impl View for Menubar {
@ -188,8 +187,8 @@ impl View for Menubar {
for (i, &(ref title, _)) in self.menus.iter().enumerate() {
// We don't want to show HighlightInactive when we're not selected,
// because it's ugly on the menubar.
let selected = (self.state != State::Inactive) &&
(i == self.focus);
let selected =
(self.state != State::Inactive) && (i == self.focus);
printer.with_selection(selected, |printer| {
printer.print((offset, 0), &format!(" {} ", title));
offset += title.width() + 2;
@ -203,39 +202,33 @@ impl View for Menubar {
self.hide();
return EventResult::with_cb(|s| s.clear());
}
Event::Key(Key::Left) => {
if self.focus > 0 {
self.focus -= 1
} else {
self.focus = self.menus.len() - 1
}
}
Event::Key(Key::Right) => {
if self.focus + 1 < self.menus.len() {
self.focus += 1
} else {
self.focus = 0
}
}
Event::Key(Key::Down) |
Event::Key(Key::Enter) => {
Event::Key(Key::Left) => if self.focus > 0 {
self.focus -= 1
} else {
self.focus = self.menus.len() - 1
},
Event::Key(Key::Right) => if self.focus + 1 < self.menus.len() {
self.focus += 1
} else {
self.focus = 0
},
Event::Key(Key::Down) | Event::Key(Key::Enter) => {
// First, we need a new Rc to send the callback,
// since we don't know when it will be called.
let menu = self.menus[self.focus].1.clone();
self.state = State::Submenu;
let offset =
(self.menus[..self.focus]
.iter()
.map(|&(ref title, _)| title.width() + 2)
.fold(0, |a, b| a + b),
if self.autohide { 1 } else { 0 });
let offset = (
self.menus[..self.focus]
.iter()
.map(|&(ref title, _)| title.width() + 2)
.fold(0, |a, b| a + b),
if self.autohide { 1 } else { 0 },
);
// Since the closure will be called multiple times,
// we also need a new Rc on every call.
return EventResult::with_cb(move |s| {
show_child(s,
offset,
menu.clone())
});
return EventResult::with_cb(
move |s| show_child(s, offset, menu.clone()),
);
}
_ => return EventResult::Ignored,
}

View File

@ -69,15 +69,15 @@ pub use self::dialog::Dialog;
pub use self::dummy::DummyView;
pub use self::edit_view::EditView;
pub use self::id_view::{IdView, ViewRef};
pub use self::on_event_view::OnEventView;
pub use self::layer::Layer;
pub use self::linear_layout::LinearLayout;
pub use self::list_view::{ListChild, ListView};
pub use self::menu_popup::MenuPopup;
pub use self::menubar::Menubar;
pub use self::on_event_view::OnEventView;
pub use self::panel::Panel;
pub use self::progress_bar::{Counter, ProgressBar};
pub use self::radio::{RadioGroup, RadioButton};
pub use self::radio::{RadioButton, RadioGroup};
pub use self::select_view::SelectView;
pub use self::shadow_view::ShadowView;
pub use self::sized_view::SizedView;

View File

@ -2,7 +2,6 @@ use Cursive;
use With;
use event::{Callback, Event, EventResult};
use std::collections::HashMap;
use std::rc::Rc;
use view::{View, ViewWrapper};

View File

@ -27,9 +27,11 @@ impl<V: View> ViewWrapper for Panel<V> {
fn wrap_draw(&self, printer: &Printer) {
printer.print_box((0, 0), printer.size, true);
self.view.draw(&printer.sub_printer((1, 1),
printer.size.saturating_sub((2, 2)),
true));
self.view.draw(&printer.sub_printer(
(1, 1),
printer.size.saturating_sub((2, 2)),
true,
));
}
fn wrap_layout(&mut self, size: Vec2) {

View File

@ -5,7 +5,6 @@ use align::HAlign;
use std::cmp;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use theme::{ColorStyle, Effect};
use view::View;
@ -118,14 +117,17 @@ impl ProgressBar {
pub fn start<F: FnOnce(Counter) + Send + 'static>(&mut self, f: F) {
let counter: Counter = self.value.clone();
thread::spawn(move || { f(counter); });
thread::spawn(move || {
f(counter);
});
}
/// Starts a function in a separate thread, and monitor the progress.
///
/// Chainable variant.
pub fn with_task<F: FnOnce(Counter) + Send + 'static>(mut self, task: F)
-> Self {
pub fn with_task<F: FnOnce(Counter) + Send + 'static>(
mut self, task: F
) -> Self {
self.start(task);
self
}
@ -143,9 +145,9 @@ impl ProgressBar {
/// format!("{} %", percent)
/// }
/// ```
pub fn with_label<F: Fn(usize, (usize, usize)) -> String + 'static>
(mut self, label_maker: F)
-> Self {
pub fn with_label<F: Fn(usize, (usize, usize)) -> String + 'static>(
mut self, label_maker: F
) -> Self {
self.label_maker = Box::new(label_maker);
self
}

View File

@ -1,7 +1,6 @@
use {Printer, With};
use direction::Direction;
use event::{Event, EventResult, Key};
use std::cell::RefCell;
use std::rc::Rc;
use theme::ColorStyle;
@ -32,7 +31,7 @@ pub struct RadioGroup<T> {
state: Rc<RefCell<SharedState<T>>>,
}
impl <T> Default for RadioGroup<T> {
impl<T> Default for RadioGroup<T> {
fn default() -> Self {
Self::new()
}
@ -52,8 +51,9 @@ impl<T> RadioGroup<T> {
/// Adds a new button to the group.
///
/// The button will display `label` next to it, and will embed `value`.
pub fn button<S: Into<String>>(&mut self, value: T, label: S)
-> RadioButton<T> {
pub fn button<S: Into<String>>(
&mut self, value: T, label: S
) -> RadioButton<T> {
let count = self.state.borrow().values.len();
self.state.borrow_mut().values.push(Rc::new(value));
RadioButton::new(self.state.clone(), count, label.into())
@ -74,8 +74,9 @@ impl<T> RadioGroup<T> {
impl RadioGroup<String> {
/// Adds a button, using the label itself as value.
pub fn button_str<S: Into<String>>(&mut self, text: S)
-> RadioButton<String> {
pub fn button_str<S: Into<String>>(
&mut self, text: S
) -> RadioButton<String> {
let text = text.into();
self.button(text.clone(), text)
}
@ -103,8 +104,9 @@ pub struct RadioButton<T> {
impl<T> RadioButton<T> {
impl_enabled!(self.enabled);
fn new(state: Rc<RefCell<SharedState<T>>>, id: usize, label: String)
-> Self {
fn new(
state: Rc<RefCell<SharedState<T>>>, id: usize, label: String
) -> Self {
RadioButton {
state: state,
id: id,
@ -159,18 +161,21 @@ impl<T> View for RadioButton<T> {
fn draw(&self, printer: &Printer) {
if self.enabled {
printer.with_selection(printer.focused,
|printer| self.draw_internal(printer));
printer.with_selection(
printer.focused,
|printer| self.draw_internal(printer),
);
} else {
printer.with_color(ColorStyle::Secondary,
|printer| self.draw_internal(printer));
printer.with_color(
ColorStyle::Secondary,
|printer| self.draw_internal(printer),
);
}
}
fn on_event(&mut self, event: Event) -> EventResult {
match event {
Event::Key(Key::Enter) |
Event::Char(' ') => {
Event::Key(Key::Enter) | Event::Char(' ') => {
self.select();
EventResult::Consumed(None)
}

View File

@ -1,5 +1,4 @@
use {Cursive, Printer};
use With;
use direction::{Direction, Orientation};
use event::{Callback, Event, EventResult, Key};
@ -55,12 +54,15 @@ impl SliderView {
///
/// Chainable variant.
pub fn value(self, value: usize) -> Self {
self.with(|s| { s.set_value(value); })
self.with(|s| {
s.set_value(value);
})
}
/// Sets a callback to be called when the slider is moved.
pub fn on_change<F>(mut self, callback: F) -> Self
where F: Fn(&mut Cursive, usize) + 'static
where
F: Fn(&mut Cursive, usize) + 'static,
{
self.on_change = Some(Rc::new(callback));
self
@ -68,7 +70,8 @@ impl SliderView {
/// Sets a callback to be called when the <Enter> key is pressed.
pub fn on_enter<F>(mut self, callback: F) -> Self
where F: Fn(&mut Cursive, usize) + 'static
where
F: Fn(&mut Cursive, usize) + 'static,
{
self.on_enter = Some(Rc::new(callback));
self
@ -77,7 +80,9 @@ impl SliderView {
fn get_change_result(&self) -> EventResult {
EventResult::Consumed(self.on_change.clone().map(|cb| {
let value = self.value;
Callback::from_fn(move |s| { cb(s, value); })
Callback::from_fn(move |s| {
cb(s, value);
})
}))
}
@ -127,24 +132,32 @@ impl View for SliderView {
fn on_event(&mut self, event: Event) -> EventResult {
match event {
Event::Key(Key::Left) if self.orientation ==
Orientation::Horizontal => {
Event::Key(Key::Left)
if self.orientation == Orientation::Horizontal =>
{
self.slide_minus()
}
Event::Key(Key::Right) if self.orientation ==
Orientation::Horizontal => {
Event::Key(Key::Right)
if self.orientation == Orientation::Horizontal =>
{
self.slide_plus()
}
Event::Key(Key::Up) if self.orientation ==
Orientation::Vertical => self.slide_minus(),
Event::Key(Key::Down) if self.orientation ==
Orientation::Vertical => {
Event::Key(Key::Up)
if self.orientation == Orientation::Vertical =>
{
self.slide_minus()
}
Event::Key(Key::Down)
if self.orientation == Orientation::Vertical =>
{
self.slide_plus()
}
Event::Key(Key::Enter) if self.on_enter.is_some() => {
let value = self.value;
let cb = self.on_enter.clone().unwrap();
EventResult::with_cb(move |s| { cb(s, value); })
EventResult::with_cb(move |s| {
cb(s, value);
})
}
_ => EventResult::Ignored,
}

View File

@ -7,7 +7,7 @@ use odds::vec::VecExt;
use theme::{ColorStyle, Effect};
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use utils::{LinesIterator, Row, prefix};
use utils::{prefix, LinesIterator, Row};
use vec::Vec2;
use view::{ScrollBase, SizeCache, View};
@ -184,9 +184,8 @@ impl TextArea {
// If the current line is full, adding a character will overflow into the next line. To
// show that, we need to add a fake "ghost" row, just for the cursor.
fn fix_ghost_row(&mut self) {
if self.rows.is_empty() ||
self.rows.last().unwrap().end != self.content.len()
if self.rows.is_empty()
|| self.rows.last().unwrap().end != self.content.len()
{
// Add a fake, empty row at the end.
self.rows.push(Row {
@ -267,7 +266,6 @@ impl TextArea {
}
fn insert(&mut self, ch: char) {
// First, we inject the data, but keep the cursor unmoved
// (So the cursor is to the left of the injected char)
self.content.insert(self.cursor, ch);
@ -313,17 +311,14 @@ impl TextArea {
// We don't need to go beyond a newline.
// If we don't find one, end of the text it is.
debug!("Cursor: {}", self.cursor);
let last_byte = self.content[self.cursor..].find('\n').map(|i| {
1 + i + self.cursor
});
let last_row = last_byte.map_or(self.rows.len(), |last_byte| {
self.row_at(last_byte)
});
let last_byte = self.content[self.cursor..]
.find('\n')
.map(|i| 1 + i + self.cursor);
let last_row = last_byte
.map_or(self.rows.len(), |last_byte| self.row_at(last_byte));
let last_byte = last_byte.unwrap_or_else(|| self.content.len());
debug!("Content: `{}` (len={})",
self.content,
self.content.len());
debug!("Content: `{}` (len={})", self.content, self.content.len());
debug!("start/end: {}/{}", first_byte, last_byte);
debug!("start/end rows: {}/{}", first_row, last_row);
@ -343,8 +338,8 @@ impl TextArea {
// How much did this add?
debug!("New rows: {:?}", new_rows);
debug!("{}-{}", first_row, last_row);
let new_row_count = self.rows.len() + new_rows.len() + first_row -
last_row;
let new_row_count =
self.rows.len() + new_rows.len() + first_row - last_row;
if !scrollable && new_row_count > size.y {
// We just changed scrollable status.
// This changes everything.
@ -376,7 +371,8 @@ impl View for TextArea {
debug!("{:?}", self.rows);
let scroll_width = if self.rows.len() > constraint.y { 1 } else { 0 };
Vec2::new(
scroll_width + 1 + self.rows.iter().map(|r| r.width).max().unwrap_or(1),
scroll_width + 1
+ self.rows.iter().map(|r| r.width).max().unwrap_or(1),
self.rows.len(),
)
}
@ -408,24 +404,23 @@ impl View for TextArea {
debug!("row: {:?}", row);
let text = &self.content[row.start..row.end];
debug!("row text: `{}`", text);
printer.with_effect(
effect,
|printer| { printer.print((0, 0), text); },
);
printer.with_effect(effect, |printer| {
printer.print((0, 0), text);
});
if printer.focused && i == self.selected_row() {
let cursor_offset = self.cursor - row.start;
let c = if cursor_offset == text.len() {
"_"
} else {
text[cursor_offset..].graphemes(true).next().expect(
"Found no char!",
)
text[cursor_offset..]
.graphemes(true)
.next()
.expect("Found no char!")
};
let offset = text[..cursor_offset].width();
printer.print((offset, 0), c);
}
});
});
}
@ -442,8 +437,8 @@ impl View for TextArea {
Event::Key(Key::End) => {
let row = self.selected_row();
self.cursor = self.rows[row].end;
if row + 1 < self.rows.len() &&
self.cursor == self.rows[row + 1].start
if row + 1 < self.rows.len()
&& self.cursor == self.rows[row + 1].start
{
self.move_left();
}
@ -455,7 +450,8 @@ impl View for TextArea {
}
Event::Key(Key::Up) if self.selected_row() > 0 => self.move_up(),
Event::Key(Key::Down)
if self.selected_row() + 1 < self.rows.len() => {
if self.selected_row() + 1 < self.rows.len() =>
{
self.move_down()
}
Event::Key(Key::PageUp) => self.page_up(),

View File

@ -1,7 +1,6 @@
use Printer;
use std::cell::Cell;
use vec::Vec2;
use view::{View, ViewWrapper};
use views::IdView;

View File

@ -1,9 +1,8 @@
use direction::Orientation;
use std::iter;
/// A generic structure with a value for each axis.
#[derive(Debug,Clone,Copy,PartialEq,Eq,Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct XY<T> {
/// X-axis value
pub x: T,