зеркало из
https://github.com/glebtv/tui-textarea.git
synced 2026-09-07 16:05:50 +03:00
implement vertial mouse scroll with both crossterm and termion supports
Этот коммит содержится в:
@@ -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| {
|
||||||
|
|||||||
64
src/input.rs
64
src/input.rs
@@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user