1
0
зеркало из https://github.com/glebtv/tui-textarea.git synced 2026-09-08 00:15:50 +03:00

implement vertial mouse scroll with both crossterm and termion supports

Этот коммит содержится в:
rhysd
2022-07-23 20:09:26 +09:00
родитель 2f7fd0c53d
Коммит b54fe5c01d
3 изменённых файлов: 78 добавлений и 22 удалений

Просмотреть файл

@@ -3,17 +3,17 @@ use std::io;
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use termion::event::Key; use termion::event::Event as TermEvent;
use termion::input::{MouseTerminal, TermRead}; use termion::input::{MouseTerminal, TermRead};
use termion::raw::IntoRawMode; use termion::raw::IntoRawMode;
use termion::screen::AlternateScreen; use termion::screen::AlternateScreen;
use tui::backend::TermionBackend; use tui::backend::TermionBackend;
use tui::widgets::{Block, Borders}; use tui::widgets::{Block, Borders};
use tui::Terminal; use tui::Terminal;
use tui_textarea::TextArea; use tui_textarea::{Input, Key, TextArea};
enum Event { enum Event {
Key(Key), Term(TermEvent),
Tick, Tick,
} }
@@ -25,12 +25,12 @@ fn main() -> Result<(), Box<dyn Error>> {
let mut term = Terminal::new(backend)?; let mut term = Terminal::new(backend)?;
let events = { let events = {
let keys = io::stdin().keys(); let events = io::stdin().events();
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let keys_tx = tx.clone(); let keys_tx = tx.clone();
thread::spawn(move || { thread::spawn(move || {
for key in keys.flatten() { for event in events.flatten() {
keys_tx.send(Event::Key(key)).unwrap(); keys_tx.send(Event::Term(event)).unwrap();
} }
}); });
thread::spawn(move || loop { thread::spawn(move || loop {
@@ -49,10 +49,12 @@ fn main() -> Result<(), Box<dyn Error>> {
loop { loop {
match events.recv()? { match events.recv()? {
Event::Key(Key::Esc) => break, Event::Term(event) => match event.into() {
Event::Key(key) => { Input { key: Key::Esc, .. } => break,
textarea.input(key); input => {
} textarea.input(input);
}
},
Event::Tick => {} Event::Tick => {}
} }
term.draw(|f| { term.draw(|f| {

Просмотреть файл

@@ -1,9 +1,12 @@
#[cfg(feature = "arbitrary")] #[cfg(feature = "arbitrary")]
use arbitrary::Arbitrary; use arbitrary::Arbitrary;
#[cfg(feature = "crossterm")] #[cfg(feature = "crossterm")]
use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers}; use crossterm::event::{
Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers, MouseEvent as CrosstermMouseEvent,
MouseEventKind as CrosstermMouseEventKind,
};
#[cfg(feature = "termion")] #[cfg(feature = "termion")]
use termion::event::{Event as TerimonEvent, Key as TermionKey}; use termion::event::{Event as TermionEvent, Key as TermionKey, MouseEvent as TermionMouseEvent};
/// Backend-agnostic key input kind. /// Backend-agnostic key input kind.
#[non_exhaustive] #[non_exhaustive]
@@ -27,6 +30,10 @@ pub enum Key {
PageUp, PageUp,
PageDown, PageDown,
Esc, Esc,
/// Virtual key to scroll down by mouse
MouseScrollDown,
/// Virtual key to scroll up by mouse
MouseScrollUp,
/// An invalid key input (this key is always ignored by [`TextArea`](crate::TextArea)). /// An invalid key input (this key is always ignored by [`TextArea`](crate::TextArea)).
Null, Null,
} }
@@ -90,10 +97,10 @@ impl Default for Input {
impl From<CrosstermEvent> for Input { impl From<CrosstermEvent> for Input {
/// Convert [`crossterm::event::Event`] to [`Input`]. /// Convert [`crossterm::event::Event`] to [`Input`].
fn from(event: CrosstermEvent) -> Self { fn from(event: CrosstermEvent) -> Self {
if let CrosstermEvent::Key(key) = event { match event {
Self::from(key) CrosstermEvent::Key(key) => Self::from(key),
} else { CrosstermEvent::Mouse(mouse) => Self::from(mouse),
Self::default() _ => Self::default(),
} }
} }
} }
@@ -126,14 +133,29 @@ impl From<KeyEvent> for Input {
} }
} }
#[cfg(feature = "crossterm")]
impl From<CrosstermMouseEvent> for Input {
/// Convert [`crossterm::event::MouseEvent`] to [`Input`].
fn from(mouse: CrosstermMouseEvent) -> Self {
let key = match mouse.kind {
CrosstermMouseEventKind::ScrollDown => Key::MouseScrollDown,
CrosstermMouseEventKind::ScrollUp => Key::MouseScrollUp,
_ => return Self::default(),
};
let ctrl = mouse.modifiers.contains(KeyModifiers::CONTROL);
let alt = mouse.modifiers.contains(KeyModifiers::ALT);
Self { key, ctrl, alt }
}
}
#[cfg(feature = "termion")] #[cfg(feature = "termion")]
impl From<TerimonEvent> for Input { impl From<TermionEvent> for Input {
/// Convert [`termion::event::Event`] to [`Input`]. /// Convert [`termion::event::Event`] to [`Input`].
fn from(event: TerimonEvent) -> Self { fn from(event: TermionEvent) -> Self {
if let TerimonEvent::Key(key) = event { match event {
Self::from(key) TermionEvent::Key(key) => Self::from(key),
} else { TermionEvent::Mouse(mouse) => Self::from(mouse),
Self::default() _ => Self::default(),
} }
} }
} }
@@ -176,3 +198,21 @@ impl From<TermionKey> for Input {
Input { key, ctrl, alt } Input { key, ctrl, alt }
} }
} }
#[cfg(feature = "termion")]
impl From<TermionMouseEvent> for Input {
/// Convert [`termion::event::MouseEvent`] to [`Input`].
fn from(mouse: TermionMouseEvent) -> Self {
use termion::event::MouseButton;
let key = match mouse {
TermionMouseEvent::Press(MouseButton::WheelUp, ..) => Key::MouseScrollUp,
TermionMouseEvent::Press(MouseButton::WheelDown, ..) => Key::MouseScrollDown,
_ => return Self::default(),
};
Self {
key,
ctrl: false,
alt: false,
}
}
}

Просмотреть файл

@@ -441,6 +441,20 @@ impl<'a> TextArea<'a> {
ctrl: true, ctrl: true,
alt: false, alt: false,
} => self.paste(), } => self.paste(),
Input {
key: Key::MouseScrollDown,
..
} => {
self.scroll(1, 0);
false
}
Input {
key: Key::MouseScrollUp,
..
} => {
self.scroll(-1, 0);
false
}
_ => false, _ => false,
}; };