Add stub termion backend

Not supported: input, colors, effects, async
This commit is contained in:
Alexandre Bury 2016-10-08 16:47:15 -07:00
parent 90c9d51a6c
commit df4397a174
7 changed files with 106 additions and 11 deletions

View File

@ -1,5 +1,6 @@
[package] [package]
authors = ["Alexandre Bury <alexandre.bury@gmail.com>"] authors = ["Alexandre Bury <alexandre.bury@gmail.com>"]
build = "build.rs"
description = "A TUI (Text User Interface) library focused on ease-of-use." description = "A TUI (Text User Interface) library focused on ease-of-use."
documentation = "https://gyscos.github.io/Cursive/cursive/index.html" documentation = "https://gyscos.github.io/Cursive/cursive/index.html"
exclude = ["doc", "assets"] exclude = ["doc", "assets"]
@ -9,10 +10,13 @@ name = "cursive"
readme = "Readme.md" readme = "Readme.md"
repository = "https://github.com/gyscos/Cursive" repository = "https://github.com/gyscos/Cursive"
version = "0.3.1" version = "0.3.1"
build="build.rs"
[build-dependencies]
skeptic = "0.6"
[dependencies] [dependencies]
odds = "0.2" odds = "0.2"
termion = "1.1.1"
toml = "0.2" toml = "0.2"
unicode-segmentation = "0.1" unicode-segmentation = "0.1"
unicode-width = "0.1" unicode-width = "0.1"
@ -25,8 +29,5 @@ version = "5.82.0"
rand = "0.3" rand = "0.3"
skeptic = "0.6" skeptic = "0.6"
[build-dependencies]
skeptic = "0.6"
[lib] [lib]
name = "cursive" name = "cursive"

View File

@ -74,7 +74,7 @@ impl backend::Backend for NcursesBackend {
ncurses::clear(); ncurses::clear();
} }
fn refresh(&self) { fn refresh(&mut self) {
ncurses::refresh(); ncurses::refresh();
} }

View File

@ -3,15 +3,18 @@ use theme;
// Module is not named `ncurses` to avoir naming conflict // Module is not named `ncurses` to avoir naming conflict
mod curses; mod curses;
mod termion;
pub use self::curses::NcursesBackend; pub use self::curses::NcursesBackend;
pub use self::termion::TermionBackend;
pub trait Backend { pub trait Backend {
fn init() -> Self; fn init() -> Self;
// TODO: take `self` by value?
fn finish(&mut self); fn finish(&mut self);
fn clear(&self); fn clear(&self);
fn refresh(&self); fn refresh(&mut self);
fn has_colors(&self) -> bool; fn has_colors(&self) -> bool;
@ -24,6 +27,7 @@ pub trait Backend {
fn set_refresh_rate(&mut self, fps: u32); fn set_refresh_rate(&mut self, fps: u32);
fn screen_size(&self) -> (usize, usize); fn screen_size(&self) -> (usize, usize);
// TODO: unify those into a single method?
fn with_color<F: FnOnce()>(&self, color: theme::ColorStyle, f: F); fn with_color<F: FnOnce()>(&self, color: theme::ColorStyle, f: F);
fn with_effect<F: FnOnce()>(&self, effect: theme::Effect, f: F); fn with_effect<F: FnOnce()>(&self, effect: theme::Effect, f: F);
} }

85
src/backend/termion.rs Normal file
View File

@ -0,0 +1,85 @@
use ::backend;
use ::event::{Event, Key};
use std::io::Write;
use termion;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use ::theme::{BaseColor, Color, ColorStyle, Effect};
pub struct TermionBackend {
terminal: termion::raw::RawTerminal<::std::io::Stdout>,
}
impl backend::Backend for TermionBackend {
fn init() -> Self {
print!("{}", termion::cursor::Hide);
Self::clear();
TermionBackend {
terminal: ::std::io::stdout().into_raw_mode().unwrap(),
}
}
fn finish(&mut self) {
// Maybe we should clear everything?
print!("{}{}", termion::cursor::Show, termion::cursor::Goto(1, 1));
Self::clear();
}
fn init_color_style(&mut self, style: ColorStyle, foreground: &Color,
background: &Color) {
// Do we _need_ to save the color?
}
fn with_color<F: FnOnce()>(&self, color: ColorStyle, f: F) {
// TODO: actually use colors
// TODO: careful! need to remember the previous state
// and apply it back
f();
}
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
// TODO: actually use effects
// TODO: careful! need to remember the previous state
// and apply it back
f();
}
fn has_colors(&self) -> bool {
// TODO: color support detection?
true
}
fn screen_size(&self) -> (usize, usize) {
let (x, y) = termion::terminal_size().unwrap_or((1, 1));
(x as usize, y as usize)
}
fn clear() {
print!("{}", termion::clear::All);
}
fn refresh(&mut self) {
// Not sure termion needs a refresh phase
self.terminal.flush().unwrap();
}
fn print_at(&self, (x, y): (usize, usize), text: &str) {
// TODO: terminals are 1-based. Should we add 1 here?
print!("{}{}", termion::cursor::Goto(x as u16, y as u16), text);
}
fn set_refresh_rate(&mut self, fps: u32) {
// TODO: handle async refresh, when no input is entered.
// Could be done with a timeout on the event polling,
// if it was supportedd.
}
fn poll_event(&self) -> Event {
::std::io::stdin().keys().next();
Event::Key(Key::Enter)
}
}

View File

@ -61,6 +61,7 @@ extern crate toml;
extern crate unicode_segmentation; extern crate unicode_segmentation;
extern crate unicode_width; extern crate unicode_width;
extern crate odds; extern crate odds;
extern crate termion;
macro_rules! println_stderr( macro_rules! println_stderr(
($($arg:tt)*) => { { ($($arg:tt)*) => { {
@ -105,7 +106,7 @@ mod utf8;
mod backend; mod backend;
use backend::{Backend, NcursesBackend}; use backend::{Backend};
use event::{Callback, Event, EventResult}; use event::{Callback, Event, EventResult};
@ -155,7 +156,7 @@ new_default!(Cursive);
// Use the Ncurses backend. // Use the Ncurses backend.
// TODO: make this feature-driven // TODO: make this feature-driven
#[doc(hidden)] #[doc(hidden)]
pub type B = NcursesBackend; pub type B = backend::TermionBackend;
impl Cursive { impl Cursive {
/// Creates a new Cursive root, and initialize ncurses. /// Creates a new Cursive root, and initialize ncurses.
@ -493,7 +494,6 @@ impl Cursive {
let id = self.active_screen; let id = self.active_screen;
self.screens.get_mut(id).unwrap().draw(&printer); self.screens.get_mut(id).unwrap().draw(&printer);
self.backend.refresh();
} }
/// Runs the event loop. /// Runs the event loop.
@ -518,6 +518,7 @@ impl Cursive {
// TODO: Do we need to redraw every view every time? // TODO: Do we need to redraw every view every time?
// (Is this getting repetitive? :p) // (Is this getting repetitive? :p)
self.draw(); self.draw();
self.backend.refresh();
// 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)

View File

@ -104,8 +104,11 @@ impl<'a> Printer<'a> {
return; return;
} }
let len = min(len, self.size.x - p.x); let len = min(len, self.size.x - p.x);
let text: String = ::std::iter::repeat(c).take(len).collect();
let p = p + self.offset; let p = p + self.offset;
// self.backend.print_at((p.x, p.y), &text);
self.print(p, &text);
for x in 0..len { for x in 0..len {
self.backend.print_at((p.x + x, p.y), c); self.backend.print_at((p.x + x, p.y), c);
} }

View File

@ -60,7 +60,8 @@ impl<T: View> ViewWrapper for ShadowView<T> {
fn wrap_draw(&self, printer: &Printer) { fn wrap_draw(&self, printer: &Printer) {
if printer.size.y == 0 || printer.size.x == 0 { if printer.size.y <= self.top_padding as usize ||
printer.size.x <= self.left_padding as usize {
// Nothing to do if there's no place to draw. // Nothing to do if there's no place to draw.
return; return;
} }
@ -79,7 +80,7 @@ impl<T: View> ViewWrapper for ShadowView<T> {
printer.size - (1, 1), printer.size - (1, 1),
true)); true));
if printer.theme.shadow { if printer.theme.shadow && false {
let h = printer.size.y; let h = printer.size.y;
let w = printer.size.x; let w = printer.size.x;