Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Handle terminal resize
Erik Kundt committed 2 years ago
commit ee743f7edbde6b13ead7903bf1e032829a3cba10
parent 4b25f64c6d40af510c4ace7d00ca22a259fcaf6d
8 files changed +38 -16
modified Cargo.lock
@@ -1643,6 +1643,7 @@ dependencies = [
 "anyhow",
 "inquire",
 "lexopt",
+
 "libc",
 "log",
 "radicle",
 "radicle-surf",
@@ -1650,6 +1651,7 @@ dependencies = [
 "ratatui 0.26.1",
 "serde",
 "serde_json",
+
 "signal-hook",
 "simple-logging",
 "termion 3.0.0",
 "textwrap",
@@ -1710,8 +1712,7 @@ dependencies = [
[[package]]
name = "ratatui"
version = "0.26.1"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8"
+
source = "git+https://github.com/erak/ratatui?branch=termion-cursor#e0fa5a0b27e2ef04ae84a5ba2b4e2050cc2037bb"
dependencies = [
 "bitflags 2.4.1",
 "cassowary",
modified Cargo.toml
@@ -19,14 +19,16 @@ flux = ["dep:tokio", "dep:tokio-stream"]
anyhow = { version = "1" }
inquire = { version = "0.6.2", default-features = false, features = ["termion", "editor"] }
lexopt = { version = "0.3.0" }
+
libc = { version = "^0.2" }
log = { version = "0.4.19" }
radicle = { git = "https://github.com/radicle-dev/heartwood" }
radicle-term = { git = "https://github.com/radicle-dev/heartwood", package = "radicle-term" }
radicle-surf = { version = "0.18.0" }
-
ratatui = { version = "0.26.0", features = ["all-widgets", "termion"] }
+
ratatui = { git = "https://github.com/erak/ratatui", branch = "termion-cursor", features = ["all-widgets", "termion"] }
simple-logging = { version = "2.0.2" }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
+
signal-hook = { version = "0.3.17" }
timeago = { version = "0.4.1" }
termion = { version = "3" }
textwrap = { version = "0.16.0" }
modified bin/commands/inbox.rs
@@ -11,8 +11,6 @@ use std::ffi::OsString;

use anyhow::anyhow;

-
use radicle::storage::ReadStorage;
-

use radicle_tui as tui;

use tui::common::cob::inbox::{self};
@@ -163,6 +161,8 @@ pub fn run(options: Options, _ctx: impl terminal::Context) -> anyhow::Result<()>
#[cfg(feature = "flux")]
#[tokio::main]
pub async fn run(options: Options, _ctx: impl terminal::Context) -> anyhow::Result<()> {
+
    use radicle::storage::ReadStorage;
+

    let (_, rid) = radicle::rad::cwd()
        .map_err(|_| anyhow!("this command must be run in the context of a project"))?;

modified bin/commands/issue.rs
@@ -11,8 +11,6 @@ use std::ffi::OsString;

use anyhow::anyhow;

-
use radicle::storage::ReadStorage;
-

use radicle_tui as tui;

use tui::common::cob::issue::{self, State};
@@ -126,7 +124,6 @@ impl Args for Options {
#[cfg(feature = "realm")]
pub fn run(options: Options, _ctx: impl terminal::Context) -> anyhow::Result<()> {
    use tui::common::context;
-
    use tui::common::log;
    use tui::realm::Window;

    pub const FPS: u64 = 60;
@@ -157,6 +154,8 @@ pub fn run(options: Options, _ctx: impl terminal::Context) -> anyhow::Result<()>
#[cfg(feature = "flux")]
#[tokio::main]
pub async fn run(options: Options, _ctx: impl terminal::Context) -> anyhow::Result<()> {
+
    use radicle::storage::ReadStorage;
+

    let (_, rid) = radicle::rad::cwd()
        .map_err(|_| anyhow!("this command must be run in the context of a project"))?;

modified bin/commands/issue/common.rs
@@ -39,4 +39,4 @@ impl Display for IssueOperation {
            }
        }
    }
-
}

\ No newline at end of file
+
}
modified src/flux.rs
@@ -1,3 +1,4 @@
+
pub mod event;
pub mod store;
pub mod termination;
pub mod ui;
added src/flux/event.rs
@@ -0,0 +1,5 @@
+
#[derive(Clone, Copy)]
+
pub enum Event {
+
    Key(termion::event::Key),
+
    Resize,
+
}
modified src/flux/ui.rs
@@ -11,7 +11,7 @@ use std::io::{self};
use std::thread;
use std::time::Duration;

-
use termion::event::Event;
+
// use termion::event::Event;
use termion::input::TermRead;
use termion::raw::{IntoRawMode, RawTerminal};

@@ -20,6 +20,7 @@ use ratatui::prelude::*;
use tokio::sync::broadcast;
use tokio::sync::mpsc::{self, UnboundedReceiver};

+
use super::event::Event;
use super::store::State;
use super::termination::Interrupted;
use super::ui::widget::{Render, Widget};
@@ -27,6 +28,7 @@ use super::ui::widget::{Render, Widget};
type Backend = TermionBackend<RawTerminal<io::Stdout>>;

const RENDERING_TICK_RATE: Duration = Duration::from_millis(250);
+
const INLINE_HEIGHT: u16 = 20;

pub struct Frontend<A> {
    action_tx: mpsc::UnboundedSender<A>,
@@ -63,8 +65,9 @@ impl<A> Frontend<A> {
            tokio::select! {
                // Tick to terminate the select every N milliseconds
                _ = ticker.tick() => (),
-
                Some(event) = events_rx.recv() => if let Event::Key(key) = event {
-
                    root.handle_key_event(key)
+
                Some(event) = events_rx.recv() => match event {
+
                    Event::Key(key) => root.handle_key_event(key),
+
                    Event::Resize => (),
                },
                // Handle state updates
                Some(state) = state_rx.recv() => {
@@ -73,7 +76,7 @@ impl<A> Frontend<A> {
                // Catch and handle interrupt signal to gracefully shutdown
                Ok(interrupted) = interrupt_rx.recv() => {
                    let size = terminal.get_frame().size();
-
                    terminal.set_cursor(size.x, size.y)?;
+
                    let _ = terminal.set_cursor(size.x, size.y);

                    break Ok(interrupted);
                }
@@ -90,7 +93,7 @@ impl<A> Frontend<A> {
fn setup_terminal() -> anyhow::Result<Terminal<Backend>> {
    let stdout = io::stdout().into_raw_mode()?;
    let options = TerminalOptions {
-
        viewport: Viewport::Inline(20),
+
        viewport: Viewport::Inline(INLINE_HEIGHT),
    };

    Ok(Terminal::with_options(
@@ -106,14 +109,25 @@ fn restore_terminal(terminal: &mut Terminal<Backend>) -> anyhow::Result<()> {

fn events() -> mpsc::UnboundedReceiver<Event> {
    let (tx, rx) = mpsc::unbounded_channel();
-
    let keys_tx = tx.clone();
+
    let events_tx = tx.clone();
    thread::spawn(move || {
        let stdin = io::stdin();
        for key in stdin.keys().flatten() {
-
            if keys_tx.send(Event::Key(key)).is_err() {
+
            if events_tx.send(Event::Key(key)).is_err() {
                return;
            }
        }
    });
+

+
    let events_tx = tx.clone();
+
    if let Ok(mut signals) = signal_hook::iterator::Signals::new([libc::SIGWINCH]) {
+
        thread::spawn(move || {
+
            for _ in signals.forever() {
+
                if events_tx.send(Event::Resize).is_err() {
+
                    return;
+
                }
+
            }
+
        });
+
    }
    rx
}