diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10f5b96..470132a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,9 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: cargo test --features=search - run: cargo test --no-default-features --features=ratatui-crossterm,search -- --skip .rs + - run: cargo test --no-default-features --features=ratatui-termion,search -- --skip .rs + if: ${{ matrix.os != 'windows-latest' }} + - run: cargo test --no-default-features --features=ratatui-termwiz,search -- --skip .rs - run: cargo test --no-default-features --features=your-backend,search -- --skip .rs - run: cargo test --no-default-features --features=ratatui-your-backend,search -- --skip .rs lint: @@ -33,6 +36,8 @@ jobs: - run: cargo clippy --examples --no-default-features --features ratatui-crossterm,search -- -D warnings - run: cargo clippy --examples --no-default-features --features ratatui-termion -- -D warnings - run: cargo clippy --examples --no-default-features --features ratatui-termion,search -- -D warnings + - run: cargo clippy --examples --no-default-features --features ratatui-termwiz -- -D warnings + - run: cargo clippy --examples --no-default-features --features ratatui-termwiz,search -- -D warnings - run: cargo clippy --examples --no-default-features --features termion -- -D warnings - run: cargo clippy --examples --no-default-features --features termion,search -- -D warnings - run: cargo clippy --examples --no-default-features --features your-backend -- -D warnings diff --git a/Cargo.toml b/Cargo.toml index b7607e3..aacb9af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ your-backend = ["tui"] # Features to use ratatui ratatui-crossterm = ["dep:crossterm-027", "ratatui/crossterm"] ratatui-termion = ["dep:termion", "ratatui/termion"] +ratatui-termwiz = ["dep:termwiz", "ratatui/termwiz"] ratatui-your-backend = ["ratatui"] # Other optional features search = ["dep:regex"] @@ -44,6 +45,7 @@ arbitrary = { version = "1", features = ["derive"], optional = true } crossterm-027 = { package = "crossterm", version = "0.27", optional = true } ratatui = { version = "0.23.0", default-features = false, optional = true } unicode-width = "0.1.11" +termwiz = { version = "0.20.0", optional = true } [[example]] name = "minimal" diff --git a/src/highlight.rs b/src/highlight.rs index 98ac75b..cec8f2d 100644 --- a/src/highlight.rs +++ b/src/highlight.rs @@ -2,6 +2,7 @@ use crate::tui::style::Style; #[cfg(any( feature = "ratatui-crossterm", feature = "ratatui-termion", + feature = "ratatui-termwiz", feature = "ratatui-your-backend", ))] use crate::tui::text::Line as Spans; @@ -9,6 +10,7 @@ use crate::tui::text::Span; #[cfg(not(any( feature = "ratatui-crossterm", feature = "ratatui-termion", + feature = "ratatui-termwiz", feature = "ratatui-your-backend", )))] use crate::tui::text::Spans; diff --git a/src/input.rs b/src/input.rs index 8d6a184..439e5a3 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,12 +1,17 @@ #[cfg(any(feature = "crossterm", feature = "ratatui-crossterm"))] use crate::crossterm::event::{ - Event as CrosstermEvent, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, - MouseEvent as CrosstermMouseEvent, MouseEventKind as CrosstermMouseEventKind, + Event as CrosstermEvent, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseEvent, + MouseEventKind as CrosstermMouseEventKind, }; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; #[cfg(any(feature = "termion", feature = "ratatui-termion"))] use termion::event::{Event as TermionEvent, Key as TermionKey, MouseEvent as TermionMouseEvent}; +#[cfg(feature = "ratatui-termwiz")] +use termwiz::input::{ + InputEvent as TermwizInputEvent, KeyEvent as TermwizKeyEvent, MouseEvent as TermwizMouseEvent, + PixelMouseEvent, +}; /// Backend-agnostic key input kind. /// @@ -136,9 +141,9 @@ impl From for Input { } #[cfg(any(feature = "crossterm", feature = "ratatui-crossterm"))] -impl From for Input { +impl From for Input { /// Convert [`crossterm::event::MouseEvent`] to [`Input`]. - fn from(mouse: CrosstermMouseEvent) -> Self { + fn from(mouse: MouseEvent) -> Self { let key = match mouse.kind { CrosstermMouseEventKind::ScrollDown => Key::MouseScrollDown, CrosstermMouseEventKind::ScrollUp => Key::MouseScrollUp, @@ -218,3 +223,102 @@ impl From for Input { } } } + +#[cfg(feature = "ratatui-termwiz")] +impl From for Input { + /// Convert [`termwiz::input::InputEvent`] to [`Input`]. + fn from(input: TermwizInputEvent) -> Self { + match input { + TermwizInputEvent::Key(key) => Self::from(key), + TermwizInputEvent::Mouse(mouse) => Self::from(mouse), + TermwizInputEvent::PixelMouse(mouse) => Self::from(mouse), + _ => Self::default(), + } + } +} + +#[cfg(feature = "ratatui-termwiz")] +impl From for Input { + /// Convert [`termwiz::input::KeyEvent`] to [`Input`]. + fn from(key: TermwizKeyEvent) -> Self { + use termwiz::input::{KeyCode, Modifiers}; + + let TermwizKeyEvent { key, modifiers } = key; + let key = match key { + KeyCode::Char(c) => Key::Char(c), + KeyCode::Backspace => Key::Backspace, + KeyCode::Tab => Key::Tab, + KeyCode::Enter => Key::Enter, + KeyCode::Escape => Key::Esc, + KeyCode::PageUp => Key::PageUp, + KeyCode::PageDown => Key::PageDown, + KeyCode::End => Key::End, + KeyCode::Home => Key::Home, + KeyCode::LeftArrow => Key::Left, + KeyCode::RightArrow => Key::Right, + KeyCode::UpArrow => Key::Up, + KeyCode::DownArrow => Key::Down, + KeyCode::Delete => Key::Delete, + KeyCode::Function(x) => Key::F(x), + _ => Key::Null, + }; + let ctrl = modifiers.contains(Modifiers::CTRL); + let alt = modifiers.contains(Modifiers::ALT); + + Self { key, ctrl, alt } + } +} + +#[cfg(feature = "ratatui-termwiz")] +impl From for Input { + /// Convert [`termwiz::input::MouseEvent`] to [`Input`]. + fn from(mouse: TermwizMouseEvent) -> Self { + use termwiz::input::{Modifiers, MouseButtons}; + + let TermwizMouseEvent { + mouse_buttons, + modifiers, + .. + } = mouse; + let key = if mouse_buttons.contains(MouseButtons::VERT_WHEEL) { + if mouse_buttons.contains(MouseButtons::WHEEL_POSITIVE) { + Key::MouseScrollDown + } else { + Key::MouseScrollUp + } + } else { + Key::Null + }; + let ctrl = modifiers.contains(Modifiers::CTRL); + let alt = modifiers.contains(Modifiers::ALT); + + Self { key, ctrl, alt } + } +} + +#[cfg(feature = "ratatui-termwiz")] +impl From for Input { + /// Convert [`termwiz::input::PixelMouseEvent`] to [`Input`]. + fn from(mouse: PixelMouseEvent) -> Self { + use termwiz::input::{Modifiers, MouseButtons}; + + let PixelMouseEvent { + mouse_buttons, + modifiers, + .. + } = mouse; + let key = if mouse_buttons.contains(MouseButtons::VERT_WHEEL) { + if mouse_buttons.contains(MouseButtons::WHEEL_POSITIVE) { + Key::MouseScrollDown + } else { + Key::MouseScrollUp + } + } else { + Key::Null + }; + let ctrl = modifiers.contains(Modifiers::CTRL); + let alt = modifiers.contains(Modifiers::ALT); + + Self { key, ctrl, alt } + } +} diff --git a/src/lib.rs b/src/lib.rs index f7e3197..ecbe412 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ any( feature = "ratatui-crossterm", feature = "ratatui-termion", + feature = "ratatui-termwiz", feature = "ratatui-your-backend" ), ))] @@ -29,12 +30,14 @@ mod word; #[cfg(any( feature = "ratatui-crossterm", feature = "ratatui-termion", + feature = "ratatui-termwiz", feature = "ratatui-your-backend", ))] use ratatui as tui; #[cfg(not(any( feature = "ratatui-crossterm", feature = "ratatui-termion", + feature = "ratatui-termwiz", feature = "ratatui-your-backend", )))] #[allow(clippy::single_component_path_imports)] diff --git a/src/textarea.rs b/src/textarea.rs index f689fc1..db40a9c 100644 --- a/src/textarea.rs +++ b/src/textarea.rs @@ -10,12 +10,14 @@ use crate::tui::style::{Color, Modifier, Style}; #[cfg(any( feature = "ratatui-crossterm", feature = "ratatui-termion", + feature = "ratatui-termwiz", feature = "ratatui-your-backend", ))] use crate::tui::text::Line as Spans; #[cfg(not(any( feature = "ratatui-crossterm", feature = "ratatui-termion", + feature = "ratatui-termwiz", feature = "ratatui-your-backend", )))] use crate::tui::text::Spans;