summaryrefslogtreecommitdiffstats
path: root/src/widget.rs
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2019-03-02 19:39:24 +0100
committerrabite <rabite@posteo.de>2019-03-02 23:28:03 +0100
commiteb5a86b7cd37dc39d20f6ce122f671f94f51b75a (patch)
treea60e83dca33a46ebcc367dbfe98c2e8250210289 /src/widget.rs
parente2acef5ddfa5c7bf470aee5e24f429eabfb17951 (diff)
moved window stuff to widget itself
Diffstat (limited to 'src/widget.rs')
-rw-r--r--src/widget.rs327
1 files changed, 253 insertions, 74 deletions
diff --git a/src/widget.rs b/src/widget.rs
index 2e47b28..f2be282 100644
--- a/src/widget.rs
+++ b/src/widget.rs
@@ -1,28 +1,115 @@
-use std::sync::mpsc::channel;
+use std::sync::{Arc, Mutex};
+use std::sync::mpsc::{Sender, Receiver, channel};
use termion::event::{Event, Key, MouseEvent};
use termion::input::TermRead;
+use termion::screen::AlternateScreen;
+use failure::Backtrace;
+
use crate::coordinates::{Coordinates, Position, Size};
-use crate::fail::{HResult, HError};
-use crate::window::{send_event, Events};
+use crate::fail::{HResult, HError, ErrorLog};
+use crate::minibuffer::MiniBuffer;
+use crate::term;
+use crate::term::ScreenExt;
+
+use std::io::{BufWriter, stdin, stdout, Stdout};
+
+#[derive(Debug)]
+pub enum Events {
+ InputEvent(Event),
+ WidgetReady,
+ ExclusiveEvent(Option<Sender<Events>>),
+}
+
+impl PartialEq for WidgetCore {
+ fn eq(&self, other: &WidgetCore) -> bool {
+ if self.coordinates == other.coordinates {
+ true
+ } else {
+ false
+ }
+ }
+}
+
+impl std::fmt::Debug for WidgetCore {
+ fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ let output = format!("{:?}{:?}{:?}",
+ self.coordinates,
+ self.minibuffer,
+ self.status_bar_content);
+ formatter.write_str(&output)
+ }
+}
+
+#[derive(Clone)]
+pub struct WidgetCore {
+ pub screen: Arc<Mutex<AlternateScreen<BufWriter<Stdout>>>>,
+ pub coordinates: Coordinates,
+ pub minibuffer: Arc<Mutex<Option<MiniBuffer>>>,
+ pub event_sender: Sender<Events>,
+ event_receiver: Arc<Mutex<Option<Receiver<Events>>>>,
+ pub status_bar_content: Arc<Mutex<Option<String>>>
+}
+
+impl WidgetCore {
+ pub fn new() -> HResult<WidgetCore> {
+ let screen = AlternateScreen::from(BufWriter::new(stdout()));
+ let coords = Coordinates::new_at(term::xsize(),
+ term::ysize() - 2,
+ 1,
+ 2);
+ let (sender, receiver) = channel();
+ let status_bar_content = Arc::new(Mutex::new(None));
+
+ let core = WidgetCore {
+ screen: Arc::new(Mutex::new(screen)),
+ coordinates: coords,
+ minibuffer: Arc::new(Mutex::new(None)),
+ event_sender: sender,
+ event_receiver: Arc::new(Mutex::new(Some(receiver))),
+ status_bar_content: status_bar_content };
-use std::io::{BufWriter, Write, stdin};
+ let minibuffer = MiniBuffer::new(&core);
+ *core.minibuffer.lock().unwrap() = Some(minibuffer);
+ Ok(core)
+ }
+ pub fn get_sender(&self) -> Sender<Events> {
+ self.event_sender.clone()
+ }
+}
pub trait Widget {
fn get_widget(&self) -> Box<dyn Widget> {
- Box::new(crate::textview::TextView::new_blank())
- }
- fn get_coordinates(&self) -> &Coordinates;
- fn set_coordinates(&mut self, coordinates: &Coordinates);
- fn render_header(&self) -> String;
- fn render_footer(&self) -> String { "".into() }
- fn refresh(&mut self);
- fn get_drawlist(&self) -> String;
+ Box::new(crate::textview::TextView::new_blank(self.get_core().unwrap()))
+ }
+ fn get_core(&self) -> HResult<&WidgetCore> {
+ Err(HError::NoWidgetCoreError(Backtrace::new()))
+ }
+ fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
+ Err(HError::NoWidgetCoreError(Backtrace::new()))
+ }
+ fn get_coordinates(&self) -> HResult<&Coordinates> {
+ Ok(&self.get_core()?.coordinates)
+ }
+ fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
+ self.get_core_mut()?.coordinates = coordinates.clone();
+ self.refresh()?;
+ Ok(())
+ }
+ fn render_header(&self) -> HResult<String> {
+ Err(HError::NoHeaderError)
+ }
+ fn render_footer(&self) -> HResult<String> {
+ Err(HError::NoHeaderError)
+ }
+ fn refresh(&mut self) -> HResult<()>;
+ fn get_drawlist(&self) -> HResult<String>;
fn after_draw(&self) -> HResult<()> { Ok(()) }
+
fn on_event(&mut self, event: Event) -> HResult<()> {
match event {
Event::Key(Key::Char('q')) => panic!("It's your fault!"),
@@ -34,67 +121,59 @@ pub trait Widget {
fn on_key(&mut self, key: Key) -> HResult<()> {
match key {
- _ => self.bad(Event::Key(key)),
+ _ => { self.bad(Event::Key(key)).unwrap() },
}
Ok(())
}
fn on_mouse(&mut self, event: MouseEvent) -> HResult<()> {
match event {
- _ => self.bad(Event::Mouse(event)),
+ _ => { self.bad(Event::Mouse(event)).unwrap() },
}
Ok(())
}
fn on_wtf(&mut self, event: Vec<u8>) -> HResult<()> {
match event {
- _ => self.bad(Event::Unsupported(event)),
+ _ => { self.bad(Event::Unsupported(event)).unwrap() },
}
Ok(())
}
- fn show_status(&self, status: &str) {
- crate::window::show_status(status);
- }
-
- fn minibuffer(&self, query: &str) -> HResult<String> {
- crate::window::minibuffer(query)
- }
-
- fn bad(&mut self, event: Event) {
- self.show_status(&format!("Stop the nasty stuff!! {:?} does nothing!", event));
+ fn bad(&mut self, event: Event) -> HResult<()> {
+ self.show_status(&format!("Stop the nasty stuff!! {:?} does nothing!", event))
}
- fn get_header_drawlist(&mut self) -> String {
- format!(
+ fn get_header_drawlist(&mut self) -> HResult<String> {
+ Ok(format!(
"{}{}{:xsize$}{}{}",
crate::term::goto_xy(1, 1),
crate::term::header_color(),
" ",
crate::term::goto_xy(1, 1),
- self.render_header(),
- xsize = self.get_coordinates().xsize() as usize
- )
+ self.render_header()?,
+ xsize = self.get_coordinates()?.xsize() as usize
+ ))
}
- fn get_footer_drawlist(&mut self) -> String {
- let xsize = self.get_coordinates().xsize();
+ fn get_footer_drawlist(&mut self) -> HResult<String> {
+ let xsize = self.get_coordinates()?.xsize();
let ypos = crate::term::ysize();
- format!(
+ Ok(format!(
"{}{}{:xsize$}{}{}",
crate::term::goto_xy(1, ypos),
crate::term::header_color(),
" ",
crate::term::goto_xy(1, ypos),
- self.render_footer(),
- xsize = xsize as usize)
+ self.render_footer()?,
+ xsize = xsize as usize))
}
- fn get_clearlist(&self) -> String {
- let (xpos, ypos) = self.get_coordinates().u16position();
- let (xsize, ysize) = self.get_coordinates().u16size();
+ fn get_clearlist(&self) -> HResult<String> {
+ let (xpos, ypos) = self.get_coordinates()?.u16position();
+ let (xsize, ysize) = self.get_coordinates()?.u16size();
- (ypos..ysize + 2)
+ Ok((ypos..ysize + 2)
.map(|line| {
format!(
"{}{}{:xsize$}",
@@ -104,15 +183,15 @@ pub trait Widget {
xsize = xsize as usize
)
})
- .collect()
+ .collect())
}
- fn get_redraw_empty_list(&self, lines: usize) -> String {
- let (xpos, ypos) = self.get_coordinates().u16position();
- let (xsize, ysize) = self.get_coordinates().u16size();
+ fn get_redraw_empty_list(&self, lines: usize) -> HResult<String> {
+ let (xpos, ypos) = self.get_coordinates()?.u16position();
+ let (xsize, ysize) = self.get_coordinates()?.u16size();
let start_y = lines + ypos as usize;
- (start_y..(ysize + 2) as usize)
+ Ok((start_y..(ysize + 2) as usize)
.map(|i| {
format!(
"{}{:xsize$}",
@@ -121,30 +200,21 @@ pub trait Widget {
xsize = xsize as usize
)
})
- .collect()
- }
-
- fn draw(&self) -> HResult<()> {
- let drawlist = self.get_drawlist();
- let mut bufout = BufWriter::new(std::io::stdout());
-
- write!(bufout, "{}", drawlist)?;
- bufout.flush()?;
- Ok(())
+ .collect())
}
fn popup(&mut self) -> HResult<()> {
- self.run_widget();
- send_event(Events::ExclusiveEvent(None))?;
+ self.run_widget().log();
+ self.get_core()?.get_sender().send(Events::ExclusiveEvent(None))?;
Ok(())
}
fn run_widget(&mut self) -> HResult<()> {
let (tx_event, rx_event) = channel();
- send_event(Events::ExclusiveEvent(Some(tx_event)))?;
+ self.get_core()?.get_sender().send(Events::ExclusiveEvent(Some(tx_event)))?;
self.clear()?;
- self.refresh();
+ self.refresh().log();
self.draw()?;
for event in rx_event.iter() {
@@ -155,32 +225,31 @@ pub trait Widget {
}
}
Events::WidgetReady => {
- self.refresh();
+ self.refresh().log();
}
_ => {}
}
- self.draw();
- self.after_draw();
+ self.draw().log();
+ self.after_draw().log();
}
Ok(())
}
fn clear(&self) -> HResult<()> {
- let clearlist = self.get_clearlist();
- write!(std::io::stdout(), "{}", clearlist)?;
- std::io::stdout().flush()?;
- Ok(())
+ let clearlist = self.get_clearlist()?;
+ self.write_to_screen(&clearlist)
}
- fn animate_slide_up(&mut self) {
- let coords = self.get_coordinates().clone();
+ fn animate_slide_up(&mut self) -> HResult<()> {
+ let coords = self.get_coordinates()?.clone();
let xpos = coords.position().x();
let ypos = coords.position().y();
let xsize = coords.xsize();
let ysize = coords.ysize();
- let clear = self.get_clearlist();
+ let clear = self.get_clearlist()?;
let pause = std::time::Duration::from_millis(5);
- let mut bufout = BufWriter::new(std::io::stdout());
+
+ self.write_to_screen(&clear).log();
for i in (0..10).rev() {
let coords = Coordinates { size: Size((xsize,ysize-i)),
@@ -188,13 +257,123 @@ pub trait Widget {
((xpos,
ypos+i))
};
- self.set_coordinates(&coords);
- let buffer = self.get_drawlist();
- write!(bufout, "{}{}",
- clear, buffer).unwrap();
- bufout.flush().ok();
+ self.set_coordinates(&coords).log();
+ let buffer = self.get_drawlist()?;
+ self.write_to_screen(&buffer).log();
std::thread::sleep(pause);
}
+ Ok(())
}
+
+ fn draw(&mut self) -> HResult<()> {
+ let output =
+ self.get_drawlist().unwrap_or("".to_string()) +
+ &self.get_header_drawlist().unwrap_or("".to_string()) +
+ &self.get_footer_drawlist().unwrap_or("".to_string());
+ self.write_to_screen(&output).log();
+ Ok(())
+ }
+
+ fn handle_input(&mut self) -> HResult<()> {
+ let (tx_event, rx_event) = channel();
+ let (tx_internal_event, rx_internal_event) = channel();
+ let rx_global_event = self.get_core()?.event_receiver.lock()?.take()?;
+
+ input_thread(tx_event.clone());
+ global_event_thread(rx_global_event, tx_event.clone());
+ dispatch_events(rx_event, tx_internal_event);
+
+ for event in rx_internal_event.iter() {
+ match event {
+ Events::InputEvent(event) => {
+ self.on_event(event).ok();
+ self.draw().ok();
+ },
+ _ => {
+ self.refresh().ok();
+ self.draw().ok();
+ },
+ }
+ }
+ Ok(())
+ }
+
+ fn draw_status(&self) -> HResult<()> {
+ let xsize = term::xsize() as u16;
+ let status = &self.get_core()?.status_bar_content;
+
+ let status = status.lock()?;
+
+ self.write_to_screen(
+ &format!(
+ "{}{}{:xsize$}{}{}",
+ term::move_bottom(),
+ term::status_bg(),
+ " ",
+ term::move_bottom(),
+ status.as_ref()?,
+ xsize = xsize as usize
+ )).log();
+
+ Ok(())
+ }
+
+ fn show_status(&self, status: &str) -> HResult<()> {
+ {
+ let mut status_content = self.get_core()?.status_bar_content.lock()?;
+ *status_content = Some(status.to_string());
+ }
+ self.draw_status()?;
+ Ok(())
+ }
+
+ fn minibuffer(&self, query: &str) -> HResult<String> {
+ let answer = self.get_core()?.minibuffer.lock()?.as_mut()?.query(query);
+ let mut screen = self.get_core()?.screen.lock()?;
+ screen.cursor_hide().log();
+ answer
+ }
+
+ fn write_to_screen(&self, s: &str) -> HResult<()> {
+ let mut screen = self.get_core()?.screen.lock()?;
+ screen.write_str(s)
+ }
+}
+
+fn dispatch_events(rx: Receiver<Events>, tx: Sender<Events>) {
+ std::thread::spawn(move || {
+ let mut tx_exclusive_event: Option<Sender<Events>> = None;
+ for event in rx.iter() {
+ match &event {
+ Events::ExclusiveEvent(tx_event) => {
+ tx_exclusive_event = tx_event.clone();
+ }
+ _ => {}
+ }
+ if let Some(tx_event) = &tx_exclusive_event {
+ tx_event.send(event).unwrap();
+ } else {
+ tx.send(event).unwrap();
+ }
+ }
+ });
+}
+
+fn global_event_thread(rx_global: Receiver<Events>,
+ tx: Sender<Events>) {
+ std::thread::spawn(move || {
+ for event in rx_global.iter() {
+ tx.send(event).unwrap();
+ }
+ });
+}
+
+fn input_thread(tx: Sender<Events>) {
+ std::thread::spawn(move || {
+ for input in stdin().events() {
+ let input = input.unwrap();
+ tx.send(Events::InputEvent(input)).unwrap();
+ }
+ });
}