`RefCell` in the `TuiLogicalState`. Also some UI thought about what
history should be shared with what.
-## General UI improvements
-
-When a keystroke requires a network transaction, the transaction often
-takes place in a `handle_keypress()` method, which means the display
-isn't updated until that method returns, because only `Tui` can do
-display updates. But some network transactions take noticeable time,
-so it would be nice to do an immediate display update to indicate that
-the keystroke was received and something is being done. This may
-involve actual code restructuring, since in Rust it's not trivial to
-just casually call back to the top-level thing.
-
## Error handling improvements
Error Log entries could usefully include various extra detail:
use super::auth::AuthConfig;
use super::posting::Post;
+use super::tui::TuiBusyIndicator;
use super::types::*;
#[derive(Hash, Debug, PartialEq, Eq, Clone, Copy)]
permit_write: bool,
logfile: Option<File>,
error_log: ErrorLog,
+ busy: Option<TuiBusyIndicator>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
permit_write: false,
logfile: None,
error_log: ErrorLog::new(),
+ busy: None,
};
client.feeds.insert(
self.logfile = file;
}
+ pub fn set_busy_indicator(&mut self, busy: TuiBusyIndicator) {
+ self.busy = Some(busy);
+ }
+
+ fn set_busy(&self) {
+ if let Some(busy) = &self.busy {
+ busy.set_busy();
+ }
+ }
+
pub fn clear_caches(&mut self) {
self.accounts.clear();
self.statuses.clear();
req_headers.push(TransactionLogEntry::translate_header(h));
}
+ self.set_busy();
let rsp = self.client.execute(req)?;
let status = rsp.status();
let mut rsp_headers = Vec::new();
use super::menu::*;
use super::options::*;
use super::posting::*;
+use super::text::{CentredInfoLine, DefaultDisplayStyle, TextFragmentOneLine};
fn ratatui_style_from_colour(colour: char) -> Style {
match colour {
let output = Rc::new(RefCell::new(TuiOutput::new(terminal)));
+ client.set_busy_indicator(TuiBusyIndicator {
+ output: Rc::clone(&output),
+ });
+
let mut tui = Tui {
output,
subthread_sender: sender,
struct TuiOutput {
terminal: Terminal<CrosstermBackend<Stdout>>,
+ prev_buffer: Option<Buffer>,
+ pending_io_error: Result<(), std::io::Error>,
+ busy_msg: CentredInfoLine,
}
impl TuiOutput {
fn new(terminal: Terminal<CrosstermBackend<Stdout>>) -> Self {
TuiOutput {
terminal,
+ prev_buffer: None,
+ pending_io_error: Ok(()),
+ busy_msg: CentredInfoLine::new(ColouredString::general(
+ "<+- please wait, accessing network -+>",
+ "HHHHKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKHHHH",
+ )),
}
}
&mut self,
state: &mut TuiLogicalState,
) -> Result<(), std::io::Error> {
+ // In case a previous set_busy reported an I/O error, return it now
+ std::mem::replace(&mut self.pending_io_error, Ok(()))?;
+
self.terminal.draw(|frame| {
let area = frame.size();
let buf = frame.buffer_mut();
- if let Some((x, y)) = state.draw_frame(area, buf) {
+ let cursor = state.draw_frame(area, buf);
+ self.prev_buffer = Some(buf.clone());
+ if let Some((x, y)) = cursor {
if let (Ok(x), Ok(y)) = (x.try_into(), y.try_into()) {
frame.set_cursor(x, y);
}
fn clear(&mut self) -> Result<(), std::io::Error> {
self.terminal.clear()
}
+
+ fn set_busy(&mut self) {
+ let result = self.terminal.draw(|frame| {
+ let area = frame.size();
+ let buf = frame.buffer_mut();
+ buf.reset();
+ if let Some(prev) = &self.prev_buffer {
+ buf.merge(prev);
+ }
+ let width = area.width as usize;
+ let height = area.height as usize;
+ let bottom_line = self.busy_msg.render_oneline(
+ width,
+ None,
+ &DefaultDisplayStyle,
+ );
+ let rpad = width.saturating_sub(bottom_line.width());
+ let bottom_line =
+ bottom_line + ColouredString::plain(" ").repeat(rpad);
+ ratatui_set_string(buf, 0, height.saturating_sub(1), bottom_line);
+ });
+
+ // This function can't conveniently return its I/O error, so
+ // instead we keep it until the network operation has
+ // finished, and report it in the next draw()
+ match result {
+ Ok(_) => (),
+ Err(e) => {
+ if self.pending_io_error.is_ok() {
+ self.pending_io_error = Err(e);
+ }
+ }
+ }
+ }
+}
+
+pub struct TuiBusyIndicator {
+ output: Rc<RefCell<TuiOutput>>,
+}
+
+impl TuiBusyIndicator {
+ pub fn set_busy(&self) {
+ self.output.borrow_mut().set_busy();
+ }
}
#[derive(Debug)]