mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Rustfmt
This commit is contained in:
parent
74612c65e5
commit
a4ca7bbf1e
10
build.rs
10
build.rs
@ -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",
|
||||
]);
|
||||
}
|
||||
|
@ -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), "+");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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()),
|
||||
);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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" })
|
||||
});
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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};
|
||||
|
40
src/menu.rs
40
src/menu.rs
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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() {
|
||||
|
@ -10,6 +10,5 @@
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use With;
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use view::{Boxable, Finder, Identifiable, View};
|
||||
|
11
src/utf8.rs
11
src/utf8.rs
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use std::io::{self, Read};
|
||||
|
||||
use views::Counter;
|
||||
|
||||
/// Wrapper around a `Read` that reports the progress made.
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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>>() {
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -1,7 +1,6 @@
|
||||
use Printer;
|
||||
use std::cell::Cell;
|
||||
use vec::Vec2;
|
||||
|
||||
use view::{View, ViewWrapper};
|
||||
use views::IdView;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user