Crossterm Performance Update (#361)

This commit is contained in:
Timon 2019-07-30 18:48:50 +02:00 committed by Alexandre Bury
parent ede8423e92
commit bddb1a9dde
5 changed files with 72 additions and 99 deletions

View File

@ -7,15 +7,17 @@
use crate::vec::Vec2;
use crate::{backend, theme};
use crossterm::{
cursor, input, terminal, AlternateScreen, AsyncReader, Attribute,
ClearType, Color, Colored, InputEvent as CInputEvent,
KeyEvent as CKeyEvent, MouseButton as CMouseButton,
MouseEvent as CMouseEvent, Terminal, TerminalCursor,
cursor, execute, input, queue, terminal, AlternateScreen, AsyncReader,
Attribute, Clear, ClearType, Color, Colored, Command, Goto, Hide,
InputEvent as CInputEvent, KeyEvent as CKeyEvent,
MouseButton as CMouseButton, MouseEvent as CMouseEvent, Output, RawScreen,
SetAttr, SetBg, SetFg, Show, Terminal, TerminalCursor,
};
use crate::event::{Event, Key, MouseButton, MouseEvent};
use core::borrow::BorrowMut;
use std::cell::{Cell, RefCell};
use std::io::{self, Stdout, Write};
use std::io::{self, BufWriter, Stdout, StdoutLock, Write};
/// Backend using crossterm
pub struct Backend {
@ -24,7 +26,7 @@ pub struct Backend {
// reader to read user input async.
async_reader: AsyncReader,
_alternate_screen: AlternateScreen,
stdout: RefCell<Stdout>,
stdout: RefCell<BufWriter<Stdout>>,
cursor: TerminalCursor,
terminal: Terminal,
}
@ -48,22 +50,31 @@ impl Backend {
last_button: None,
async_reader,
_alternate_screen,
stdout: RefCell::new(io::stdout()),
stdout: RefCell::new(BufWriter::new(io::stdout())),
terminal: terminal(),
cursor: cursor(),
}))
}
fn apply_colors(&self, colors: theme::ColorPair) {
with_color(colors.front, |c| self.write(Colored::Fg(*c)));
with_color(colors.back, |c| self.write(Colored::Bg(*c)));
with_color(colors.front, |c| {
queue!(self.stdout.borrow_mut(), SetFg(*c))
});
with_color(colors.back, |c| {
queue!(self.stdout.borrow_mut(), SetBg(*c))
});
}
fn write<T>(&self, content: T)
where
T: std::fmt::Display,
{
write!(self.stdout.borrow_mut(), "{}", format!("{}", content)).unwrap()
write!(self.stdout.borrow_mut(), "{}", format!("{}", content))
.unwrap();
}
fn set_attr(&self, attr: Attribute) {
queue!(self.stdout.borrow_mut(), SetAttr(attr));
}
fn map_key(&mut self, event: CInputEvent) -> Event {
@ -164,11 +175,16 @@ impl backend::Backend for Backend {
}
fn finish(&mut self) {
self.cursor.goto(1, 1).unwrap();
self.terminal.clear(ClearType::All).unwrap();
self.write(Attribute::Reset);
execute!(
self.stdout.borrow_mut(),
Goto(0, 0),
Clear(ClearType::All),
SetBg(Color::Reset),
SetFg(Color::Reset),
SetAttr(Attribute::Reset),
Show
);
input().disable_mouse_mode().unwrap();
cursor().show().unwrap();
}
fn refresh(&mut self) {
@ -182,11 +198,12 @@ impl backend::Backend for Backend {
fn screen_size(&self) -> Vec2 {
let size = self.terminal.terminal_size();
Vec2::from(size) + (1, 1)
Vec2::from(size)
}
fn print_at(&self, pos: Vec2, text: &str) {
self.cursor.goto(pos.x as u16, pos.y as u16).unwrap();
queue!(self.stdout.borrow_mut(), Goto(pos.x as u16, pos.y as u16))
.unwrap();
self.write(text);
}
@ -194,7 +211,7 @@ impl backend::Backend for Backend {
if repetitions > 0 {
let mut out = self.stdout.borrow_mut();
self.cursor.goto(pos.x as u16, pos.y as u16).unwrap();
queue!(out, Goto(pos.x as u16, pos.y as u16)).unwrap();
// as I (Timon) wrote this I figured out that calling `write_str` for unix was flushing the stdout.
// Current work aground is writing bytes instead of a string to the terminal.
@ -214,7 +231,7 @@ impl backend::Backend for Backend {
back: color,
});
self.terminal.clear(ClearType::All).unwrap();
queue!(self.stdout.borrow_mut(), Clear(ClearType::All)).unwrap();
}
fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair {
@ -231,22 +248,22 @@ impl backend::Backend for Backend {
fn set_effect(&self, effect: theme::Effect) {
match effect {
theme::Effect::Simple => (),
theme::Effect::Reverse => self.write(Attribute::Reverse),
theme::Effect::Bold => self.write(Attribute::Bold),
theme::Effect::Italic => self.write(Attribute::Italic),
theme::Effect::Strikethrough => self.write(Attribute::CrossedOut),
theme::Effect::Underline => self.write(Attribute::Underlined),
theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
theme::Effect::Bold => self.set_attr(Attribute::Bold),
theme::Effect::Italic => self.set_attr(Attribute::Italic),
theme::Effect::Strikethrough => self.set_attr(Attribute::CrossedOut),
theme::Effect::Underline => self.set_attr(Attribute::Underlined),
}
}
fn unset_effect(&self, effect: theme::Effect) {
match effect {
theme::Effect::Simple => (),
theme::Effect::Reverse => self.write(Attribute::Reverse),
theme::Effect::Bold => self.write(Attribute::NoBold),
theme::Effect::Italic => self.write(Attribute::NoItalic),
theme::Effect::Strikethrough => self.write(Attribute::NotCrossedOut),
theme::Effect::Underline => self.write(Attribute::Underlined),
theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
theme::Effect::Bold => self.set_attr(Attribute::NoBold),
theme::Effect::Italic => self.set_attr(Attribute::NoItalic),
theme::Effect::Strikethrough => self.set_attr(Attribute::NotCrossedOut),
theme::Effect::Underline => self.set_attr(Attribute::Underlined),
}
}
}

View File

@ -97,8 +97,7 @@ impl Core {
/// Returns a sub-printer ready to draw the content.
pub fn sub_printer<'a, 'b>(
&self,
printer: &Printer<'a, 'b>,
&self, printer: &Printer<'a, 'b>,
) -> Printer<'a, 'b> {
// Draw scrollbar?
let scrolling = self.is_scrolling();
@ -185,9 +184,7 @@ impl Core {
/// Handle an event after processing by the content.
pub fn on_inner_event(
&mut self,
event: Event,
inner_result: EventResult,
&mut self, event: Event, inner_result: EventResult,
important_area: Rect,
) -> EventResult {
match inner_result {
@ -365,9 +362,7 @@ impl Core {
/// Performs `View::call_on_any()`
pub fn call_on_any<'a, F>(
&mut self,
selector: &Selector<'_>,
cb: AnyCb<'a>,
&mut self, selector: &Selector<'_>, cb: AnyCb<'a>,
inner_call_on_any: F,
) where
F: FnOnce(&Selector, AnyCb),
@ -377,9 +372,7 @@ impl Core {
/// Performs `View::focus_view()`
pub fn focus_view<F>(
&mut self,
selector: &Selector<'_>,
inner_focus_view: F,
&mut self, selector: &Selector<'_>, inner_focus_view: F,
) -> Result<(), ()>
where
F: FnOnce(&Selector) -> Result<(), ()>,
@ -413,8 +406,7 @@ impl Core {
/// Sets the padding between content and scrollbar.
pub fn set_scrollbar_padding<V: Into<Vec2>>(
&mut self,
scrollbar_padding: V,
&mut self, scrollbar_padding: V,
) {
self.scrollbar_padding = scrollbar_padding.into();
}
@ -423,8 +415,7 @@ impl Core {
///
/// Chainable variant.
pub fn scrollbar_padding<V: Into<Vec2>>(
self,
scrollbar_padding: V,
self, scrollbar_padding: V,
) -> Self {
self.with(|s| s.set_scrollbar_padding(scrollbar_padding))
}

View File

@ -54,9 +54,7 @@ impl Default for ScrollStrategy {
/// }
/// ```
pub fn on_event<T, OnEvent, ImportantArea>(
scroller: &mut T,
event: Event,
on_event: OnEvent,
scroller: &mut T, event: Event, on_event: OnEvent,
important_area: ImportantArea,
) -> EventResult
where
@ -75,9 +73,7 @@ where
/// Performs `View::important_area` on a `scroll::Scroller`.
pub fn important_area<T, ImportantArea>(
scroller: &T,
size: Vec2,
mut important_area: ImportantArea,
scroller: &T, size: Vec2, mut important_area: ImportantArea,
) -> Rect
where
T: Scroller,
@ -96,10 +92,7 @@ where
/// Performs `View::layout` on a `scroll::Scroller`.
pub fn layout<T, Layout, RequiredSize>(
scroller: &mut T,
size: Vec2,
needs_relayout: bool,
layout: Layout,
scroller: &mut T, size: Vec2, needs_relayout: bool, layout: Layout,
required_size: RequiredSize,
) where
T: Scroller,
@ -118,9 +111,7 @@ pub fn layout<T, Layout, RequiredSize>(
/// Performs `View::required_size` on a `scroll::Scroller`.
pub fn required_size<T, RequiredSize>(
scroller: &mut T,
size: Vec2,
needs_relayout: bool,
scroller: &mut T, size: Vec2, needs_relayout: bool,
required_size: RequiredSize,
) -> Vec2
where
@ -149,9 +140,7 @@ where
///
/// This is an alternative to `scroll::draw()` when you just need to print individual lines.
pub fn draw_lines<T, LineDrawer>(
scroller: &T,
printer: &Printer,
mut line_drawer: LineDrawer,
scroller: &T, printer: &Printer, mut line_drawer: LineDrawer,
) where
T: Scroller,
LineDrawer: FnMut(&T, &Printer, usize),
@ -170,11 +159,8 @@ pub fn draw_lines<T, LineDrawer>(
///
/// `left_border` will be called for each row to draw the left border for the given line number.
pub fn draw_frame<T, LeftBorder, TopBorder, RightBorder, BottomBorder>(
scroller: &T,
printer: &Printer,
mut left_border: LeftBorder,
mut top_border: TopBorder,
mut right_border: RightBorder,
scroller: &T, printer: &Printer, mut left_border: LeftBorder,
mut top_border: TopBorder, mut right_border: RightBorder,
mut bottom_border: BottomBorder,
) where
T: Scroller,
@ -224,9 +210,7 @@ pub fn draw_frame<T, LeftBorder, TopBorder, RightBorder, BottomBorder>(
///
/// It will print a box with the appropriate `├`, `┤` and so on.
pub fn draw_box_frame<T, IsHDelim, IsVDelim>(
scroller: &T,
printer: &Printer,
is_h_delim: IsHDelim,
scroller: &T, printer: &Printer, is_h_delim: IsHDelim,
is_v_delim: IsVDelim,
) where
T: Scroller,

View File

@ -11,9 +11,7 @@ use crate::Vec2;
/// Implements `View::draw` over the `model`.
pub fn draw<Model, GetScroller, Draw>(
printer: &Printer,
model: &Model,
mut get_scroller: GetScroller,
printer: &Printer, model: &Model, mut get_scroller: GetScroller,
inner_draw: Draw,
) where
Model: ?Sized,
@ -32,12 +30,8 @@ pub fn draw<Model, GetScroller, Draw>(
///
/// Returns (Inner size, Outer size, New scrolling)
fn sizes_when_scrolling<Model, GetScroller, RequiredSize>(
constraint: Vec2,
scrolling: XY<bool>,
strict: bool,
model: &mut Model,
get_scroller: &mut GetScroller,
required_size: &mut RequiredSize,
constraint: Vec2, scrolling: XY<bool>, strict: bool, model: &mut Model,
get_scroller: &mut GetScroller, required_size: &mut RequiredSize,
) -> (Vec2, Vec2, XY<bool>)
where
Model: ?Sized,
@ -83,12 +77,8 @@ where
///
/// Returns (Inner size, Outer size)
fn sizes<Model, GetScroller, RequiredSize>(
constraint: Vec2,
strict: bool,
needs_relayout: bool,
model: &mut Model,
get_scroller: &mut GetScroller,
required_size: &mut RequiredSize,
constraint: Vec2, strict: bool, needs_relayout: bool, model: &mut Model,
get_scroller: &mut GetScroller, required_size: &mut RequiredSize,
) -> (Vec2, Vec2)
where
Model: ?Sized,
@ -151,11 +141,8 @@ where
/// Implements `View::layout` on the given model.
pub fn layout<Model, GetScroller, RequiredSize, Layout>(
size: Vec2,
needs_relayout: bool,
model: &mut Model,
mut get_scroller: GetScroller,
mut required_size: RequiredSize,
size: Vec2, needs_relayout: bool, model: &mut Model,
mut get_scroller: GetScroller, mut required_size: RequiredSize,
mut layout: Layout,
) where
Model: ?Sized,
@ -185,11 +172,8 @@ pub fn layout<Model, GetScroller, RequiredSize, Layout>(
/// Implements `View::required_size` on the given model.
pub fn required_size<Model, GetScroller, RequiredSize>(
constraint: Vec2,
needs_relayout: bool,
model: &mut Model,
mut get_scroller: GetScroller,
mut required_size: RequiredSize,
constraint: Vec2, needs_relayout: bool, model: &mut Model,
mut get_scroller: GetScroller, mut required_size: RequiredSize,
) -> Vec2
where
Model: ?Sized,
@ -210,11 +194,8 @@ where
/// Implements `View::on_event` on the given model.
pub fn on_event<Model, GetScroller, OnEvent, ImportantArea>(
event: Event,
model: &mut Model,
mut get_scroller: GetScroller,
mut on_event: OnEvent,
mut important_area: ImportantArea,
event: Event, model: &mut Model, mut get_scroller: GetScroller,
mut on_event: OnEvent, mut important_area: ImportantArea,
) -> EventResult
where
Model: ?Sized,

View File

@ -303,8 +303,8 @@ impl TextArea {
}
fn backspace(&mut self) {
self.move_left();
self.delete();
self.move_left();
self.delete();
}
fn delete(&mut self) {