From 885b56038f5dbb8b1ef71bf3e77db5d5a5f028c1 Mon Sep 17 00:00:00 2001 From: Ulyssa Date: Wed, 14 Jun 2023 22:42:23 -0700 Subject: [PATCH] Use terminal window focus to determine when a message has actually been seen (#94) --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 41 +++++++++++++++++++++++++++------- src/windows/room/chat.rs | 4 +++- src/windows/room/scrollback.rs | 15 +++++++++++-- 5 files changed, 51 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e520d26..513f4a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2137,7 +2137,7 @@ dependencies = [ [[package]] name = "modalkit" version = "0.0.15" -source = "git+https://github.com/ulyssa/modalkit?rev=7979680#797968002c53287d907ca3ae823e3482265c94bb" +source = "git+https://github.com/ulyssa/modalkit?rev=f641162#f6411625caa1ddb764c91a3fc47e052cfdc405b5" dependencies = [ "anymap2", "arboard", diff --git a/Cargo.toml b/Cargo.toml index 0840449..b8dd957 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ url = {version = "^2.2.2", features = ["serde"]} [dependencies.modalkit] git = "https://github.com/ulyssa/modalkit" -rev = "7979680" +rev = "f641162" [dependencies.matrix-sdk] version = "0.6" diff --git a/src/main.rs b/src/main.rs index cf3afec..5edc49a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,15 @@ use matrix_sdk::ruma::OwnedUserId; use modalkit::crossterm::{ self, cursor::Show as CursorShow, - event::{poll, read, DisableBracketedPaste, EnableBracketedPaste, Event}, + event::{ + poll, + read, + DisableBracketedPaste, + DisableFocusChange, + EnableBracketedPaste, + EnableFocusChange, + Event, + }, execute, terminal::{EnterAlternateScreen, LeaveAlternateScreen, SetTitle}, }; @@ -109,6 +117,7 @@ struct Application { bindings: KeyManager, actstack: VecDeque<(ProgramAction, ProgramContext)>, screen: ScreenState, + focused: bool, } impl Application { @@ -120,6 +129,7 @@ impl Application { crossterm::terminal::enable_raw_mode()?; crossterm::execute!(stdout, EnterAlternateScreen)?; crossterm::execute!(stdout, EnableBracketedPaste)?; + crossterm::execute!(stdout, EnableFocusChange)?; let title = format!("iamb ({})", settings.profile.user_id); crossterm::execute!(stdout, SetTitle(title))?; @@ -154,11 +164,13 @@ impl Application { bindings, actstack, screen, + focused: true, }) } fn redraw(&mut self, full: bool, store: &mut ProgramStore) -> Result<(), std::io::Error> { let bindings = &mut self.bindings; + let focused = self.focused; let sstate = &mut self.screen; let term = &mut self.terminal; @@ -176,7 +188,11 @@ impl Application { // Don't show terminal cursor when we show a dialog. let hide_cursor = !dialogstr.is_empty(); - let screen = Screen::new(store).show_dialog(dialogstr).show_mode(modestr).borders(true); + let screen = Screen::new(store) + .show_dialog(dialogstr) + .show_mode(modestr) + .borders(true) + .focus(focused); f.render_stateful_widget(screen, area, sstate); if hide_cursor { @@ -212,8 +228,11 @@ impl Application { Event::Mouse(_) => { // Do nothing for now. }, - Event::FocusGained | Event::FocusLost => { - // Do nothing for now. + Event::FocusGained => { + self.focused = true; + }, + Event::FocusLost => { + self.focused = false; }, Event::Resize(_, _) => { // We'll redraw for the new size next time step() is called. @@ -532,13 +551,18 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> { login(worker, &settings).await.unwrap_or_else(print_exit); + fn restore_tty() { + let _ = crossterm::terminal::disable_raw_mode(); + let _ = crossterm::execute!(stdout(), DisableBracketedPaste); + let _ = crossterm::execute!(stdout(), DisableFocusChange); + let _ = crossterm::execute!(stdout(), LeaveAlternateScreen); + let _ = crossterm::execute!(stdout(), CursorShow); + } + // Make sure panics clean up the terminal properly. let orig_hook = std::panic::take_hook(); std::panic::set_hook(Box::new(move |panic_info| { - let _ = crossterm::terminal::disable_raw_mode(); - let _ = crossterm::execute!(stdout(), DisableBracketedPaste); - let _ = crossterm::execute!(stdout(), LeaveAlternateScreen); - let _ = crossterm::execute!(stdout(), CursorShow); + restore_tty(); orig_hook(panic_info); process::exit(1); })); @@ -547,6 +571,7 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> { // We can now run the application. application.run().await?; + restore_tty(); Ok(()) } diff --git a/src/windows/room/chat.rs b/src/windows/room/chat.rs index 4cba31b..581df65 100644 --- a/src/windows/room/chat.rs +++ b/src/windows/room/chat.rs @@ -848,7 +848,9 @@ impl<'a> StatefulWidget for Chat<'a> { // Render the message scrollback. let scrollback_focused = state.focus.is_scrollback() && self.focused; - let scrollback = Scrollback::new(self.store).focus(scrollback_focused); + let scrollback = Scrollback::new(self.store) + .focus(scrollback_focused) + .room_focus(self.focused); scrollback.render(scrollarea, buf, &mut state.scrollback); } } diff --git a/src/windows/room/scrollback.rs b/src/windows/room/scrollback.rs index f3d614b..d1959ae 100644 --- a/src/windows/room/scrollback.rs +++ b/src/windows/room/scrollback.rs @@ -1206,13 +1206,21 @@ impl TerminalCursor for ScrollbackState { } pub struct Scrollback<'a> { + room_focused: bool, focused: bool, store: &'a mut ProgramStore, } impl<'a> Scrollback<'a> { pub fn new(store: &'a mut ProgramStore) -> Self { - Scrollback { focused: false, store } + Scrollback { room_focused: false, focused: false, store } + } + + /// Indicate whether the room window is currently focused, regardless of whether the scrollback + /// also is. + pub fn room_focus(mut self, focused: bool) -> Self { + self.room_focused = focused; + self } /// Indicate whether the scrollback is currently focused. @@ -1307,7 +1315,10 @@ impl<'a> StatefulWidget for Scrollback<'a> { y += 1; } - if settings.tunables.read_receipt_send && state.cursor.timestamp.is_none() { + if self.room_focused && + settings.tunables.read_receipt_send && + state.cursor.timestamp.is_none() + { // If the cursor is at the last message, then update the read marker. info.read_till = info.messages.last_key_value().map(|(k, _)| k.1.clone()); }