mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-12 20:23:35 +00:00
Push most ncurses-specific code into separate trait
Prepare backend change. Still not isolated is the color setup in `theme`.
This commit is contained in:
parent
69e58a76db
commit
5751a293e5
221
src/backend.rs
Normal file
221
src/backend.rs
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
use event;
|
||||||
|
use theme;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
|
use ncurses;
|
||||||
|
|
||||||
|
pub trait Backend {
|
||||||
|
fn init();
|
||||||
|
fn finish();
|
||||||
|
|
||||||
|
fn clear();
|
||||||
|
fn refresh();
|
||||||
|
|
||||||
|
fn print_at((usize, usize), &str);
|
||||||
|
|
||||||
|
fn poll_event() -> event::Event;
|
||||||
|
fn set_refresh_rate(fps: u32);
|
||||||
|
fn screen_size() -> (usize, usize);
|
||||||
|
|
||||||
|
fn with_color<F: FnOnce()>(color: theme::ColorPair, f: F);
|
||||||
|
fn with_effect<F: FnOnce()>(effect: theme::Effect, f: F);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct NcursesBackend;
|
||||||
|
|
||||||
|
impl Backend for NcursesBackend {
|
||||||
|
fn init() {
|
||||||
|
ncurses::setlocale(ncurses::LcCategory::all, "");
|
||||||
|
ncurses::initscr();
|
||||||
|
ncurses::keypad(ncurses::stdscr, true);
|
||||||
|
ncurses::noecho();
|
||||||
|
ncurses::cbreak();
|
||||||
|
ncurses::start_color();
|
||||||
|
ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
|
||||||
|
ncurses::wbkgd(ncurses::stdscr,
|
||||||
|
ncurses::COLOR_PAIR(theme::ColorPair::Background.ncurses_id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn screen_size() -> (usize, usize) {
|
||||||
|
let mut x: i32 = 0;
|
||||||
|
let mut y: i32 = 0;
|
||||||
|
ncurses::getmaxyx(ncurses::stdscr, &mut y, &mut x);
|
||||||
|
(x as usize, y as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish() {
|
||||||
|
ncurses::endwin();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_color<F: FnOnce()>(color: theme::ColorPair, f: F) {
|
||||||
|
let mut current_style: ncurses::attr_t = 0;
|
||||||
|
let mut current_color: i16 = 0;
|
||||||
|
ncurses::attr_get(&mut current_style, &mut current_color);
|
||||||
|
|
||||||
|
let style = ncurses::COLOR_PAIR(color.ncurses_id());
|
||||||
|
ncurses::attron(style);
|
||||||
|
f();
|
||||||
|
// ncurses::attroff(style);
|
||||||
|
ncurses::attron(current_style);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_effect<F: FnOnce()>(effect: theme::Effect, f: F) {
|
||||||
|
let style = match effect {
|
||||||
|
theme::Effect::Reverse => ncurses::A_REVERSE(),
|
||||||
|
theme::Effect::Simple => ncurses::A_NORMAL(),
|
||||||
|
};
|
||||||
|
ncurses::attron(style);
|
||||||
|
f();
|
||||||
|
ncurses::attroff(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear() {
|
||||||
|
ncurses::clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refresh() {
|
||||||
|
ncurses::refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_at((x, y): (usize, usize), text: &str) {
|
||||||
|
println!("{} {}", x, y);
|
||||||
|
ncurses::mvaddstr(y as i32, x as i32, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_event() -> event::Event {
|
||||||
|
let ch: i32 = ncurses::getch();
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
} else {
|
||||||
|
event::Event::Key(parse_ncurses_char(ch))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_refresh_rate(fps: u32) {
|
||||||
|
if fps == 0 {
|
||||||
|
ncurses::timeout(-1);
|
||||||
|
} else {
|
||||||
|
ncurses::timeout(1000 / fps as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the Key enum corresponding to the given ncurses event.
|
||||||
|
fn parse_ncurses_char(ch: i32) -> event::Key {
|
||||||
|
|
||||||
|
match ch {
|
||||||
|
// Values under 256 are chars and control values
|
||||||
|
//
|
||||||
|
// Tab is '\t'
|
||||||
|
9 => event::Key::Tab,
|
||||||
|
// Treat '\n' and the numpad Enter the same
|
||||||
|
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,
|
||||||
|
|
||||||
|
410 => event::Key::Resize,
|
||||||
|
|
||||||
|
// Values 512 and above are probably extensions
|
||||||
|
// Those keys don't seem to be documented...
|
||||||
|
519 => event::Key::AltDel,
|
||||||
|
520 => event::Key::AltShiftDel,
|
||||||
|
521 => event::Key::CtrlDel,
|
||||||
|
522 => event::Key::CtrlShiftDel,
|
||||||
|
// 523: CtrlAltDel?
|
||||||
|
//
|
||||||
|
// 524?
|
||||||
|
525 => event::Key::AltDown,
|
||||||
|
526 => event::Key::AltShiftDown,
|
||||||
|
527 => event::Key::CtrlDown,
|
||||||
|
528 => event::Key::CtrlShiftDown,
|
||||||
|
529 => event::Key::CtrlAltDown,
|
||||||
|
|
||||||
|
530 => event::Key::AltEnd,
|
||||||
|
531 => event::Key::AltShiftEnd,
|
||||||
|
532 => event::Key::CtrlEnd,
|
||||||
|
533 => event::Key::CtrlShiftEnd,
|
||||||
|
534 => event::Key::CtrlAltEnd,
|
||||||
|
|
||||||
|
535 => event::Key::AltHome,
|
||||||
|
536 => event::Key::AltShiftHome,
|
||||||
|
537 => event::Key::CtrlHome,
|
||||||
|
538 => event::Key::CtrlShiftHome,
|
||||||
|
539 => event::Key::CtrlAltHome,
|
||||||
|
|
||||||
|
540 => event::Key::AltIns,
|
||||||
|
// 541: AltShiftIns?
|
||||||
|
542 => event::Key::CtrlIns,
|
||||||
|
// 543: CtrlShiftIns?
|
||||||
|
544 => event::Key::CtrlAltIns,
|
||||||
|
|
||||||
|
545 => event::Key::AltLeft,
|
||||||
|
546 => event::Key::AltShiftLeft,
|
||||||
|
547 => event::Key::CtrlLeft,
|
||||||
|
548 => event::Key::CtrlShiftLeft,
|
||||||
|
549 => event::Key::CtrlAltLeft,
|
||||||
|
|
||||||
|
550 => event::Key::AltPageDown,
|
||||||
|
551 => event::Key::AltShiftPageDown,
|
||||||
|
552 => event::Key::CtrlPageDown,
|
||||||
|
553 => event::Key::CtrlShiftPageDown,
|
||||||
|
554 => event::Key::CtrlAltPageDown,
|
||||||
|
|
||||||
|
555 => event::Key::AltPageUp,
|
||||||
|
556 => event::Key::AltShiftPageUp,
|
||||||
|
557 => event::Key::CtrlPageUp,
|
||||||
|
558 => event::Key::CtrlShiftPageUp,
|
||||||
|
559 => event::Key::CtrlAltPageUp,
|
||||||
|
|
||||||
|
560 => event::Key::AltRight,
|
||||||
|
561 => event::Key::AltShiftRight,
|
||||||
|
562 => event::Key::CtrlRight,
|
||||||
|
563 => event::Key::CtrlShiftRight,
|
||||||
|
564 => event::Key::CtrlAltRight,
|
||||||
|
// 565?
|
||||||
|
566 => event::Key::AltUp,
|
||||||
|
567 => event::Key::AltShiftUp,
|
||||||
|
568 => event::Key::CtrlUp,
|
||||||
|
569 => event::Key::CtrlShiftUp,
|
||||||
|
570 => event::Key::CtrlAltUp,
|
||||||
|
|
||||||
|
ncurses::KEY_B2 => event::Key::NumpadCenter,
|
||||||
|
ncurses::KEY_DC => event::Key::Del,
|
||||||
|
ncurses::KEY_IC => event::Key::Ins,
|
||||||
|
ncurses::KEY_BTAB => event::Key::ShiftTab,
|
||||||
|
ncurses::KEY_SLEFT => event::Key::ShiftLeft,
|
||||||
|
ncurses::KEY_SRIGHT => event::Key::ShiftRight,
|
||||||
|
ncurses::KEY_LEFT => event::Key::Left,
|
||||||
|
ncurses::KEY_RIGHT => event::Key::Right,
|
||||||
|
ncurses::KEY_UP => event::Key::Up,
|
||||||
|
ncurses::KEY_DOWN => event::Key::Down,
|
||||||
|
ncurses::KEY_SR => event::Key::ShiftUp,
|
||||||
|
ncurses::KEY_SF => event::Key::ShiftDown,
|
||||||
|
ncurses::KEY_PPAGE => event::Key::PageUp,
|
||||||
|
ncurses::KEY_NPAGE => event::Key::PageDown,
|
||||||
|
ncurses::KEY_HOME => event::Key::Home,
|
||||||
|
ncurses::KEY_END => event::Key::End,
|
||||||
|
ncurses::KEY_SHOME => event::Key::ShiftHome,
|
||||||
|
ncurses::KEY_SEND => event::Key::ShiftEnd,
|
||||||
|
ncurses::KEY_SDC => event::Key::ShiftDel,
|
||||||
|
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 @ 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),
|
||||||
|
f @ 313...324 => event::Key::AltF((f - 313) as u8),
|
||||||
|
// Shift and Ctrl F{1-4} need escape sequences...
|
||||||
|
//
|
||||||
|
// 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),
|
||||||
|
_ => event::Key::Unknown(ch),
|
||||||
|
}
|
||||||
|
}
|
124
src/event.rs
124
src/event.rs
@ -3,8 +3,6 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use ncurses;
|
|
||||||
|
|
||||||
use Cursive;
|
use Cursive;
|
||||||
|
|
||||||
/// Callback is a function that can be triggered by an event.
|
/// Callback is a function that can be triggered by an event.
|
||||||
@ -130,128 +128,6 @@ pub enum Key {
|
|||||||
Unknown(i32),
|
Unknown(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Key {
|
|
||||||
/// Returns the Key enum corresponding to the given ncurses event.
|
|
||||||
pub fn from_ncurses(ch: i32) -> Self {
|
|
||||||
match ch {
|
|
||||||
// Values under 256 are chars and control values
|
|
||||||
//
|
|
||||||
// Tab is '\t'
|
|
||||||
9 => Key::Tab,
|
|
||||||
// Treat '\n' and the numpad Enter the same
|
|
||||||
10 | ncurses::KEY_ENTER => Key::Enter,
|
|
||||||
// This is the escape key when pressed by itself.
|
|
||||||
// When used for control sequences, it should have been caught earlier.
|
|
||||||
27 => Key::Esc,
|
|
||||||
// `Backspace` sends 127, but Ctrl-H sends `Backspace`
|
|
||||||
127 | ncurses::KEY_BACKSPACE => Key::Backspace,
|
|
||||||
|
|
||||||
410 => Key::Resize,
|
|
||||||
|
|
||||||
// Values 512 and above are probably extensions
|
|
||||||
// Those keys don't seem to be documented...
|
|
||||||
519 => Key::AltDel,
|
|
||||||
520 => Key::AltShiftDel,
|
|
||||||
521 => Key::CtrlDel,
|
|
||||||
522 => Key::CtrlShiftDel,
|
|
||||||
// 523: CtrlAltDel?
|
|
||||||
//
|
|
||||||
// 524?
|
|
||||||
525 => Key::AltDown,
|
|
||||||
526 => Key::AltShiftDown,
|
|
||||||
527 => Key::CtrlDown,
|
|
||||||
528 => Key::CtrlShiftDown,
|
|
||||||
529 => Key::CtrlAltDown,
|
|
||||||
|
|
||||||
530 => Key::AltEnd,
|
|
||||||
531 => Key::AltShiftEnd,
|
|
||||||
532 => Key::CtrlEnd,
|
|
||||||
533 => Key::CtrlShiftEnd,
|
|
||||||
534 => Key::CtrlAltEnd,
|
|
||||||
|
|
||||||
535 => Key::AltHome,
|
|
||||||
536 => Key::AltShiftHome,
|
|
||||||
537 => Key::CtrlHome,
|
|
||||||
538 => Key::CtrlShiftHome,
|
|
||||||
539 => Key::CtrlAltHome,
|
|
||||||
|
|
||||||
540 => Key::AltIns,
|
|
||||||
// 541: AltShiftIns?
|
|
||||||
542 => Key::CtrlIns,
|
|
||||||
// 543: CtrlShiftIns?
|
|
||||||
544 => Key::CtrlAltIns,
|
|
||||||
|
|
||||||
545 => Key::AltLeft,
|
|
||||||
546 => Key::AltShiftLeft,
|
|
||||||
547 => Key::CtrlLeft,
|
|
||||||
548 => Key::CtrlShiftLeft,
|
|
||||||
549 => Key::CtrlAltLeft,
|
|
||||||
|
|
||||||
550 => Key::AltPageDown,
|
|
||||||
551 => Key::AltShiftPageDown,
|
|
||||||
552 => Key::CtrlPageDown,
|
|
||||||
553 => Key::CtrlShiftPageDown,
|
|
||||||
554 => Key::CtrlAltPageDown,
|
|
||||||
|
|
||||||
555 => Key::AltPageUp,
|
|
||||||
556 => Key::AltShiftPageUp,
|
|
||||||
557 => Key::CtrlPageUp,
|
|
||||||
558 => Key::CtrlShiftPageUp,
|
|
||||||
559 => Key::CtrlAltPageUp,
|
|
||||||
|
|
||||||
560 => Key::AltRight,
|
|
||||||
561 => Key::AltShiftRight,
|
|
||||||
562 => Key::CtrlRight,
|
|
||||||
563 => Key::CtrlShiftRight,
|
|
||||||
564 => Key::CtrlAltRight,
|
|
||||||
// 565?
|
|
||||||
566 => Key::AltUp,
|
|
||||||
567 => Key::AltShiftUp,
|
|
||||||
568 => Key::CtrlUp,
|
|
||||||
569 => Key::CtrlShiftUp,
|
|
||||||
570 => Key::CtrlAltUp,
|
|
||||||
|
|
||||||
ncurses::KEY_B2 => Key::NumpadCenter,
|
|
||||||
ncurses::KEY_DC => Key::Del,
|
|
||||||
ncurses::KEY_IC => Key::Ins,
|
|
||||||
ncurses::KEY_BTAB => Key::ShiftTab,
|
|
||||||
ncurses::KEY_SLEFT => Key::ShiftLeft,
|
|
||||||
ncurses::KEY_SRIGHT => Key::ShiftRight,
|
|
||||||
ncurses::KEY_LEFT => Key::Left,
|
|
||||||
ncurses::KEY_RIGHT => Key::Right,
|
|
||||||
ncurses::KEY_UP => Key::Up,
|
|
||||||
ncurses::KEY_DOWN => Key::Down,
|
|
||||||
ncurses::KEY_SR => Key::ShiftUp,
|
|
||||||
ncurses::KEY_SF => Key::ShiftDown,
|
|
||||||
ncurses::KEY_PPAGE => Key::PageUp,
|
|
||||||
ncurses::KEY_NPAGE => Key::PageDown,
|
|
||||||
ncurses::KEY_HOME => Key::Home,
|
|
||||||
ncurses::KEY_END => Key::End,
|
|
||||||
ncurses::KEY_SHOME => Key::ShiftHome,
|
|
||||||
ncurses::KEY_SEND => Key::ShiftEnd,
|
|
||||||
ncurses::KEY_SDC => Key::ShiftDel,
|
|
||||||
ncurses::KEY_SNEXT => Key::ShiftPageDown,
|
|
||||||
ncurses::KEY_SPREVIOUS => Key::ShiftPageUp,
|
|
||||||
// All Fn keys use the same enum with associated number
|
|
||||||
f @ ncurses::KEY_F1...ncurses::KEY_F12 => {
|
|
||||||
Key::F((f - ncurses::KEY_F0) as u8)
|
|
||||||
}
|
|
||||||
f @ 277...288 => Key::ShiftF((f - 277) as u8),
|
|
||||||
f @ 289...300 => Key::CtrlF((f - 289) as u8),
|
|
||||||
f @ 301...312 => Key::CtrlShiftF((f - 300) as u8),
|
|
||||||
f @ 313...324 => Key::AltF((f - 313) as u8),
|
|
||||||
// Shift and Ctrl F{1-4} need escape sequences...
|
|
||||||
//
|
|
||||||
// TODO: shift and ctrl Fn keys
|
|
||||||
// Avoids 8-10 (H,I,J), they are used by other commands.
|
|
||||||
c @ 1...7 | c @ 11...25 => {
|
|
||||||
Key::CtrlChar((b'a' + (c - 1) as u8) as char)
|
|
||||||
}
|
|
||||||
_ => Key::Unknown(ch),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Key {
|
impl fmt::Display for Key {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
|
60
src/lib.rs
60
src/lib.rs
@ -39,6 +39,10 @@ mod menubar;
|
|||||||
mod div;
|
mod div;
|
||||||
mod utf8;
|
mod utf8;
|
||||||
|
|
||||||
|
mod backend;
|
||||||
|
|
||||||
|
use backend::{Backend, NcursesBackend};
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -49,7 +53,7 @@ use printer::Printer;
|
|||||||
use view::View;
|
use view::View;
|
||||||
use view::{Selector, StackView};
|
use view::{Selector, StackView};
|
||||||
|
|
||||||
use event::{Callback, Event, EventResult, Key, ToEvent};
|
use event::{Callback, Event, EventResult, ToEvent};
|
||||||
|
|
||||||
/// Identifies a screen in the cursive ROOT.
|
/// Identifies a screen in the cursive ROOT.
|
||||||
pub type ScreenId = usize;
|
pub type ScreenId = usize;
|
||||||
@ -78,23 +82,20 @@ impl Default for Cursive {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use the Ncurses backend.
|
||||||
|
// TODO: make this feature-driven
|
||||||
|
type B = NcursesBackend;
|
||||||
|
|
||||||
impl Cursive {
|
impl Cursive {
|
||||||
/// Creates a new Cursive root, and initialize ncurses.
|
/// Creates a new Cursive root, and initialize ncurses.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
// Default delay is way too long. 25 is imperceptible yet works fine.
|
// Default delay is way too long. 25 is imperceptible yet works fine.
|
||||||
std::env::set_var("ESCDELAY", "25");
|
std::env::set_var("ESCDELAY", "25");
|
||||||
ncurses::setlocale(ncurses::LcCategory::all, "");
|
B::init();
|
||||||
ncurses::initscr();
|
|
||||||
ncurses::keypad(ncurses::stdscr, true);
|
|
||||||
ncurses::noecho();
|
|
||||||
ncurses::cbreak();
|
|
||||||
ncurses::start_color();
|
|
||||||
ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
|
|
||||||
let theme = theme::load_default();
|
let theme = theme::load_default();
|
||||||
// let theme = theme::load_theme("assets/style.toml").unwrap();
|
// let theme = theme::load_theme("assets/style.toml").unwrap();
|
||||||
|
|
||||||
ncurses::wbkgd(ncurses::stdscr,
|
|
||||||
ncurses::COLOR_PAIR(theme::ColorPair::Background.ncurses_id()));
|
|
||||||
|
|
||||||
let mut res = Cursive {
|
let mut res = Cursive {
|
||||||
theme: theme,
|
theme: theme,
|
||||||
@ -148,11 +149,7 @@ impl Cursive {
|
|||||||
///
|
///
|
||||||
/// Call with fps=0 to disable (default value).
|
/// Call with fps=0 to disable (default value).
|
||||||
pub fn set_fps(&self, fps: u32) {
|
pub fn set_fps(&self, fps: u32) {
|
||||||
if fps == 0 {
|
B::set_refresh_rate(fps)
|
||||||
ncurses::timeout(-1);
|
|
||||||
} else {
|
|
||||||
ncurses::timeout(1000 / fps as i32);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable reference to the currently active screen.
|
/// Returns a mutable reference to the currently active screen.
|
||||||
@ -235,9 +232,7 @@ impl Cursive {
|
|||||||
|
|
||||||
/// Returns the size of the screen, in characters.
|
/// Returns the size of the screen, in characters.
|
||||||
pub fn screen_size(&self) -> Vec2 {
|
pub fn screen_size(&self) -> Vec2 {
|
||||||
let mut x: i32 = 0;
|
let (x, y) = B::screen_size();
|
||||||
let mut y: i32 = 0;
|
|
||||||
ncurses::getmaxyx(ncurses::stdscr, &mut y, &mut x);
|
|
||||||
|
|
||||||
Vec2 {
|
Vec2 {
|
||||||
x: x as usize,
|
x: x as usize,
|
||||||
@ -258,35 +253,20 @@ impl Cursive {
|
|||||||
// Draw the currently active screen
|
// Draw the currently active screen
|
||||||
// If the menubar is active, nothing else can be.
|
// If the menubar is active, nothing else can be.
|
||||||
let offset = if self.menu.autohide {
|
let offset = if self.menu.autohide {
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
};
|
};
|
||||||
let selected = self.menu.selected;
|
let selected = self.menu.selected;
|
||||||
self.screen_mut()
|
self.screen_mut()
|
||||||
.draw(&printer.sub_printer(Vec2::new(0, offset),
|
.draw(&printer.sub_printer(Vec2::new(0, offset), printer.size, !selected));
|
||||||
printer.size,
|
|
||||||
!selected));
|
|
||||||
|
|
||||||
// Draw the menubar?
|
// Draw the menubar?
|
||||||
if self.menu.selected || !self.menu.autohide {
|
if self.menu.selected || !self.menu.autohide {
|
||||||
self.menu.draw(&printer);
|
self.menu.draw(&printer);
|
||||||
}
|
}
|
||||||
|
|
||||||
ncurses::refresh();
|
B::refresh();
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_event() -> Event {
|
|
||||||
let ch: i32 = ncurses::getch();
|
|
||||||
|
|
||||||
// Is it a UTF-8 starting point?
|
|
||||||
if 32 <= ch && ch < 0x100 && ch != 127 {
|
|
||||||
Event::Char(utf8::read_char(ch as u8,
|
|
||||||
|| ncurses::getch() as u8)
|
|
||||||
.unwrap())
|
|
||||||
} else {
|
|
||||||
Event::Key(Key::from_ncurses(ch))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the event loop.
|
/// Runs the event loop.
|
||||||
@ -300,7 +280,7 @@ impl Cursive {
|
|||||||
// Do we need to redraw everytime?
|
// Do we need to redraw everytime?
|
||||||
// Probably, actually.
|
// Probably, actually.
|
||||||
// TODO: Do we actually need to clear everytime?
|
// TODO: Do we actually need to clear everytime?
|
||||||
ncurses::clear();
|
B::clear();
|
||||||
// TODO: Do we need to re-layout everytime?
|
// TODO: Do we need to re-layout everytime?
|
||||||
self.layout();
|
self.layout();
|
||||||
// TODO: Do we need to redraw every view every time?
|
// TODO: Do we need to redraw every view every time?
|
||||||
@ -309,7 +289,7 @@ impl Cursive {
|
|||||||
|
|
||||||
// Wait for next event.
|
// Wait for next event.
|
||||||
// (If set_fps was called, this returns -1 now and then)
|
// (If set_fps was called, this returns -1 now and then)
|
||||||
let event = Cursive::poll_event();
|
let event = B::poll_event();
|
||||||
|
|
||||||
// Event dispatch order:
|
// Event dispatch order:
|
||||||
// * Focused element:
|
// * Focused element:
|
||||||
@ -339,6 +319,6 @@ impl Cursive {
|
|||||||
|
|
||||||
impl Drop for Cursive {
|
impl Drop for Cursive {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
ncurses::endwin();
|
B::finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,7 @@ impl MenuTree {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn leaf<F: 'static + Fn(&mut Cursive)>(&mut self, title: &str, cb: F)
|
pub fn leaf<F: 'static + Fn(&mut Cursive)>(&mut self, title: &str, cb: F) -> &mut Self {
|
||||||
-> &mut Self {
|
|
||||||
self.children
|
self.children
|
||||||
.push(MenuItem::Leaf(title.to_string(), Rc::new(Box::new(cb))));
|
.push(MenuItem::Leaf(title.to_string(), Rc::new(Box::new(cb))));
|
||||||
self
|
self
|
||||||
|
@ -44,12 +44,8 @@ impl Orientation {
|
|||||||
/// For a vertical view, returns (Max(x),Sum(y)).
|
/// For a vertical view, returns (Max(x),Sum(y)).
|
||||||
pub fn stack<'a, T: Iterator<Item = &'a Vec2>>(&self, iter: T) -> Vec2 {
|
pub fn stack<'a, T: Iterator<Item = &'a Vec2>>(&self, iter: T) -> Vec2 {
|
||||||
match *self {
|
match *self {
|
||||||
Orientation::Horizontal => {
|
Orientation::Horizontal => iter.fold(Vec2::zero(), |a, b| a.stack_horizontal(b)),
|
||||||
iter.fold(Vec2::zero(), |a, b| a.stack_horizontal(b))
|
Orientation::Vertical => iter.fold(Vec2::zero(), |a, b| a.stack_vertical(b)),
|
||||||
}
|
|
||||||
Orientation::Vertical => {
|
|
||||||
iter.fold(Vec2::zero(), |a, b| a.stack_vertical(b))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
|
||||||
use ncurses;
|
use backend::Backend;
|
||||||
|
|
||||||
use theme::{ColorPair, Theme};
|
use B;
|
||||||
|
|
||||||
|
use theme::{ColorPair, Theme, Effect};
|
||||||
use vec::{ToVec2, Vec2};
|
use vec::{ToVec2, Vec2};
|
||||||
|
|
||||||
/// Convenient interface to draw on a subset of the screen.
|
/// Convenient interface to draw on a subset of the screen.
|
||||||
@ -49,11 +51,9 @@ impl Printer {
|
|||||||
|
|
||||||
let p = p + self.offset;
|
let p = p + self.offset;
|
||||||
if text.contains('%') {
|
if text.contains('%') {
|
||||||
ncurses::mvprintw(p.y as i32,
|
B::print_at((p.x, p.y), &text.replace("%", "%%"));
|
||||||
p.x as i32,
|
|
||||||
&text.replace("%", "%%"));
|
|
||||||
} else {
|
} else {
|
||||||
ncurses::mvprintw(p.y as i32, p.x as i32, text);
|
B::print_at((p.x, p.y), text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ impl Printer {
|
|||||||
|
|
||||||
let p = p + self.offset;
|
let p = p + self.offset;
|
||||||
for y in 0..len {
|
for y in 0..len {
|
||||||
ncurses::mvaddstr((p.y + y) as i32, p.x as i32, c);
|
B::print_at((p.x, (p.y + y)), c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ impl Printer {
|
|||||||
|
|
||||||
let p = p + self.offset;
|
let p = p + self.offset;
|
||||||
for x in 0..len {
|
for x in 0..len {
|
||||||
ncurses::mvaddstr(p.y as i32, (p.x + x) as i32, c);
|
B::print_at((p.x + x, p.y), c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,22 +99,19 @@ impl Printer {
|
|||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with_color<F>(&self, c: ColorPair, f: F)
|
pub fn with_color<F>(&self, c: ColorPair, f: F)
|
||||||
where F: Fn(&Printer)
|
where F: FnOnce(&Printer)
|
||||||
{
|
{
|
||||||
self.with_style(ncurses::COLOR_PAIR(c.ncurses_id()), f);
|
B::with_color(c, || f(self));
|
||||||
ncurses::attron(ncurses::COLOR_PAIR(ColorPair::Primary.ncurses_id()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `with_color`, but apply a ncurses style instead,
|
/// Same as `with_color`, but apply a ncurses style instead,
|
||||||
/// like `ncurses::A_BOLD()` or `ncurses::A_REVERSE()`.
|
/// like `ncurses::A_BOLD()` or `ncurses::A_REVERSE()`.
|
||||||
///
|
///
|
||||||
/// Will probably use a cursive enum some day.
|
/// Will probably use a cursive enum some day.
|
||||||
pub fn with_style<F>(&self, style: ncurses::attr_t, f: F)
|
pub fn with_effect<F>(&self, effect: Effect, f: F)
|
||||||
where F: Fn(&Printer)
|
where F: FnOnce(&Printer)
|
||||||
{
|
{
|
||||||
ncurses::attron(style);
|
B::with_effect(effect, || f(self));
|
||||||
f(self);
|
|
||||||
ncurses::attroff(style);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prints a rectangular box.
|
/// Prints a rectangular box.
|
||||||
@ -138,12 +135,8 @@ impl Printer {
|
|||||||
|
|
||||||
self.print_hline(start_v + (1, 0), size_v.x - 1, "─");
|
self.print_hline(start_v + (1, 0), size_v.x - 1, "─");
|
||||||
self.print_vline(start_v + (0, 1), size_v.y - 1, "│");
|
self.print_vline(start_v + (0, 1), size_v.y - 1, "│");
|
||||||
self.print_hline(start_v + (1, 0) + size_v.keep_y(),
|
self.print_hline(start_v + (1, 0) + size_v.keep_y(), size_v.x - 1, "─");
|
||||||
size_v.x - 1,
|
self.print_vline(start_v + (0, 1) + size_v.keep_x(), size_v.y - 1, "│");
|
||||||
"─");
|
|
||||||
self.print_vline(start_v + (0, 1) + size_v.keep_x(),
|
|
||||||
size_v.y - 1,
|
|
||||||
"│");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_hdelim<T: ToVec2>(&self, start: T, len: usize) {
|
pub fn print_hdelim<T: ToVec2>(&self, start: T, len: usize) {
|
||||||
@ -154,8 +147,7 @@ impl Printer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a printer on a subset of this one's area.
|
/// Returns a printer on a subset of this one's area.
|
||||||
pub fn sub_printer<S: ToVec2>(&self, offset: S, size: S, focused: bool)
|
pub fn sub_printer<S: ToVec2>(&self, offset: S, size: S, focused: bool) -> Printer {
|
||||||
-> Printer {
|
|
||||||
let offset_v = offset.to_vec2();
|
let offset_v = offset.to_vec2();
|
||||||
Printer {
|
Printer {
|
||||||
offset: self.offset + offset_v,
|
offset: self.offset + offset_v,
|
||||||
|
15
src/theme.rs
15
src/theme.rs
@ -8,6 +8,11 @@ use std::path::Path;
|
|||||||
use ncurses;
|
use ncurses;
|
||||||
use toml;
|
use toml;
|
||||||
|
|
||||||
|
pub enum Effect {
|
||||||
|
Simple,
|
||||||
|
Reverse,
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents the color of a character and its background.
|
/// Represents the color of a character and its background.
|
||||||
#[derive(Clone,Copy)]
|
#[derive(Clone,Copy)]
|
||||||
pub enum ColorPair {
|
pub enum ColorPair {
|
||||||
@ -97,14 +102,10 @@ impl Theme {
|
|||||||
|
|
||||||
fn apply(&self) {
|
fn apply(&self) {
|
||||||
Theme::apply_color(ColorPair::Background,
|
Theme::apply_color(ColorPair::Background,
|
||||||
&self.colors.background,
|
&self.colors.view,
|
||||||
&self.colors.background);
|
&self.colors.background);
|
||||||
Theme::apply_color(ColorPair::Shadow,
|
Theme::apply_color(ColorPair::Shadow, &self.colors.shadow, &self.colors.shadow);
|
||||||
&self.colors.shadow,
|
Theme::apply_color(ColorPair::Primary, &self.colors.primary, &self.colors.view);
|
||||||
&self.colors.shadow);
|
|
||||||
Theme::apply_color(ColorPair::Primary,
|
|
||||||
&self.colors.primary,
|
|
||||||
&self.colors.view);
|
|
||||||
Theme::apply_color(ColorPair::Secondary,
|
Theme::apply_color(ColorPair::Secondary,
|
||||||
&self.colors.secondary,
|
&self.colors.secondary,
|
||||||
&self.colors.view);
|
&self.colors.view);
|
||||||
|
@ -50,9 +50,7 @@ impl View for Button {
|
|||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
match event {
|
match event {
|
||||||
// 10 is the ascii code for '\n', that is the return key
|
// 10 is the ascii code for '\n', that is the return key
|
||||||
Event::Key(Key::Enter) => {
|
Event::Key(Key::Enter) => EventResult::Consumed(Some(self.callback.clone())),
|
||||||
EventResult::Consumed(Some(self.callback.clone()))
|
|
||||||
}
|
|
||||||
_ => EventResult::Ignored,
|
_ => EventResult::Ignored,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,9 +121,7 @@ impl View for Dialog {
|
|||||||
};
|
};
|
||||||
let overhead = self.padding + self.borders;
|
let overhead = self.padding + self.borders;
|
||||||
let mut offset = overhead.left +
|
let mut offset = overhead.left +
|
||||||
self.align.h.get_offset(width,
|
self.align.h.get_offset(width, printer.size.x - overhead.horizontal());
|
||||||
printer.size.x -
|
|
||||||
overhead.horizontal());
|
|
||||||
let y = printer.size.y - self.padding.bottom - self.borders.bottom - 1;
|
let y = printer.size.y - self.padding.bottom - self.borders.bottom - 1;
|
||||||
|
|
||||||
for (i, button) in self.buttons.iter_mut().enumerate() {
|
for (i, button) in self.buttons.iter_mut().enumerate() {
|
||||||
@ -139,12 +137,10 @@ impl View for Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// What do we have left?
|
// What do we have left?
|
||||||
let inner_size = printer.size - Vec2::new(0, height) -
|
let inner_size = printer.size - Vec2::new(0, height) - self.borders.combined() -
|
||||||
self.borders.combined() -
|
|
||||||
self.padding.combined();
|
self.padding.combined();
|
||||||
|
|
||||||
self.content.draw(&printer.sub_printer(self.borders.top_left() +
|
self.content.draw(&printer.sub_printer(self.borders.top_left() + self.padding.top_left(),
|
||||||
self.padding.top_left(),
|
|
||||||
inner_size,
|
inner_size,
|
||||||
self.focus == Focus::Content));
|
self.focus == Focus::Content));
|
||||||
|
|
||||||
@ -156,16 +152,14 @@ impl View for Dialog {
|
|||||||
printer.print((x - 2, 0), "┤ ");
|
printer.print((x - 2, 0), "┤ ");
|
||||||
printer.print((x + len, 0), " ├");
|
printer.print((x + len, 0), " ├");
|
||||||
|
|
||||||
printer.with_color(ColorPair::TitlePrimary,
|
printer.with_color(ColorPair::TitlePrimary, |p| p.print((x, 0), &self.title));
|
||||||
|p| p.print((x, 0), &self.title));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_min_size(&self, req: SizeRequest) -> Vec2 {
|
fn get_min_size(&self, req: SizeRequest) -> Vec2 {
|
||||||
// Padding and borders are not available for kids.
|
// Padding and borders are not available for kids.
|
||||||
let content_req = req.reduced(self.padding.combined() +
|
let content_req = req.reduced(self.padding.combined() + self.borders.combined());
|
||||||
self.borders.combined());
|
|
||||||
let content_size = self.content.get_min_size(content_req);
|
let content_size = self.content.get_min_size(content_req);
|
||||||
|
|
||||||
let mut buttons_size = Vec2::new(0, 0);
|
let mut buttons_size = Vec2::new(0, 0);
|
||||||
@ -251,8 +245,8 @@ impl View for Dialog {
|
|||||||
}
|
}
|
||||||
// Left and Right move to other buttons
|
// Left and Right move to other buttons
|
||||||
Event::Key(Key::Right) if i + 1 <
|
Event::Key(Key::Right) if i + 1 <
|
||||||
self.buttons
|
self.buttons
|
||||||
.len() => {
|
.len() => {
|
||||||
self.focus = Focus::Button(i + 1);
|
self.focus = Focus::Button(i + 1);
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use ncurses;
|
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
|
||||||
use theme::ColorPair;
|
use theme::{ColorPair, Effect};
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::{IdView, SizeRequest, View};
|
use view::{IdView, SizeRequest, View};
|
||||||
use event::*;
|
use event::*;
|
||||||
@ -87,13 +86,12 @@ impl View for EditView {
|
|||||||
// let style = if focused { color::HIGHLIGHT } else { color::HIGHLIGHT_INACTIVE };
|
// let style = if focused { color::HIGHLIGHT } else { color::HIGHLIGHT_INACTIVE };
|
||||||
let len = self.content.chars().count();
|
let len = self.content.chars().count();
|
||||||
printer.with_color(ColorPair::Secondary, |printer| {
|
printer.with_color(ColorPair::Secondary, |printer| {
|
||||||
printer.with_style(ncurses::A_REVERSE(), |printer| {
|
printer.with_effect(Effect::Reverse, |printer| {
|
||||||
if len < self.last_length {
|
if len < self.last_length {
|
||||||
printer.print((0, 0), &self.content);
|
printer.print((0, 0), &self.content);
|
||||||
printer.print_hline((len, 0), printer.size.x - len, "_");
|
printer.print_hline((len, 0), printer.size.x - len, "_");
|
||||||
} else {
|
} else {
|
||||||
let visible_end = min(self.content.len(),
|
let visible_end = min(self.content.len(), self.offset + self.last_length);
|
||||||
self.offset + self.last_length);
|
|
||||||
|
|
||||||
let content = &self.content[self.offset..visible_end];
|
let content = &self.content[self.offset..visible_end];
|
||||||
printer.print((0, 0), content);
|
printer.print((0, 0), content);
|
||||||
@ -151,10 +149,7 @@ impl View for EditView {
|
|||||||
Key::Home => self.cursor = 0,
|
Key::Home => self.cursor = 0,
|
||||||
Key::End => self.cursor = self.content.chars().count(),
|
Key::End => self.cursor = self.content.chars().count(),
|
||||||
Key::Left if self.cursor > 0 => self.cursor -= 1,
|
Key::Left if self.cursor > 0 => self.cursor -= 1,
|
||||||
Key::Right if self.cursor <
|
Key::Right if self.cursor < self.content.chars().count() => self.cursor += 1,
|
||||||
self.content.chars().count() => {
|
|
||||||
self.cursor += 1
|
|
||||||
}
|
|
||||||
Key::Backspace if self.cursor > 0 => {
|
Key::Backspace if self.cursor > 0 => {
|
||||||
self.cursor -= 1;
|
self.cursor -= 1;
|
||||||
remove_char(&mut self.content, self.cursor);
|
remove_char(&mut self.content, self.cursor);
|
||||||
|
@ -113,9 +113,7 @@ impl View for LinearLayout {
|
|||||||
// Use pre-computed sizes
|
// Use pre-computed sizes
|
||||||
let mut offset = Vec2::zero();
|
let mut offset = Vec2::zero();
|
||||||
for (i, child) in self.children.iter_mut().enumerate() {
|
for (i, child) in self.children.iter_mut().enumerate() {
|
||||||
child.view.draw(&printer.sub_printer(offset,
|
child.view.draw(&printer.sub_printer(offset, child.size, i == self.focus));
|
||||||
child.size,
|
|
||||||
i == self.focus));
|
|
||||||
|
|
||||||
*self.orientation.get_ref(&mut offset) += self.orientation
|
*self.orientation.get_ref(&mut offset) += self.orientation
|
||||||
.get(&child.size);
|
.get(&child.size);
|
||||||
@ -130,9 +128,7 @@ impl View for LinearLayout {
|
|||||||
};
|
};
|
||||||
let min_sizes: Vec<Vec2> = self.children
|
let min_sizes: Vec<Vec2> = self.children
|
||||||
.iter()
|
.iter()
|
||||||
.map(|child| {
|
.map(|child| child.view.get_min_size(req))
|
||||||
child.view.get_min_size(req)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
let min_size = self.orientation.stack(min_sizes.iter());
|
let min_size = self.orientation.stack(min_sizes.iter());
|
||||||
|
|
||||||
@ -153,14 +149,12 @@ impl View for LinearLayout {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
for (child, (child_size, extra)) in
|
for (child, (child_size, extra)) in self.children
|
||||||
self.children
|
.iter_mut()
|
||||||
.iter_mut()
|
.zip(min_sizes.iter().zip(extras.iter())) {
|
||||||
.zip(min_sizes.iter().zip(extras.iter())) {
|
|
||||||
let mut child_size = *child_size;
|
let mut child_size = *child_size;
|
||||||
*self.orientation.get_ref(&mut child_size) += *extra;
|
*self.orientation.get_ref(&mut child_size) += *extra;
|
||||||
*self.orientation.swap().get_ref(&mut child_size) =
|
*self.orientation.swap().get_ref(&mut child_size) = self.orientation.swap().get(&size);
|
||||||
self.orientation.swap().get(&size);
|
|
||||||
child.size = child_size;
|
child.size = child_size;
|
||||||
child.view.layout(child_size);
|
child.view.layout(child_size);
|
||||||
}
|
}
|
||||||
@ -194,34 +188,27 @@ impl View for LinearLayout {
|
|||||||
self.focus -= 1;
|
self.focus -= 1;
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
Event::Key(Key::ShiftTab) if self.focus + 1 <
|
Event::Key(Key::ShiftTab) if self.focus + 1 < self.children.len() => {
|
||||||
self.children.len() => {
|
|
||||||
self.focus += 1;
|
self.focus += 1;
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
Event::Key(Key::Left) if self.orientation ==
|
Event::Key(Key::Left) if self.orientation == Orientation::Horizontal &&
|
||||||
Orientation::Horizontal &&
|
self.focus > 0 => {
|
||||||
self.focus > 0 => {
|
|
||||||
self.focus -= 1;
|
self.focus -= 1;
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
Event::Key(Key::Up) if self.orientation ==
|
Event::Key(Key::Up) if self.orientation == Orientation::Vertical &&
|
||||||
Orientation::Vertical &&
|
self.focus > 0 => {
|
||||||
self.focus > 0 => {
|
|
||||||
self.focus -= 1;
|
self.focus -= 1;
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
Event::Key(Key::Right) if self.orientation ==
|
Event::Key(Key::Right) if self.orientation == Orientation::Horizontal &&
|
||||||
Orientation::Horizontal &&
|
self.focus + 1 < self.children.len() => {
|
||||||
self.focus + 1 <
|
|
||||||
self.children.len() => {
|
|
||||||
self.focus += 1;
|
self.focus += 1;
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
Event::Key(Key::Down) if self.orientation ==
|
Event::Key(Key::Down) if self.orientation == Orientation::Vertical &&
|
||||||
Orientation::Vertical &&
|
self.focus + 1 < self.children.len() => {
|
||||||
self.focus + 1 <
|
|
||||||
self.children.len() => {
|
|
||||||
self.focus += 1;
|
self.focus += 1;
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,7 @@ impl DimensionRequest {
|
|||||||
pub fn reduced(self, offset: usize) -> Self {
|
pub fn reduced(self, offset: usize) -> Self {
|
||||||
match self {
|
match self {
|
||||||
DimensionRequest::Fixed(w) => DimensionRequest::Fixed(w - offset),
|
DimensionRequest::Fixed(w) => DimensionRequest::Fixed(w - offset),
|
||||||
DimensionRequest::AtMost(w) => {
|
DimensionRequest::AtMost(w) => DimensionRequest::AtMost(w - offset),
|
||||||
DimensionRequest::AtMost(w - offset)
|
|
||||||
}
|
|
||||||
DimensionRequest::Unknown => DimensionRequest::Unknown,
|
DimensionRequest::Unknown => DimensionRequest::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,7 @@ impl ScrollBase {
|
|||||||
self.content_height = content_height;
|
self.content_height = content_height;
|
||||||
|
|
||||||
if self.scrollable() {
|
if self.scrollable() {
|
||||||
self.start_line = min(self.start_line,
|
self.start_line = min(self.start_line, self.content_height - self.view_height);
|
||||||
self.content_height - self.view_height);
|
|
||||||
} else {
|
} else {
|
||||||
self.start_line = 0;
|
self.start_line = 0;
|
||||||
}
|
}
|
||||||
@ -70,8 +69,7 @@ impl ScrollBase {
|
|||||||
|
|
||||||
/// Scroll down by the given number of line, never going further than the bottom of the view.
|
/// 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) {
|
pub fn scroll_down(&mut self, n: usize) {
|
||||||
self.start_line = min(self.start_line + n,
|
self.start_line = min(self.start_line + n, self.content_height - self.view_height);
|
||||||
self.content_height - self.view_height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scroll up by the given number of lines, never going above the top of the view.
|
/// Scroll up by the given number of lines, never going above the top of the view.
|
||||||
@ -104,8 +102,7 @@ impl ScrollBase {
|
|||||||
where F: Fn(&Printer, usize)
|
where F: Fn(&Printer, usize)
|
||||||
{
|
{
|
||||||
// Print the content in a sub_printer
|
// Print the content in a sub_printer
|
||||||
let max_y = min(self.view_height,
|
let max_y = min(self.view_height, self.content_height - self.start_line);
|
||||||
self.content_height - self.start_line);
|
|
||||||
let w = if self.scrollable() {
|
let w = if self.scrollable() {
|
||||||
printer.size.x - 2
|
printer.size.x - 2
|
||||||
} else {
|
} else {
|
||||||
@ -114,9 +111,7 @@ impl ScrollBase {
|
|||||||
for y in 0..max_y {
|
for y in 0..max_y {
|
||||||
// Y is the actual coordinate of the line.
|
// Y is the actual coordinate of the line.
|
||||||
// The item ID is then Y + self.start_line
|
// The item ID is then Y + self.start_line
|
||||||
line_drawer(&printer.sub_printer(Vec2::new(0, y),
|
line_drawer(&printer.sub_printer(Vec2::new(0, y), Vec2::new(w, 1), true),
|
||||||
Vec2::new(w, 1),
|
|
||||||
true),
|
|
||||||
y + self.start_line);
|
y + self.start_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,15 +121,12 @@ impl ScrollBase {
|
|||||||
// We directly compute the size of the scrollbar (this allow use to avoid using floats).
|
// We directly compute the size of the scrollbar (this allow use to avoid using floats).
|
||||||
// (ratio) * max_height
|
// (ratio) * max_height
|
||||||
// Where ratio is ({start or end} / content.height)
|
// Where ratio is ({start or end} / content.height)
|
||||||
let height = max(1,
|
let height = max(1, self.view_height * self.view_height / self.content_height);
|
||||||
self.view_height * self.view_height /
|
|
||||||
self.content_height);
|
|
||||||
// Number of different possible positions
|
// Number of different possible positions
|
||||||
let steps = self.view_height - height + 1;
|
let steps = self.view_height - height + 1;
|
||||||
|
|
||||||
// Now
|
// Now
|
||||||
let start = steps * self.start_line /
|
let start = steps * self.start_line / (1 + self.content_height - self.view_height);
|
||||||
(1 + self.content_height - self.view_height);
|
|
||||||
|
|
||||||
let color = if printer.focused {
|
let color = if printer.focused {
|
||||||
ColorPair::Highlight
|
ColorPair::Highlight
|
||||||
|
@ -128,9 +128,7 @@ impl<T: 'static> View for SelectView<T> {
|
|||||||
|
|
||||||
let h = self.items.len();
|
let h = self.items.len();
|
||||||
let offset = self.align.v.get_offset(h, printer.size.y);
|
let offset = self.align.v.get_offset(h, printer.size.y);
|
||||||
let printer = &printer.sub_printer(Vec2::new(0, offset),
|
let printer = &printer.sub_printer(Vec2::new(0, offset), printer.size, true);
|
||||||
printer.size,
|
|
||||||
true);
|
|
||||||
|
|
||||||
self.scrollbase.draw(printer, |printer, i| {
|
self.scrollbase.draw(printer, |printer, i| {
|
||||||
let style = if i == self.focus {
|
let style = if i == self.focus {
|
||||||
@ -181,12 +179,9 @@ impl<T: 'static> View for SelectView<T> {
|
|||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
match event {
|
match event {
|
||||||
Event::Key(Key::Up) if self.focus > 0 => self.focus -= 1,
|
Event::Key(Key::Up) if self.focus > 0 => self.focus -= 1,
|
||||||
Event::Key(Key::Down) if self.focus + 1 <
|
Event::Key(Key::Down) if self.focus + 1 < self.items.len() => self.focus += 1,
|
||||||
self.items.len() => self.focus += 1,
|
|
||||||
Event::Key(Key::PageUp) => self.focus -= min(self.focus, 10),
|
Event::Key(Key::PageUp) => self.focus -= min(self.focus, 10),
|
||||||
Event::Key(Key::PageDown) => {
|
Event::Key(Key::PageDown) => self.focus = min(self.focus + 10, self.items.len() - 1),
|
||||||
self.focus = min(self.focus + 10, self.items.len() - 1)
|
|
||||||
}
|
|
||||||
Event::Key(Key::Home) => self.focus = 0,
|
Event::Key(Key::Home) => self.focus = 0,
|
||||||
Event::Key(Key::End) => self.focus = self.items.len() - 1,
|
Event::Key(Key::End) => self.focus = self.items.len() - 1,
|
||||||
Event::Key(Key::Enter) if self.select_cb.is_some() => {
|
Event::Key(Key::Enter) if self.select_cb.is_some() => {
|
||||||
@ -205,9 +200,7 @@ impl<T: 'static> View for SelectView<T> {
|
|||||||
let iter = self.items.iter().chain(self.items.iter());
|
let iter = self.items.iter().chain(self.items.iter());
|
||||||
if let Some((i, _)) = iter.enumerate()
|
if let Some((i, _)) = iter.enumerate()
|
||||||
.skip(self.focus + 1)
|
.skip(self.focus + 1)
|
||||||
.find(|&(_, item)| {
|
.find(|&(_, item)| item.label.starts_with(c)) {
|
||||||
item.label.starts_with(c)
|
|
||||||
}) {
|
|
||||||
// Apply modulo in case we have a hit
|
// Apply modulo in case we have a hit
|
||||||
// from the chained iterator
|
// from the chained iterator
|
||||||
self.focus = i % self.items.len();
|
self.focus = i % self.items.len();
|
||||||
|
@ -30,16 +30,12 @@ impl<T: View> ViewWrapper for ShadowView<T> {
|
|||||||
|
|
||||||
fn wrap_draw(&mut self, printer: &Printer) {
|
fn wrap_draw(&mut self, printer: &Printer) {
|
||||||
|
|
||||||
printer.with_color(ColorPair::Primary, |printer| {
|
// Draw the view background
|
||||||
// Draw the view background
|
for y in 1..printer.size.y - 1 {
|
||||||
for y in 1..printer.size.y - 1 {
|
printer.print_hline((1, y), printer.size.x - 2, " ");
|
||||||
printer.print_hline((1, y), printer.size.x - 2, " ");
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.view.draw(&printer.sub_printer(Vec2::new(1, 1),
|
self.view.draw(&printer.sub_printer(Vec2::new(1, 1), printer.size - (2, 2), true));
|
||||||
printer.size - (2, 2),
|
|
||||||
true));
|
|
||||||
|
|
||||||
let h = printer.size.y - 1;
|
let h = printer.size.y - 1;
|
||||||
let w = printer.size.x - 1;
|
let w = printer.size.x - 1;
|
||||||
|
@ -4,6 +4,7 @@ use vec::Vec2;
|
|||||||
use view::{DimensionRequest, Selector, ShadowView, SizeRequest, View};
|
use view::{DimensionRequest, Selector, ShadowView, SizeRequest, View};
|
||||||
use event::{Event, EventResult};
|
use event::{Event, EventResult};
|
||||||
use printer::Printer;
|
use printer::Printer;
|
||||||
|
use theme::ColorPair;
|
||||||
|
|
||||||
/// Simple stack of views.
|
/// Simple stack of views.
|
||||||
/// Only the top-most view is active and can receive input.
|
/// Only the top-most view is active and can receive input.
|
||||||
@ -48,13 +49,15 @@ impl StackView {
|
|||||||
impl View for StackView {
|
impl View for StackView {
|
||||||
fn draw(&mut self, printer: &Printer) {
|
fn draw(&mut self, printer: &Printer) {
|
||||||
let last = self.layers.len();
|
let last = self.layers.len();
|
||||||
for (i, v) in self.layers.iter_mut().enumerate() {
|
printer.with_color(ColorPair::Primary, |printer| {
|
||||||
// Center the view
|
for (i, v) in self.layers.iter_mut().enumerate() {
|
||||||
let size = v.size;
|
// Center the view
|
||||||
let offset = (printer.size - size) / 2;
|
let size = v.size;
|
||||||
// TODO: only draw focus for the top view
|
let offset = (printer.size - size) / 2;
|
||||||
v.view.draw(&printer.sub_printer(offset, size, i + 1 == last));
|
// TODO: only draw focus for the top view
|
||||||
}
|
v.view.draw(&printer.sub_printer(offset, size, i + 1 == last));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
|
@ -214,9 +214,7 @@ impl View for TextView {
|
|||||||
|
|
||||||
let h = self.rows.len();
|
let h = self.rows.len();
|
||||||
let offset = self.align.v.get_offset(h, printer.size.y);
|
let offset = self.align.v.get_offset(h, printer.size.y);
|
||||||
let printer = &printer.sub_printer(Vec2::new(0, offset),
|
let printer = &printer.sub_printer(Vec2::new(0, offset), printer.size, true);
|
||||||
printer.size,
|
|
||||||
true);
|
|
||||||
|
|
||||||
self.scrollbase.draw(printer, |printer, i| {
|
self.scrollbase.draw(printer, |printer, i| {
|
||||||
let row = &self.rows[i];
|
let row = &self.rows[i];
|
||||||
@ -235,13 +233,9 @@ impl View for TextView {
|
|||||||
match event {
|
match event {
|
||||||
Event::Key(Key::Home) => self.scrollbase.scroll_top(),
|
Event::Key(Key::Home) => self.scrollbase.scroll_top(),
|
||||||
Event::Key(Key::End) => self.scrollbase.scroll_bottom(),
|
Event::Key(Key::End) => self.scrollbase.scroll_bottom(),
|
||||||
Event::Key(Key::Up) if self.scrollbase.can_scroll_up() => {
|
Event::Key(Key::Up) if self.scrollbase.can_scroll_up() => self.scrollbase.scroll_up(1),
|
||||||
self.scrollbase.scroll_up(1)
|
|
||||||
}
|
|
||||||
Event::Key(Key::Down) if self.scrollbase
|
Event::Key(Key::Down) if self.scrollbase
|
||||||
.can_scroll_down() => {
|
.can_scroll_down() => self.scrollbase.scroll_down(1),
|
||||||
self.scrollbase.scroll_down(1)
|
|
||||||
}
|
|
||||||
Event::Key(Key::PageDown) => self.scrollbase.scroll_down(10),
|
Event::Key(Key::PageDown) => self.scrollbase.scroll_down(10),
|
||||||
Event::Key(Key::PageUp) => self.scrollbase.scroll_up(10),
|
Event::Key(Key::PageUp) => self.scrollbase.scroll_up(10),
|
||||||
_ => return EventResult::Ignored,
|
_ => return EventResult::Ignored,
|
||||||
@ -254,9 +248,7 @@ impl View for TextView {
|
|||||||
match (size.w, size.h) {
|
match (size.w, size.h) {
|
||||||
// If we have no directive, ask for a single big line.
|
// If we have no directive, ask for a single big line.
|
||||||
// TODO: what if the text has newlines??
|
// TODO: what if the text has newlines??
|
||||||
(DimensionRequest::Unknown, DimensionRequest::Unknown) => {
|
(DimensionRequest::Unknown, DimensionRequest::Unknown) => self.get_ideal_size(),
|
||||||
self.get_ideal_size()
|
|
||||||
}
|
|
||||||
(DimensionRequest::Fixed(w), _) => {
|
(DimensionRequest::Fixed(w), _) => {
|
||||||
// In a BoxView or something.
|
// In a BoxView or something.
|
||||||
let h = self.get_num_lines(w);
|
let h = self.get_num_lines(w);
|
||||||
@ -291,8 +283,7 @@ impl View for TextView {
|
|||||||
// Compute the text rows.
|
// Compute the text rows.
|
||||||
self.rows = LinesIterator::new(&self.content, size.x).collect();
|
self.rows = LinesIterator::new(&self.content, size.x).collect();
|
||||||
if self.rows.len() > size.y {
|
if self.rows.len() > size.y {
|
||||||
self.rows = LinesIterator::new(&self.content, size.x - 2)
|
self.rows = LinesIterator::new(&self.content, size.x - 2).collect();
|
||||||
.collect();
|
|
||||||
}
|
}
|
||||||
self.scrollbase.set_heights(size.y, self.rows.len());
|
self.scrollbase.set_heights(size.y, self.rows.len());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user