mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-12 20:23:35 +00:00
Apply rustfmt
This commit is contained in:
parent
08ab18608b
commit
4b095a0652
@ -37,8 +37,7 @@ impl backend::Backend for NcursesBackend {
|
||||
}
|
||||
|
||||
|
||||
fn init_color_style(style: theme::ColorStyle,
|
||||
foreground: &theme::Color,
|
||||
fn init_color_style(style: theme::ColorStyle, foreground: &theme::Color,
|
||||
background: &theme::Color) {
|
||||
// TODO: build the color on the spot
|
||||
|
||||
@ -86,7 +85,9 @@ impl backend::Backend for NcursesBackend {
|
||||
|
||||
// Is it a UTF-8 starting point?
|
||||
if 32 <= ch && ch < 0x100 && ch != 127 {
|
||||
event::Event::Char(utf8::read_char(ch as u8, || ncurses::getch() as u8).unwrap())
|
||||
event::Event::Char(utf8::read_char(ch as u8,
|
||||
|| ncurses::getch() as u8)
|
||||
.unwrap())
|
||||
} else {
|
||||
event::Event::Key(parse_ncurses_char(ch))
|
||||
}
|
||||
@ -110,12 +111,14 @@ fn parse_ncurses_char(ch: i32) -> event::Key {
|
||||
// Tab is '\t'
|
||||
9 => event::Key::Tab,
|
||||
// Treat '\n' and the numpad Enter the same
|
||||
10 | ncurses::KEY_ENTER => event::Key::Enter,
|
||||
10 |
|
||||
ncurses::KEY_ENTER => event::Key::Enter,
|
||||
// This is the escape key when pressed by itself.
|
||||
// When used for control sequences, it should have been caught earlier.
|
||||
27 => event::Key::Esc,
|
||||
// `Backspace` sends 127, but Ctrl-H sends `Backspace`
|
||||
127 | ncurses::KEY_BACKSPACE => event::Key::Backspace,
|
||||
127 |
|
||||
ncurses::KEY_BACKSPACE => event::Key::Backspace,
|
||||
|
||||
410 => event::Key::Resize,
|
||||
|
||||
@ -204,7 +207,9 @@ fn parse_ncurses_char(ch: i32) -> event::Key {
|
||||
ncurses::KEY_SNEXT => event::Key::ShiftPageDown,
|
||||
ncurses::KEY_SPREVIOUS => event::Key::ShiftPageUp,
|
||||
// All Fn keys use the same enum with associated number
|
||||
f @ ncurses::KEY_F1...ncurses::KEY_F12 => event::Key::F((f - ncurses::KEY_F0) as u8),
|
||||
f @ ncurses::KEY_F1...ncurses::KEY_F12 => {
|
||||
event::Key::F((f - ncurses::KEY_F0) as u8)
|
||||
}
|
||||
f @ 277...288 => event::Key::ShiftF((f - 277) as u8),
|
||||
f @ 289...300 => event::Key::CtrlF((f - 289) as u8),
|
||||
f @ 301...312 => event::Key::CtrlShiftF((f - 300) as u8),
|
||||
@ -213,7 +218,9 @@ fn parse_ncurses_char(ch: i32) -> event::Key {
|
||||
//
|
||||
// TODO: shift and ctrl Fn keys
|
||||
// Avoids 8-10 (H,I,J), they are used by other commands.
|
||||
c @ 1...7 | c @ 11...25 => event::Key::CtrlChar((b'a' + (c - 1) as u8) as char),
|
||||
c @ 1...7 | c @ 11...25 => {
|
||||
event::Key::CtrlChar((b'a' + (c - 1) as u8) as char)
|
||||
}
|
||||
_ => event::Key::Unknown(ch),
|
||||
}
|
||||
}
|
||||
@ -234,8 +241,6 @@ fn find_closest(color: &theme::Color) -> u8 {
|
||||
let b = 6 * b as u16 / 256;
|
||||
(16 + 36 * r + 6 * g + b) as u8
|
||||
}
|
||||
theme::Color::RgbLowRes(r, g, b) => {
|
||||
(16 + 36 * r + 6 * g + b) as u8
|
||||
}
|
||||
theme::Color::RgbLowRes(r, g, b) => (16 + 36 * r + 6 * g + b) as u8,
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ mod curses;
|
||||
pub use self::curses::NcursesBackend;
|
||||
|
||||
pub trait Backend {
|
||||
|
||||
fn init();
|
||||
fn finish();
|
||||
|
||||
@ -16,8 +15,7 @@ pub trait Backend {
|
||||
|
||||
fn has_colors() -> bool;
|
||||
|
||||
fn init_color_style(style: theme::ColorStyle,
|
||||
foreground: &theme::Color,
|
||||
fn init_color_style(style: theme::ColorStyle, foreground: &theme::Color,
|
||||
background: &theme::Color);
|
||||
|
||||
fn print_at((usize, usize), &str);
|
||||
|
29
src/div.rs
29
src/div.rs
@ -1,16 +1,15 @@
|
||||
|
||||
/*
|
||||
/// Integer division that rounds up.
|
||||
pub fn div_up_usize(p: usize, q: usize) -> usize {
|
||||
div_up(p as u32, q as u32) as usize
|
||||
}
|
||||
|
||||
/// Integer division that rounds up.
|
||||
pub fn div_up(p: u32, q: u32) -> u32 {
|
||||
if p % q == 0 {
|
||||
p / q
|
||||
} else {
|
||||
1 + p / q
|
||||
}
|
||||
}
|
||||
*/
|
||||
// Integer division that rounds up.
|
||||
// pub fn div_up_usize(p: usize, q: usize) -> usize {
|
||||
// div_up(p as u32, q as u32) as usize
|
||||
// }
|
||||
//
|
||||
// Integer division that rounds up.
|
||||
// pub fn div_up(p: u32, q: u32) -> u32 {
|
||||
// if p % q == 0 {
|
||||
// p / q
|
||||
// } else {
|
||||
// 1 + p / q
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
28
src/lib.rs
28
src/lib.rs
@ -153,8 +153,10 @@ impl Cursive {
|
||||
true
|
||||
}
|
||||
|
||||
/// Regularly redraws everything, even when no input is given. Between 0 and 1000.
|
||||
/// Sets the refresh rate, in frames per second.
|
||||
///
|
||||
/// Regularly redraws everything, even when no input is given.
|
||||
/// Between 0 and 1000.
|
||||
/// Call with fps=0 to disable (default value).
|
||||
pub fn set_fps(&self, fps: u32) {
|
||||
B::set_refresh_rate(fps)
|
||||
@ -199,8 +201,8 @@ impl Cursive {
|
||||
/// Tries to find the view pointed to by the given path.
|
||||
/// If the view is not found, or if it is not of the asked type,
|
||||
/// it returns None.
|
||||
pub fn find<V: View + Any>(&mut self, selector: &Selector) -> Option<&mut V> {
|
||||
match self.find_any(selector) {
|
||||
pub fn find<V: View + Any>(&mut self, sel: &Selector) -> Option<&mut V> {
|
||||
match self.find_any(sel) {
|
||||
None => None,
|
||||
Some(b) => b.downcast_mut::<V>(),
|
||||
}
|
||||
@ -211,7 +213,9 @@ impl Cursive {
|
||||
self.find(&Selector::Id(id))
|
||||
}
|
||||
|
||||
/// Adds a global callback, triggered on the given key press when no view catches it.
|
||||
/// Adds a global callback.
|
||||
///
|
||||
/// Will be triggered on the given key press when no view catches it.
|
||||
pub fn add_global_callback<F, E: ToEvent>(&mut self, event: E, cb: F)
|
||||
where F: Fn(&mut Cursive) + 'static
|
||||
{
|
||||
@ -267,21 +271,26 @@ impl Cursive {
|
||||
};
|
||||
// Draw the menubar?
|
||||
if self.menubar.visible() {
|
||||
let printer = printer.sub_printer(Vec2::zero(), printer.size, self.menubar.receive_events());
|
||||
let printer = printer.sub_printer(Vec2::zero(),
|
||||
printer.size,
|
||||
self.menubar.receive_events());
|
||||
self.menubar.draw(&printer);
|
||||
}
|
||||
|
||||
let selected = self.menubar.receive_events();
|
||||
|
||||
self.screen_mut()
|
||||
.draw(&printer.sub_printer(Vec2::new(0, offset), printer.size, !selected));
|
||||
let printer =
|
||||
printer.sub_printer(Vec2::new(0, offset), printer.size, !selected);
|
||||
self.screen_mut().draw(&printer);
|
||||
|
||||
B::refresh();
|
||||
}
|
||||
|
||||
/// Runs the event loop.
|
||||
///
|
||||
/// It will wait for user input (key presses) and trigger callbacks accordingly.
|
||||
/// It will wait for user input (key presses)
|
||||
/// and trigger callbacks accordingly.
|
||||
///
|
||||
/// Blocks until quit() is called.
|
||||
pub fn run(&mut self) {
|
||||
|
||||
@ -315,7 +324,8 @@ impl Cursive {
|
||||
}
|
||||
} else {
|
||||
match self.screen_mut().on_event(event) {
|
||||
// If the event was ignored, it is our turn to play with it.
|
||||
// If the event was ignored,
|
||||
// it is our turn to play with it.
|
||||
EventResult::Ignored => self.on_event(event),
|
||||
EventResult::Consumed(None) => (),
|
||||
EventResult::Consumed(Some(cb)) => cb(self),
|
||||
|
@ -31,7 +31,7 @@ impl MenuItem {
|
||||
|
||||
pub fn is_subtree(&self) -> bool {
|
||||
match *self {
|
||||
MenuItem::Subtree(_,_) => true,
|
||||
MenuItem::Subtree(_, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,8 @@ impl MenuTree {
|
||||
self.with(|menu| menu.add_delimiter())
|
||||
}
|
||||
|
||||
pub fn add_leaf<F: 'static + Fn(&mut Cursive)>(&mut self, title: &str, cb: F) {
|
||||
pub fn add_leaf<F: 'static + Fn(&mut Cursive)>(&mut self, title: &str,
|
||||
cb: F) {
|
||||
self.children
|
||||
.push(MenuItem::Leaf(title.to_string(), Rc::new(cb)));
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use Cursive;
|
||||
use menu::MenuTree;
|
||||
use view::MenuPopup;
|
||||
use view::KeyEventView;
|
||||
@ -109,42 +110,18 @@ impl Menubar {
|
||||
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),
|
||||
.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 Some(Rc::new(move |s| {
|
||||
// Since the closure will be called multiple times,
|
||||
// we also need a new Rc on every call.
|
||||
s.screen_mut()
|
||||
.add_layer_at(Position::absolute(offset),
|
||||
KeyEventView::new(MenuPopup::new(menu.clone())
|
||||
.on_dismiss(|s| s.select_menubar())
|
||||
.on_action(|s| {
|
||||
s.menubar().state = State::Inactive
|
||||
}))
|
||||
.register(Key::Right, |s| {
|
||||
s.pop_layer();
|
||||
// Act as if we sent "Left" then "Enter"
|
||||
s.select_menubar();
|
||||
s.menubar().on_event(Event::Key(Key::Right));
|
||||
if let Some(cb) = s.menubar().on_event(Event::Key(Key::Down)) {
|
||||
cb(s);
|
||||
}
|
||||
})
|
||||
.register(Key::Left, |s| {
|
||||
s.pop_layer();
|
||||
// Act as if we sent "Left" then "Enter"
|
||||
s.select_menubar();
|
||||
s.menubar().on_event(Event::Key(Key::Left));
|
||||
if let Some(cb) = s.menubar().on_event(Event::Key(Key::Down)) {
|
||||
cb(s);
|
||||
}
|
||||
}));
|
||||
show_child(s, offset, menu.clone())
|
||||
}));
|
||||
}
|
||||
_ => (),
|
||||
@ -152,3 +129,34 @@ impl Menubar {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn show_child(s: &mut Cursive, offset: (usize, usize), menu: Rc<MenuTree>) {
|
||||
s.screen_mut()
|
||||
.add_layer_at(Position::absolute(offset),
|
||||
KeyEventView::new(MenuPopup::new(menu)
|
||||
.on_dismiss(|s| s.select_menubar())
|
||||
.on_action(|s| {
|
||||
s.menubar().state = State::Inactive
|
||||
}))
|
||||
.register(Key::Right, |s| {
|
||||
s.pop_layer();
|
||||
// Act as if we sent "Left" then "Enter"
|
||||
s.select_menubar();
|
||||
s.menubar().on_event(Event::Key(Key::Right));
|
||||
if let Some(cb) = s.menubar()
|
||||
.on_event(Event::Key(Key::Down)) {
|
||||
cb(s);
|
||||
}
|
||||
})
|
||||
.register(Key::Left, |s| {
|
||||
s.pop_layer();
|
||||
// Act as if we sent "Left" then "Enter"
|
||||
s.select_menubar();
|
||||
s.menubar().on_event(Event::Key(Key::Left));
|
||||
if let Some(cb) = s.menubar()
|
||||
.on_event(Event::Key(Key::Down)) {
|
||||
cb(s);
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
|
@ -44,8 +44,12 @@ impl Orientation {
|
||||
/// For a vertical view, returns (Max(x),Sum(y)).
|
||||
pub fn stack<'a, T: Iterator<Item = &'a Vec2>>(&self, iter: T) -> Vec2 {
|
||||
match *self {
|
||||
Orientation::Horizontal => iter.fold(Vec2::zero(), |a, b| a.stack_horizontal(b)),
|
||||
Orientation::Vertical => iter.fold(Vec2::zero(), |a, b| a.stack_vertical(b)),
|
||||
Orientation::Horizontal => {
|
||||
iter.fold(Vec2::zero(), |a, b| a.stack_horizontal(b))
|
||||
}
|
||||
Orientation::Vertical => {
|
||||
iter.fold(Vec2::zero(), |a, b| a.stack_vertical(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use backend::Backend;
|
||||
|
||||
use B;
|
||||
|
||||
use theme::{ColorStyle, Theme, Effect};
|
||||
use theme::{ColorStyle, Effect, Theme};
|
||||
use vec::{ToVec2, Vec2};
|
||||
|
||||
/// Convenient interface to draw on a subset of the screen.
|
||||
@ -136,20 +136,25 @@ impl Printer {
|
||||
|
||||
self.print_hline(start_v + (1, 0), size_v.x - 1, "─");
|
||||
self.print_vline(start_v + (0, 1), size_v.y - 1, "│");
|
||||
self.print_hline(start_v + (1, 0) + size_v.keep_y(), size_v.x - 1, "─");
|
||||
self.print_vline(start_v + (0, 1) + size_v.keep_x(), size_v.y - 1, "│");
|
||||
self.print_hline(start_v + (1, 0) + size_v.keep_y(),
|
||||
size_v.x - 1,
|
||||
"─");
|
||||
self.print_vline(start_v + (0, 1) + size_v.keep_x(),
|
||||
size_v.y - 1,
|
||||
"│");
|
||||
}
|
||||
|
||||
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);
|
||||
if self.focused {
|
||||
ColorStyle::Highlight
|
||||
} else {
|
||||
ColorStyle::HighlightInactive
|
||||
}
|
||||
} else {
|
||||
ColorStyle::Primary
|
||||
},
|
||||
f);
|
||||
}
|
||||
|
||||
pub fn print_hdelim<T: ToVec2>(&self, start: T, len: usize) {
|
||||
@ -160,7 +165,8 @@ impl Printer {
|
||||
}
|
||||
|
||||
/// Returns a printer on a subset of this one's area.
|
||||
pub fn sub_printer<S: ToVec2>(&self, offset: S, size: S, focused: bool) -> Printer {
|
||||
pub fn sub_printer<S: ToVec2>(&self, offset: S, size: S, focused: bool)
|
||||
-> Printer {
|
||||
let offset_v = offset.to_vec2();
|
||||
Printer {
|
||||
offset: self.offset + offset_v,
|
||||
|
67
src/theme.rs
67
src/theme.rs
@ -112,32 +112,32 @@ impl Theme {
|
||||
fn activate(&self) {
|
||||
// Initialize each color with the backend
|
||||
B::init_color_style(ColorStyle::Background,
|
||||
&self.colors.view,
|
||||
&self.colors.background);
|
||||
&self.colors.view,
|
||||
&self.colors.background);
|
||||
B::init_color_style(ColorStyle::Shadow,
|
||||
&self.colors.shadow,
|
||||
&self.colors.shadow);
|
||||
&self.colors.shadow,
|
||||
&self.colors.shadow);
|
||||
B::init_color_style(ColorStyle::Primary,
|
||||
&self.colors.primary,
|
||||
&self.colors.view);
|
||||
&self.colors.primary,
|
||||
&self.colors.view);
|
||||
B::init_color_style(ColorStyle::Secondary,
|
||||
&self.colors.secondary,
|
||||
&self.colors.view);
|
||||
&self.colors.secondary,
|
||||
&self.colors.view);
|
||||
B::init_color_style(ColorStyle::Tertiary,
|
||||
&self.colors.tertiary,
|
||||
&self.colors.view);
|
||||
&self.colors.tertiary,
|
||||
&self.colors.view);
|
||||
B::init_color_style(ColorStyle::TitlePrimary,
|
||||
&self.colors.title_primary,
|
||||
&self.colors.view);
|
||||
&self.colors.title_primary,
|
||||
&self.colors.view);
|
||||
B::init_color_style(ColorStyle::TitleSecondary,
|
||||
&self.colors.title_secondary,
|
||||
&self.colors.view);
|
||||
&self.colors.title_secondary,
|
||||
&self.colors.view);
|
||||
B::init_color_style(ColorStyle::Highlight,
|
||||
&self.colors.view,
|
||||
&self.colors.highlight);
|
||||
&self.colors.view,
|
||||
&self.colors.highlight);
|
||||
B::init_color_style(ColorStyle::HighlightInactive,
|
||||
&self.colors.view,
|
||||
&self.colors.highlight_inactive);
|
||||
&self.colors.view,
|
||||
&self.colors.highlight_inactive);
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,20 +207,25 @@ impl Palette {
|
||||
load_color(&mut self.title_primary, table.get("title_primary"));
|
||||
load_color(&mut self.title_secondary, table.get("title_secondary"));
|
||||
load_color(&mut self.highlight, table.get("highlight"));
|
||||
load_color(&mut self.highlight_inactive, table.get("highlight_inactive"));
|
||||
load_color(&mut self.highlight_inactive,
|
||||
table.get("highlight_inactive"));
|
||||
}
|
||||
}
|
||||
|
||||
fn load_color(target: &mut Color, value: Option<&toml::Value>) -> bool {
|
||||
if let Some(value) = value {
|
||||
match *value {
|
||||
toml::Value::String(ref value) => if let Some(color) = Color::parse(value) {
|
||||
*target = color;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
},
|
||||
toml::Value::Array(ref array) => array.iter().any(|item| load_color(target, Some(item))),
|
||||
toml::Value::String(ref value) => {
|
||||
if let Some(color) = Color::parse(value) {
|
||||
*target = color;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
toml::Value::Array(ref array) => {
|
||||
array.iter().any(|item| load_color(target, Some(item)))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
@ -245,8 +250,7 @@ pub enum Color {
|
||||
RgbLowRes(u8, u8, u8),
|
||||
}
|
||||
|
||||
impl Color {
|
||||
}
|
||||
impl Color {}
|
||||
|
||||
/// Possible error returned when loading a theme.
|
||||
#[derive(Debug)]
|
||||
@ -294,9 +298,12 @@ impl Color {
|
||||
Some(Color::Rgb(r as u8, g as u8, b as u8))
|
||||
} else if value.len() == 3 {
|
||||
// RGB values between 0 and 5 maybe?
|
||||
let rgb: Vec<_> = value.chars().map(|c| c as i16 - '0' as i16).collect();
|
||||
let rgb: Vec<_> =
|
||||
value.chars().map(|c| c as i16 - '0' as i16).collect();
|
||||
if rgb.iter().all(|&i| i >= 0 && i < 6) {
|
||||
Some(Color::RgbLowRes(rgb[0] as u8, rgb[1] as u8, rgb[2] as u8))
|
||||
Some(Color::RgbLowRes(rgb[0] as u8,
|
||||
rgb[1] as u8,
|
||||
rgb[2] as u8))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::rc::Rc;
|
||||
use theme::ColorStyle;
|
||||
use Cursive;
|
||||
use vec::Vec2;
|
||||
use view::{View};
|
||||
use view::View;
|
||||
use event::*;
|
||||
use printer::Printer;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
@ -51,7 +51,9 @@ impl View for Button {
|
||||
fn on_event(&mut self, event: Event) -> EventResult {
|
||||
match event {
|
||||
// 10 is the ascii code for '\n', that is the return key
|
||||
Event::Key(Key::Enter) => EventResult::Consumed(Some(self.callback.clone())),
|
||||
Event::Key(Key::Enter) => {
|
||||
EventResult::Consumed(Some(self.callback.clone()))
|
||||
}
|
||||
_ => EventResult::Ignored,
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,9 @@ impl EditView {
|
||||
&self.content
|
||||
}
|
||||
|
||||
/// Sets the current content to the given value. Convenient chainable method.
|
||||
/// Sets the current content to the given value.
|
||||
///
|
||||
/// Convenient chainable method.
|
||||
pub fn content(mut self, content: &str) -> Self {
|
||||
self.set_content(content);
|
||||
self
|
||||
@ -77,7 +79,6 @@ impl EditView {
|
||||
|
||||
impl View for EditView {
|
||||
fn draw(&mut self, printer: &Printer) {
|
||||
// let style = if focused { color::HIGHLIGHT } else { color::HIGHLIGHT_INACTIVE };
|
||||
assert!(printer.size.x == self.last_length);
|
||||
|
||||
let width = self.content.width();
|
||||
@ -92,16 +93,16 @@ impl View for EditView {
|
||||
} else {
|
||||
let content = &self.content[self.offset..];
|
||||
let display_bytes = content.graphemes(true)
|
||||
.scan(0, |w, g| {
|
||||
*w += g.width();
|
||||
if *w > self.last_length {
|
||||
None
|
||||
} else {
|
||||
Some(g)
|
||||
}
|
||||
})
|
||||
.map(|g| g.len())
|
||||
.fold(0, |a, b| a + b);
|
||||
.scan(0, |w, g| {
|
||||
*w += g.width();
|
||||
if *w > self.last_length {
|
||||
None
|
||||
} else {
|
||||
Some(g)
|
||||
}
|
||||
})
|
||||
.map(|g| g.len())
|
||||
.fold(0, |a, b| a + b);
|
||||
|
||||
let content = &content[..display_bytes];
|
||||
|
||||
@ -163,26 +164,26 @@ impl View for EditView {
|
||||
Key::End => self.cursor = self.content.len(),
|
||||
Key::Left if self.cursor > 0 => {
|
||||
let len = self.content[..self.cursor]
|
||||
.graphemes(true)
|
||||
.last()
|
||||
.unwrap()
|
||||
.len();
|
||||
.graphemes(true)
|
||||
.last()
|
||||
.unwrap()
|
||||
.len();
|
||||
self.cursor -= len;
|
||||
}
|
||||
Key::Right if self.cursor < self.content.len() => {
|
||||
let len = self.content[self.cursor..]
|
||||
.graphemes(true)
|
||||
.next()
|
||||
.unwrap()
|
||||
.len();
|
||||
.graphemes(true)
|
||||
.next()
|
||||
.unwrap()
|
||||
.len();
|
||||
self.cursor += len;
|
||||
}
|
||||
Key::Backspace if self.cursor > 0 => {
|
||||
let len = self.content[..self.cursor]
|
||||
.graphemes(true)
|
||||
.last()
|
||||
.unwrap()
|
||||
.len();
|
||||
.graphemes(true)
|
||||
.last()
|
||||
.unwrap()
|
||||
.len();
|
||||
self.cursor -= len;
|
||||
self.content.remove(self.cursor);
|
||||
}
|
||||
@ -196,18 +197,21 @@ impl View for EditView {
|
||||
|
||||
// Keep cursor in [offset, offset+last_length] by changing offset
|
||||
// So keep offset in [last_length-cursor,cursor]
|
||||
// Also call this on resize, but right now it is an event like any other
|
||||
// Also call this on resize,
|
||||
// but right now it is an event like any other
|
||||
if self.cursor < self.offset {
|
||||
self.offset = self.cursor;
|
||||
} else {
|
||||
// So we're against the right wall.
|
||||
// Let's find how much space will be taken by the selection (either a char, or _)
|
||||
// Let's find how much space will be taken by the selection
|
||||
// (either a char, or _)
|
||||
let c_len = self.content[self.cursor..]
|
||||
.graphemes(true)
|
||||
.map(|g| g.width())
|
||||
.next()
|
||||
.unwrap_or(1);
|
||||
// Now, we have to fit self.content[..self.cursor] into self.last_length - c_len.
|
||||
.graphemes(true)
|
||||
.map(|g| g.width())
|
||||
.next()
|
||||
.unwrap_or(1);
|
||||
// Now, we have to fit self.content[..self.cursor]
|
||||
// into self.last_length - c_len.
|
||||
let available = self.last_length - c_len;
|
||||
// Look at the content before the cursor (we will print its tail).
|
||||
// From the end, count the length until we reach `available`.
|
||||
|
@ -118,7 +118,7 @@ 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);
|
||||
.get(&child.size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,9 +127,9 @@ impl View for LinearLayout {
|
||||
// Look how mean we are: we offer the whole size to every child.
|
||||
// As if they could get it all.
|
||||
let min_sizes: Vec<Vec2> = self.children
|
||||
.iter_mut()
|
||||
.map(|child| Vec2::min(size, child.view.get_min_size(size)))
|
||||
.collect();
|
||||
.iter_mut()
|
||||
.map(|child| Vec2::min(size, child.view.get_min_size(size)))
|
||||
.collect();
|
||||
let min_size = self.orientation.stack(min_sizes.iter());
|
||||
|
||||
// Emulate 'non-strict inequality' on integers
|
||||
@ -150,11 +150,12 @@ impl View for LinearLayout {
|
||||
|
||||
|
||||
for (child, (child_size, extra)) in self.children
|
||||
.iter_mut()
|
||||
.zip(min_sizes.iter().zip(extras.iter())) {
|
||||
.iter_mut()
|
||||
.zip(min_sizes.iter().zip(extras.iter())) {
|
||||
let mut child_size = *child_size;
|
||||
*self.orientation.get_ref(&mut child_size) += *extra;
|
||||
*self.orientation.swap().get_ref(&mut child_size) = self.orientation.swap().get(&size);
|
||||
*self.orientation.swap().get_ref(&mut child_size) =
|
||||
self.orientation.swap().get(&size);
|
||||
child.size = child_size;
|
||||
child.view.layout(child_size);
|
||||
}
|
||||
@ -163,9 +164,9 @@ impl View for LinearLayout {
|
||||
fn get_min_size(&mut self, req: Vec2) -> Vec2 {
|
||||
// First, make a naive scenario: everything will work fine.
|
||||
let sizes: Vec<Vec2> = self.children
|
||||
.iter_mut()
|
||||
.map(|view| view.view.get_min_size(req))
|
||||
.collect();
|
||||
.iter_mut()
|
||||
.map(|view| view.view.get_min_size(req))
|
||||
.collect();
|
||||
self.orientation.stack(sizes.iter())
|
||||
|
||||
|
||||
@ -188,27 +189,34 @@ impl View for LinearLayout {
|
||||
self.focus -= 1;
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
Event::Key(Key::ShiftTab) if self.focus + 1 < self.children.len() => {
|
||||
Event::Key(Key::ShiftTab) if self.focus + 1 <
|
||||
self.children.len() => {
|
||||
self.focus += 1;
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
Event::Key(Key::Left) if self.orientation == Orientation::Horizontal &&
|
||||
Event::Key(Key::Left) if self.orientation ==
|
||||
Orientation::Horizontal &&
|
||||
self.focus > 0 => {
|
||||
self.focus -= 1;
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
Event::Key(Key::Up) if self.orientation == Orientation::Vertical &&
|
||||
Event::Key(Key::Up) if self.orientation ==
|
||||
Orientation::Vertical &&
|
||||
self.focus > 0 => {
|
||||
self.focus -= 1;
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
Event::Key(Key::Right) if self.orientation == Orientation::Horizontal &&
|
||||
self.focus + 1 < self.children.len() => {
|
||||
Event::Key(Key::Right) if self.orientation ==
|
||||
Orientation::Horizontal &&
|
||||
self.focus + 1 <
|
||||
self.children.len() => {
|
||||
self.focus += 1;
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
Event::Key(Key::Down) if self.orientation == Orientation::Vertical &&
|
||||
self.focus + 1 < self.children.len() => {
|
||||
Event::Key(Key::Down) if self.orientation ==
|
||||
Orientation::Vertical &&
|
||||
self.focus + 1 <
|
||||
self.children.len() => {
|
||||
self.focus += 1;
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
|
@ -96,28 +96,28 @@ impl MenuPopup {
|
||||
let tree = tree.clone();
|
||||
let max_width = 4 +
|
||||
self.menu
|
||||
.children
|
||||
.iter()
|
||||
.map(Self::item_width)
|
||||
.max()
|
||||
.unwrap_or(1);
|
||||
.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),
|
||||
KeyEventView::new(MenuPopup::new(tree.clone())
|
||||
.on_action(move |s| {
|
||||
// This will happen when the subtree popup
|
||||
// activates something;
|
||||
// First, remove ourselve.
|
||||
s.pop_layer();
|
||||
.add_layer_at(Position::parent(offset),
|
||||
KeyEventView::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);
|
||||
}
|
||||
}))
|
||||
.register(Key::Left, |s| s.pop_layer()));
|
||||
}))
|
||||
.register(Key::Left, |s| s.pop_layer()));
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -126,9 +126,8 @@ impl View for MenuPopup {
|
||||
fn draw(&mut self, printer: &Printer) {
|
||||
let h = self.menu.len();
|
||||
let offset = self.align.v.get_offset(h, printer.size.y);
|
||||
let printer = &printer.sub_printer(Vec2::new(0, offset),
|
||||
printer.size,
|
||||
true);
|
||||
let printer =
|
||||
&printer.sub_printer(Vec2::new(0, offset), printer.size, true);
|
||||
|
||||
// Start with a box
|
||||
printer.print_box(Vec2::new(0, 0), printer.size);
|
||||
@ -164,11 +163,11 @@ impl View for MenuPopup {
|
||||
// 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);
|
||||
.children
|
||||
.iter()
|
||||
.map(Self::item_width)
|
||||
.max()
|
||||
.unwrap_or(1);
|
||||
let h = 2 + self.menu.children.len();
|
||||
|
||||
|
||||
@ -203,7 +202,7 @@ impl View for MenuPopup {
|
||||
Event::Key(Key::End) => self.focus = self.menu.children.len() - 1,
|
||||
|
||||
Event::Key(Key::Right) if self.menu.children[self.focus]
|
||||
.is_subtree() => {
|
||||
.is_subtree() => {
|
||||
return match self.menu.children[self.focus] {
|
||||
MenuItem::Subtree(_, ref tree) => {
|
||||
self.make_subtree_cb(tree)
|
||||
@ -213,7 +212,7 @@ impl View for MenuPopup {
|
||||
};
|
||||
}
|
||||
Event::Key(Key::Enter) if !self.menu.children[self.focus]
|
||||
.is_delimiter() => {
|
||||
.is_delimiter() => {
|
||||
return match self.menu.children[self.focus] {
|
||||
MenuItem::Leaf(_, ref cb) => {
|
||||
|
||||
|
@ -34,7 +34,7 @@ use event::{Event, EventResult};
|
||||
use vec::Vec2;
|
||||
use printer::Printer;
|
||||
|
||||
pub use self::position::{Position, Offset};
|
||||
pub use self::position::{Offset, Position};
|
||||
|
||||
pub use self::scroll::ScrollBase;
|
||||
|
||||
@ -64,7 +64,7 @@ pub trait View {
|
||||
EventResult::Ignored
|
||||
}
|
||||
|
||||
/// Returns the minimum size the view requires under the given restrictions.
|
||||
/// Returns the minimum size the view requires with the given restrictions.
|
||||
fn get_min_size(&mut self, Vec2) -> Vec2 {
|
||||
Vec2::new(1, 1)
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ impl Position {
|
||||
Position::new(Offset::Parent(offset.x), Offset::Parent(offset.y))
|
||||
}
|
||||
|
||||
pub fn compute_offset(&self, size: Vec2, available: Vec2, parent: Vec2) -> Vec2 {
|
||||
pub fn compute_offset(&self, size: Vec2, available: Vec2, parent: Vec2)
|
||||
-> Vec2 {
|
||||
Vec2::new(self.x.compute_offset(size.x, available.x, parent.x),
|
||||
self.y.compute_offset(size.y, available.y, parent.y))
|
||||
}
|
||||
@ -40,14 +41,15 @@ pub enum Offset {
|
||||
/// Place top-left corner at the given absolute coordinates
|
||||
Absolute(usize),
|
||||
|
||||
/// Place top-left corner at the given offset from the previous layer's top-left corner.
|
||||
/// Offset from the previous layer's top-left corner.
|
||||
///
|
||||
/// If this is the first layer, behaves like `Absolute`.
|
||||
Parent(usize), // TODO: use a signed vec for negative offset?
|
||||
}
|
||||
|
||||
impl Offset {
|
||||
pub fn compute_offset(&self, size: usize, available: usize, parent: usize) -> usize {
|
||||
pub fn compute_offset(&self, size: usize, available: usize, parent: usize)
|
||||
-> usize {
|
||||
match *self {
|
||||
Offset::Center => (available - size) / 2,
|
||||
Offset::Absolute(offset) => min(offset, available - size),
|
||||
|
@ -34,7 +34,8 @@ impl ScrollBase {
|
||||
self.content_height = content_height;
|
||||
|
||||
if self.scrollable() {
|
||||
self.start_line = min(self.start_line, self.content_height - self.view_height);
|
||||
self.start_line = min(self.start_line,
|
||||
self.content_height - self.view_height);
|
||||
} else {
|
||||
self.start_line = 0;
|
||||
}
|
||||
@ -76,7 +77,8 @@ impl ScrollBase {
|
||||
|
||||
/// Scroll down by the given number of line, never going further than the bottom of the view.
|
||||
pub fn scroll_down(&mut self, n: usize) {
|
||||
self.start_line = min(self.start_line + n, self.content_height - self.view_height);
|
||||
self.start_line = min(self.start_line + n,
|
||||
self.content_height - self.view_height);
|
||||
}
|
||||
|
||||
/// Scroll up by the given number of lines, never going above the top of the view.
|
||||
@ -109,7 +111,8 @@ impl ScrollBase {
|
||||
where F: Fn(&Printer, usize)
|
||||
{
|
||||
// Print the content in a sub_printer
|
||||
let max_y = min(self.view_height, self.content_height - self.start_line);
|
||||
let max_y = min(self.view_height,
|
||||
self.content_height - self.start_line);
|
||||
let w = if self.scrollable() {
|
||||
printer.size.x - 1 // TODO: 2
|
||||
} else {
|
||||
@ -118,7 +121,9 @@ impl ScrollBase {
|
||||
for y in 0..max_y {
|
||||
// Y is the actual coordinate of the line.
|
||||
// The item ID is then Y + self.start_line
|
||||
line_drawer(&printer.sub_printer(Vec2::new(0, y), Vec2::new(w, 1), true),
|
||||
line_drawer(&printer.sub_printer(Vec2::new(0, y),
|
||||
Vec2::new(w, 1),
|
||||
true),
|
||||
y + self.start_line);
|
||||
}
|
||||
|
||||
@ -128,12 +133,15 @@ impl ScrollBase {
|
||||
// We directly compute the size of the scrollbar (this allow use to avoid using floats).
|
||||
// (ratio) * max_height
|
||||
// Where ratio is ({start or end} / content.height)
|
||||
let height = max(1, self.view_height * self.view_height / self.content_height);
|
||||
let height = max(1,
|
||||
self.view_height * self.view_height /
|
||||
self.content_height);
|
||||
// Number of different possible positions
|
||||
let steps = self.view_height - height + 1;
|
||||
|
||||
// Now
|
||||
let start = steps * self.start_line / (1 + self.content_height - self.view_height);
|
||||
let start = steps * self.start_line /
|
||||
(1 + self.content_height - self.view_height);
|
||||
|
||||
let color = if printer.focused {
|
||||
ColorStyle::Highlight
|
||||
|
@ -130,9 +130,8 @@ impl<T: 'static> View for SelectView<T> {
|
||||
|
||||
let h = self.items.len();
|
||||
let offset = self.align.v.get_offset(h, printer.size.y);
|
||||
let printer = &printer.sub_printer(Vec2::new(0, offset),
|
||||
printer.size,
|
||||
true);
|
||||
let printer =
|
||||
&printer.sub_printer(Vec2::new(0, offset), printer.size, true);
|
||||
|
||||
self.scrollbase.draw(printer, |printer, i| {
|
||||
printer.with_selection(i == self.focus, |printer| {
|
||||
@ -150,10 +149,10 @@ impl<T: 'static> View for SelectView<T> {
|
||||
// So no matter what the horizontal requirements are,
|
||||
// we'll still return our longest item.
|
||||
let w = self.items
|
||||
.iter()
|
||||
.map(|item| item.label.width())
|
||||
.max()
|
||||
.unwrap_or(1);
|
||||
.iter()
|
||||
.map(|item| item.label.width())
|
||||
.max()
|
||||
.unwrap_or(1);
|
||||
let h = self.items.len();
|
||||
|
||||
let scrolling = req.y < h;
|
||||
@ -184,7 +183,9 @@ impl<T: 'static> View for SelectView<T> {
|
||||
let cb = self.select_cb.as_ref().unwrap().clone();
|
||||
let v = self.selection();
|
||||
// We return a Callback Rc<|s| cb(s, &*v)>
|
||||
return EventResult::Consumed(Some(Rc::new(move |s| cb(s, &*v))));
|
||||
return EventResult::Consumed(Some(Rc::new(move |s| {
|
||||
cb(s, &*v)
|
||||
})));
|
||||
}
|
||||
Event::Char(c) => {
|
||||
// Starting from the current focus,
|
||||
@ -194,10 +195,8 @@ impl<T: 'static> View for SelectView<T> {
|
||||
// This is achieved by chaining twice the iterator
|
||||
let iter = self.items.iter().chain(self.items.iter());
|
||||
if let Some((i, _)) = iter.enumerate()
|
||||
.skip(self.focus + 1)
|
||||
.find(|&(_, item)| {
|
||||
item.label.starts_with(c)
|
||||
}) {
|
||||
.skip(self.focus + 1)
|
||||
.find(|&(_, item)| item.label.starts_with(c)) {
|
||||
// Apply modulo in case we have a hit
|
||||
// from the chained iterator
|
||||
self.focus = i % self.items.len();
|
||||
|
@ -23,8 +23,7 @@ impl<T: View> ShadowView<T> {
|
||||
}
|
||||
|
||||
fn padding(&self) -> (usize, usize) {
|
||||
(1 + self.left_padding as usize,
|
||||
1 + self.top_padding as usize)
|
||||
(1 + self.left_padding as usize, 1 + self.top_padding as usize)
|
||||
}
|
||||
|
||||
pub fn left_padding(mut self, value: bool) -> Self {
|
||||
@ -35,7 +34,6 @@ impl<T: View> ShadowView<T> {
|
||||
self.top_padding = value;
|
||||
self
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<T: View> ViewWrapper for ShadowView<T> {
|
||||
@ -54,7 +52,11 @@ impl<T: View> ViewWrapper for ShadowView<T> {
|
||||
fn wrap_draw(&mut self, printer: &Printer) {
|
||||
|
||||
// Skip the first row/column
|
||||
let printer = &printer.sub_printer(Vec2::new(self.left_padding as usize, self.top_padding as usize), printer.size, true);
|
||||
let printer =
|
||||
&printer.sub_printer(Vec2::new(self.left_padding as usize,
|
||||
self.top_padding as usize),
|
||||
printer.size,
|
||||
true);
|
||||
|
||||
// Draw the view background
|
||||
for y in 0..printer.size.y - 1 {
|
||||
@ -69,8 +71,8 @@ impl<T: View> ViewWrapper for ShadowView<T> {
|
||||
let w = printer.size.x;
|
||||
|
||||
printer.with_color(ColorStyle::Shadow, |printer| {
|
||||
printer.print_hline((1, h-1), w - 1, " ");
|
||||
printer.print_vline((w-1, 1), h - 1, " ");
|
||||
printer.print_hline((1, h - 1), w - 1, " ");
|
||||
printer.print_vline((w - 1, 1), h - 1, " ");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ impl StackView {
|
||||
self.layers.push(Layer {
|
||||
// Skip padding for absolute/parent-placed views
|
||||
view: Box::new(ShadowView::new(view)
|
||||
.top_padding(position.y == Offset::Center)
|
||||
.left_padding(position.x == Offset::Center)),
|
||||
.top_padding(position.y == Offset::Center)
|
||||
.left_padding(position.x == Offset::Center)),
|
||||
size: Vec2::new(0, 0),
|
||||
position: position,
|
||||
virgin: true,
|
||||
@ -68,11 +68,11 @@ impl View for StackView {
|
||||
// Place the view
|
||||
// Center the view
|
||||
let offset = v.position
|
||||
.compute_offset(v.size, printer.size, previous);
|
||||
.compute_offset(v.size, printer.size, previous);
|
||||
|
||||
previous = offset;
|
||||
v.view
|
||||
.draw(&printer.sub_printer(offset, v.size, i + 1 == last));
|
||||
.draw(&printer.sub_printer(offset, v.size, i + 1 == last));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ pub struct TrackedView<T: View> {
|
||||
pub offset: Vec2,
|
||||
}
|
||||
|
||||
impl <T: View> TrackedView<T> {
|
||||
impl<T: View> TrackedView<T> {
|
||||
pub fn new(view: T) -> Self {
|
||||
TrackedView {
|
||||
view: view,
|
||||
|
Loading…
Reference in New Issue
Block a user