mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +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::rc::Rc;
|
||||
|
||||
use ncurses;
|
||||
|
||||
use Cursive;
|
||||
|
||||
/// Callback is a function that can be triggered by an event.
|
||||
@ -130,128 +128,6 @@ pub enum Key {
|
||||
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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
|
60
src/lib.rs
60
src/lib.rs
@ -39,6 +39,10 @@ mod menubar;
|
||||
mod div;
|
||||
mod utf8;
|
||||
|
||||
mod backend;
|
||||
|
||||
use backend::{Backend, NcursesBackend};
|
||||
|
||||
use std::any::Any;
|
||||
use std::rc::Rc;
|
||||
use std::collections::HashMap;
|
||||
@ -49,7 +53,7 @@ use printer::Printer;
|
||||
use view::View;
|
||||
use view::{Selector, StackView};
|
||||
|
||||
use event::{Callback, Event, EventResult, Key, ToEvent};
|
||||
use event::{Callback, Event, EventResult, ToEvent};
|
||||
|
||||
/// Identifies a screen in the cursive ROOT.
|
||||
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 {
|
||||
/// Creates a new Cursive root, and initialize ncurses.
|
||||
pub fn new() -> Self {
|
||||
// Default delay is way too long. 25 is imperceptible yet works fine.
|
||||
std::env::set_var("ESCDELAY", "25");
|
||||
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);
|
||||
B::init();
|
||||
|
||||
let theme = theme::load_default();
|
||||
// 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 {
|
||||
theme: theme,
|
||||
@ -148,11 +149,7 @@ impl Cursive {
|
||||
///
|
||||
/// Call with fps=0 to disable (default value).
|
||||
pub fn set_fps(&self, fps: u32) {
|
||||
if fps == 0 {
|
||||
ncurses::timeout(-1);
|
||||
} else {
|
||||
ncurses::timeout(1000 / fps as i32);
|
||||
}
|
||||
B::set_refresh_rate(fps)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the currently active screen.
|
||||
@ -235,9 +232,7 @@ impl Cursive {
|
||||
|
||||
/// Returns the size of the screen, in characters.
|
||||
pub fn screen_size(&self) -> Vec2 {
|
||||
let mut x: i32 = 0;
|
||||
let mut y: i32 = 0;
|
||||
ncurses::getmaxyx(ncurses::stdscr, &mut y, &mut x);
|
||||
let (x, y) = B::screen_size();
|
||||
|
||||
Vec2 {
|
||||
x: x as usize,
|
||||
@ -258,35 +253,20 @@ impl Cursive {
|
||||
// Draw the currently active screen
|
||||
// If the menubar is active, nothing else can be.
|
||||
let offset = if self.menu.autohide {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let selected = self.menu.selected;
|
||||
self.screen_mut()
|
||||
.draw(&printer.sub_printer(Vec2::new(0, offset),
|
||||
printer.size,
|
||||
!selected));
|
||||
.draw(&printer.sub_printer(Vec2::new(0, offset), printer.size, !selected));
|
||||
|
||||
// Draw the menubar?
|
||||
if self.menu.selected || !self.menu.autohide {
|
||||
self.menu.draw(&printer);
|
||||
}
|
||||
|
||||
ncurses::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))
|
||||
}
|
||||
B::refresh();
|
||||
}
|
||||
|
||||
/// Runs the event loop.
|
||||
@ -300,7 +280,7 @@ impl Cursive {
|
||||
// Do we need to redraw everytime?
|
||||
// Probably, actually.
|
||||
// TODO: Do we actually need to clear everytime?
|
||||
ncurses::clear();
|
||||
B::clear();
|
||||
// TODO: Do we need to re-layout everytime?
|
||||
self.layout();
|
||||
// TODO: Do we need to redraw every view every time?
|
||||
@ -309,7 +289,7 @@ impl Cursive {
|
||||
|
||||
// Wait for next event.
|
||||
// (If set_fps was called, this returns -1 now and then)
|
||||
let event = Cursive::poll_event();
|
||||
let event = B::poll_event();
|
||||
|
||||
// Event dispatch order:
|
||||
// * Focused element:
|
||||
@ -339,6 +319,6 @@ impl Cursive {
|
||||
|
||||
impl Drop for Cursive {
|
||||
fn drop(&mut self) {
|
||||
ncurses::endwin();
|
||||
B::finish();
|
||||
}
|
||||
}
|
||||
|
@ -35,8 +35,7 @@ impl MenuTree {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn leaf<F: 'static + Fn(&mut Cursive)>(&mut self, title: &str, cb: F)
|
||||
-> &mut Self {
|
||||
pub fn leaf<F: 'static + Fn(&mut Cursive)>(&mut self, title: &str, cb: F) -> &mut Self {
|
||||
self.children
|
||||
.push(MenuItem::Leaf(title.to_string(), Rc::new(Box::new(cb))));
|
||||
self
|
||||
|
@ -44,12 +44,8 @@ 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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,11 @@
|
||||
|
||||
use std::cmp::min;
|
||||
|
||||
use ncurses;
|
||||
use backend::Backend;
|
||||
|
||||
use theme::{ColorPair, Theme};
|
||||
use B;
|
||||
|
||||
use theme::{ColorPair, Theme, Effect};
|
||||
use vec::{ToVec2, Vec2};
|
||||
|
||||
/// Convenient interface to draw on a subset of the screen.
|
||||
@ -49,11 +51,9 @@ impl Printer {
|
||||
|
||||
let p = p + self.offset;
|
||||
if text.contains('%') {
|
||||
ncurses::mvprintw(p.y as i32,
|
||||
p.x as i32,
|
||||
&text.replace("%", "%%"));
|
||||
B::print_at((p.x, p.y), &text.replace("%", "%%"));
|
||||
} 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;
|
||||
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;
|
||||
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)
|
||||
where F: Fn(&Printer)
|
||||
where F: FnOnce(&Printer)
|
||||
{
|
||||
self.with_style(ncurses::COLOR_PAIR(c.ncurses_id()), f);
|
||||
ncurses::attron(ncurses::COLOR_PAIR(ColorPair::Primary.ncurses_id()));
|
||||
B::with_color(c, || f(self));
|
||||
}
|
||||
|
||||
/// Same as `with_color`, but apply a ncurses style instead,
|
||||
/// like `ncurses::A_BOLD()` or `ncurses::A_REVERSE()`.
|
||||
///
|
||||
/// Will probably use a cursive enum some day.
|
||||
pub fn with_style<F>(&self, style: ncurses::attr_t, f: F)
|
||||
where F: Fn(&Printer)
|
||||
pub fn with_effect<F>(&self, effect: Effect, f: F)
|
||||
where F: FnOnce(&Printer)
|
||||
{
|
||||
ncurses::attron(style);
|
||||
f(self);
|
||||
ncurses::attroff(style);
|
||||
B::with_effect(effect, || f(self));
|
||||
}
|
||||
|
||||
/// Prints a rectangular box.
|
||||
@ -138,12 +135,8 @@ 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 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.
|
||||
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,
|
||||
|
15
src/theme.rs
15
src/theme.rs
@ -8,6 +8,11 @@ use std::path::Path;
|
||||
use ncurses;
|
||||
use toml;
|
||||
|
||||
pub enum Effect {
|
||||
Simple,
|
||||
Reverse,
|
||||
}
|
||||
|
||||
/// Represents the color of a character and its background.
|
||||
#[derive(Clone,Copy)]
|
||||
pub enum ColorPair {
|
||||
@ -97,14 +102,10 @@ impl Theme {
|
||||
|
||||
fn apply(&self) {
|
||||
Theme::apply_color(ColorPair::Background,
|
||||
&self.colors.background,
|
||||
&self.colors.view,
|
||||
&self.colors.background);
|
||||
Theme::apply_color(ColorPair::Shadow,
|
||||
&self.colors.shadow,
|
||||
&self.colors.shadow);
|
||||
Theme::apply_color(ColorPair::Primary,
|
||||
&self.colors.primary,
|
||||
&self.colors.view);
|
||||
Theme::apply_color(ColorPair::Shadow, &self.colors.shadow, &self.colors.shadow);
|
||||
Theme::apply_color(ColorPair::Primary, &self.colors.primary, &self.colors.view);
|
||||
Theme::apply_color(ColorPair::Secondary,
|
||||
&self.colors.secondary,
|
||||
&self.colors.view);
|
||||
|
@ -50,9 +50,7 @@ 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,
|
||||
}
|
||||
}
|
||||
|
@ -121,9 +121,7 @@ impl View for Dialog {
|
||||
};
|
||||
let overhead = self.padding + self.borders;
|
||||
let mut offset = overhead.left +
|
||||
self.align.h.get_offset(width,
|
||||
printer.size.x -
|
||||
overhead.horizontal());
|
||||
self.align.h.get_offset(width, printer.size.x - overhead.horizontal());
|
||||
let y = printer.size.y - self.padding.bottom - self.borders.bottom - 1;
|
||||
|
||||
for (i, button) in self.buttons.iter_mut().enumerate() {
|
||||
@ -139,12 +137,10 @@ impl View for Dialog {
|
||||
}
|
||||
|
||||
// What do we have left?
|
||||
let inner_size = printer.size - Vec2::new(0, height) -
|
||||
self.borders.combined() -
|
||||
let inner_size = printer.size - Vec2::new(0, height) - self.borders.combined() -
|
||||
self.padding.combined();
|
||||
|
||||
self.content.draw(&printer.sub_printer(self.borders.top_left() +
|
||||
self.padding.top_left(),
|
||||
self.content.draw(&printer.sub_printer(self.borders.top_left() + self.padding.top_left(),
|
||||
inner_size,
|
||||
self.focus == Focus::Content));
|
||||
|
||||
@ -156,16 +152,14 @@ impl View for Dialog {
|
||||
printer.print((x - 2, 0), "┤ ");
|
||||
printer.print((x + len, 0), " ├");
|
||||
|
||||
printer.with_color(ColorPair::TitlePrimary,
|
||||
|p| p.print((x, 0), &self.title));
|
||||
printer.with_color(ColorPair::TitlePrimary, |p| p.print((x, 0), &self.title));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn get_min_size(&self, req: SizeRequest) -> Vec2 {
|
||||
// Padding and borders are not available for kids.
|
||||
let content_req = req.reduced(self.padding.combined() +
|
||||
self.borders.combined());
|
||||
let content_req = req.reduced(self.padding.combined() + self.borders.combined());
|
||||
let content_size = self.content.get_min_size(content_req);
|
||||
|
||||
let mut buttons_size = Vec2::new(0, 0);
|
||||
@ -251,8 +245,8 @@ impl View for Dialog {
|
||||
}
|
||||
// Left and Right move to other buttons
|
||||
Event::Key(Key::Right) if i + 1 <
|
||||
self.buttons
|
||||
.len() => {
|
||||
self.buttons
|
||||
.len() => {
|
||||
self.focus = Focus::Button(i + 1);
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
use ncurses;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use std::cmp::min;
|
||||
|
||||
use theme::ColorPair;
|
||||
use theme::{ColorPair, Effect};
|
||||
use vec::Vec2;
|
||||
use view::{IdView, SizeRequest, View};
|
||||
use event::*;
|
||||
@ -87,13 +86,12 @@ impl View for EditView {
|
||||
// let style = if focused { color::HIGHLIGHT } else { color::HIGHLIGHT_INACTIVE };
|
||||
let len = self.content.chars().count();
|
||||
printer.with_color(ColorPair::Secondary, |printer| {
|
||||
printer.with_style(ncurses::A_REVERSE(), |printer| {
|
||||
printer.with_effect(Effect::Reverse, |printer| {
|
||||
if len < self.last_length {
|
||||
printer.print((0, 0), &self.content);
|
||||
printer.print_hline((len, 0), printer.size.x - len, "_");
|
||||
} else {
|
||||
let visible_end = min(self.content.len(),
|
||||
self.offset + self.last_length);
|
||||
let visible_end = min(self.content.len(), self.offset + self.last_length);
|
||||
|
||||
let content = &self.content[self.offset..visible_end];
|
||||
printer.print((0, 0), content);
|
||||
@ -151,10 +149,7 @@ impl View for EditView {
|
||||
Key::Home => self.cursor = 0,
|
||||
Key::End => self.cursor = self.content.chars().count(),
|
||||
Key::Left if self.cursor > 0 => self.cursor -= 1,
|
||||
Key::Right if self.cursor <
|
||||
self.content.chars().count() => {
|
||||
self.cursor += 1
|
||||
}
|
||||
Key::Right if self.cursor < self.content.chars().count() => self.cursor += 1,
|
||||
Key::Backspace if self.cursor > 0 => {
|
||||
self.cursor -= 1;
|
||||
remove_char(&mut self.content, self.cursor);
|
||||
|
@ -113,9 +113,7 @@ impl View for LinearLayout {
|
||||
// Use pre-computed sizes
|
||||
let mut offset = Vec2::zero();
|
||||
for (i, child) in self.children.iter_mut().enumerate() {
|
||||
child.view.draw(&printer.sub_printer(offset,
|
||||
child.size,
|
||||
i == self.focus));
|
||||
child.view.draw(&printer.sub_printer(offset, child.size, i == self.focus));
|
||||
|
||||
*self.orientation.get_ref(&mut offset) += self.orientation
|
||||
.get(&child.size);
|
||||
@ -130,9 +128,7 @@ impl View for LinearLayout {
|
||||
};
|
||||
let min_sizes: Vec<Vec2> = self.children
|
||||
.iter()
|
||||
.map(|child| {
|
||||
child.view.get_min_size(req)
|
||||
})
|
||||
.map(|child| child.view.get_min_size(req))
|
||||
.collect();
|
||||
let min_size = self.orientation.stack(min_sizes.iter());
|
||||
|
||||
@ -153,14 +149,12 @@ impl View for LinearLayout {
|
||||
};
|
||||
|
||||
|
||||
for (child, (child_size, extra)) in
|
||||
self.children
|
||||
.iter_mut()
|
||||
.zip(min_sizes.iter().zip(extras.iter())) {
|
||||
for (child, (child_size, extra)) in self.children
|
||||
.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);
|
||||
}
|
||||
@ -194,34 +188,27 @@ 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 &&
|
||||
self.focus > 0 => {
|
||||
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 &&
|
||||
self.focus > 0 => {
|
||||
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)
|
||||
}
|
||||
|
@ -16,9 +16,7 @@ impl DimensionRequest {
|
||||
pub fn reduced(self, offset: usize) -> Self {
|
||||
match self {
|
||||
DimensionRequest::Fixed(w) => DimensionRequest::Fixed(w - offset),
|
||||
DimensionRequest::AtMost(w) => {
|
||||
DimensionRequest::AtMost(w - offset)
|
||||
}
|
||||
DimensionRequest::AtMost(w) => DimensionRequest::AtMost(w - offset),
|
||||
DimensionRequest::Unknown => DimensionRequest::Unknown,
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,7 @@ 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;
|
||||
}
|
||||
@ -70,8 +69,7 @@ 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.
|
||||
@ -104,8 +102,7 @@ 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 - 2
|
||||
} else {
|
||||
@ -114,9 +111,7 @@ 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);
|
||||
}
|
||||
|
||||
@ -126,15 +121,12 @@ 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 {
|
||||
ColorPair::Highlight
|
||||
|
@ -128,9 +128,7 @@ 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| {
|
||||
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 {
|
||||
match event {
|
||||
Event::Key(Key::Up) if self.focus > 0 => self.focus -= 1,
|
||||
Event::Key(Key::Down) if self.focus + 1 <
|
||||
self.items.len() => self.focus += 1,
|
||||
Event::Key(Key::Down) if self.focus + 1 < self.items.len() => self.focus += 1,
|
||||
Event::Key(Key::PageUp) => self.focus -= min(self.focus, 10),
|
||||
Event::Key(Key::PageDown) => {
|
||||
self.focus = min(self.focus + 10, self.items.len() - 1)
|
||||
}
|
||||
Event::Key(Key::PageDown) => self.focus = min(self.focus + 10, self.items.len() - 1),
|
||||
Event::Key(Key::Home) => self.focus = 0,
|
||||
Event::Key(Key::End) => self.focus = self.items.len() - 1,
|
||||
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());
|
||||
if let Some((i, _)) = iter.enumerate()
|
||||
.skip(self.focus + 1)
|
||||
.find(|&(_, item)| {
|
||||
item.label.starts_with(c)
|
||||
}) {
|
||||
.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();
|
||||
|
@ -30,16 +30,12 @@ impl<T: View> ViewWrapper for ShadowView<T> {
|
||||
|
||||
fn wrap_draw(&mut self, printer: &Printer) {
|
||||
|
||||
printer.with_color(ColorPair::Primary, |printer| {
|
||||
// Draw the view background
|
||||
for y in 1..printer.size.y - 1 {
|
||||
printer.print_hline((1, y), printer.size.x - 2, " ");
|
||||
}
|
||||
});
|
||||
// Draw the view background
|
||||
for y in 1..printer.size.y - 1 {
|
||||
printer.print_hline((1, y), printer.size.x - 2, " ");
|
||||
}
|
||||
|
||||
self.view.draw(&printer.sub_printer(Vec2::new(1, 1),
|
||||
printer.size - (2, 2),
|
||||
true));
|
||||
self.view.draw(&printer.sub_printer(Vec2::new(1, 1), printer.size - (2, 2), true));
|
||||
|
||||
let h = printer.size.y - 1;
|
||||
let w = printer.size.x - 1;
|
||||
|
@ -4,6 +4,7 @@ use vec::Vec2;
|
||||
use view::{DimensionRequest, Selector, ShadowView, SizeRequest, View};
|
||||
use event::{Event, EventResult};
|
||||
use printer::Printer;
|
||||
use theme::ColorPair;
|
||||
|
||||
/// Simple stack of views.
|
||||
/// Only the top-most view is active and can receive input.
|
||||
@ -48,13 +49,15 @@ impl StackView {
|
||||
impl View for StackView {
|
||||
fn draw(&mut self, printer: &Printer) {
|
||||
let last = self.layers.len();
|
||||
for (i, v) in self.layers.iter_mut().enumerate() {
|
||||
// Center the view
|
||||
let size = v.size;
|
||||
let offset = (printer.size - size) / 2;
|
||||
// TODO: only draw focus for the top view
|
||||
v.view.draw(&printer.sub_printer(offset, size, i + 1 == last));
|
||||
}
|
||||
printer.with_color(ColorPair::Primary, |printer| {
|
||||
for (i, v) in self.layers.iter_mut().enumerate() {
|
||||
// Center the view
|
||||
let size = v.size;
|
||||
let offset = (printer.size - size) / 2;
|
||||
// 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 {
|
||||
|
@ -214,9 +214,7 @@ impl View for TextView {
|
||||
|
||||
let h = self.rows.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| {
|
||||
let row = &self.rows[i];
|
||||
@ -235,13 +233,9 @@ impl View for TextView {
|
||||
match event {
|
||||
Event::Key(Key::Home) => self.scrollbase.scroll_top(),
|
||||
Event::Key(Key::End) => self.scrollbase.scroll_bottom(),
|
||||
Event::Key(Key::Up) if self.scrollbase.can_scroll_up() => {
|
||||
self.scrollbase.scroll_up(1)
|
||||
}
|
||||
Event::Key(Key::Up) if self.scrollbase.can_scroll_up() => self.scrollbase.scroll_up(1),
|
||||
Event::Key(Key::Down) if self.scrollbase
|
||||
.can_scroll_down() => {
|
||||
self.scrollbase.scroll_down(1)
|
||||
}
|
||||
.can_scroll_down() => self.scrollbase.scroll_down(1),
|
||||
Event::Key(Key::PageDown) => self.scrollbase.scroll_down(10),
|
||||
Event::Key(Key::PageUp) => self.scrollbase.scroll_up(10),
|
||||
_ => return EventResult::Ignored,
|
||||
@ -254,9 +248,7 @@ impl View for TextView {
|
||||
match (size.w, size.h) {
|
||||
// If we have no directive, ask for a single big line.
|
||||
// TODO: what if the text has newlines??
|
||||
(DimensionRequest::Unknown, DimensionRequest::Unknown) => {
|
||||
self.get_ideal_size()
|
||||
}
|
||||
(DimensionRequest::Unknown, DimensionRequest::Unknown) => self.get_ideal_size(),
|
||||
(DimensionRequest::Fixed(w), _) => {
|
||||
// In a BoxView or something.
|
||||
let h = self.get_num_lines(w);
|
||||
@ -291,8 +283,7 @@ impl View for TextView {
|
||||
// Compute the text rows.
|
||||
self.rows = LinesIterator::new(&self.content, size.x).collect();
|
||||
if self.rows.len() > size.y {
|
||||
self.rows = LinesIterator::new(&self.content, size.x - 2)
|
||||
.collect();
|
||||
self.rows = LinesIterator::new(&self.content, size.x - 2).collect();
|
||||
}
|
||||
self.scrollbase.set_heights(size.y, self.rows.len());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user