mirror of
https://github.com/ratatui/ratatui.git
synced 2025-09-28 05:21:23 +00:00
124 lines
4.2 KiB
Rust
124 lines
4.2 KiB
Rust
/// A Ratatui example that demonstrates how to handle mouse events.
|
||
///
|
||
/// This example demonstrates how to handle mouse events in Ratatui. You can draw lines by
|
||
/// clicking and dragging the mouse.
|
||
///
|
||
/// This example runs with the Ratatui library code in the branch that you are currently
|
||
/// reading. See the [`latest`] branch for the code which works with the most recent Ratatui
|
||
/// release.
|
||
///
|
||
/// [`latest`]: https://github.com/ratatui/ratatui/tree/latest
|
||
use color_eyre::Result;
|
||
use crossterm::{
|
||
event::{
|
||
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEvent,
|
||
MouseEventKind,
|
||
},
|
||
execute,
|
||
};
|
||
use ratatui::{
|
||
layout::{Position, Rect, Size},
|
||
style::{Color, Stylize},
|
||
symbols,
|
||
text::Line,
|
||
DefaultTerminal, Frame,
|
||
};
|
||
|
||
fn main() -> Result<()> {
|
||
color_eyre::install()?;
|
||
let terminal = ratatui::init();
|
||
let result = MouseDrawingApp::default().run(terminal);
|
||
ratatui::restore();
|
||
result
|
||
}
|
||
|
||
#[derive(Default)]
|
||
struct MouseDrawingApp {
|
||
// Whether the app should exit
|
||
pub should_exit: bool,
|
||
// The last known mouse position
|
||
pub mouse_position: Option<Position>,
|
||
// The points that have been clicked / drawn by dragging the mouse
|
||
pub points: Vec<(Position, Color)>,
|
||
// The color to draw with
|
||
pub current_color: Color,
|
||
}
|
||
|
||
impl MouseDrawingApp {
|
||
fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
|
||
execute!(std::io::stdout(), EnableMouseCapture)?;
|
||
while !self.should_exit {
|
||
terminal.draw(|frame| self.render(frame))?;
|
||
self.handle_events()?;
|
||
}
|
||
execute!(std::io::stdout(), DisableMouseCapture)?;
|
||
Ok(())
|
||
}
|
||
|
||
fn handle_events(&mut self) -> Result<()> {
|
||
match event::read()? {
|
||
Event::Key(event) => self.on_key_event(event),
|
||
Event::Mouse(event) => self.on_mouse_event(event),
|
||
_ => {}
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
/// Quit the app if the user presses 'q' or 'Esc'
|
||
fn on_key_event(&mut self, event: KeyEvent) {
|
||
match event.code {
|
||
KeyCode::Char(' ') => {
|
||
self.current_color = Color::Rgb(rand::random(), rand::random(), rand::random());
|
||
}
|
||
KeyCode::Char('q') | KeyCode::Esc => self.should_exit = true,
|
||
_ => {}
|
||
}
|
||
}
|
||
|
||
/// Adds any points which were clicked or dragged to the `points` vector.
|
||
fn on_mouse_event(&mut self, event: MouseEvent) {
|
||
let position = Position::new(event.column, event.row);
|
||
match event.kind {
|
||
MouseEventKind::Down(_) => self.points.push((position, self.current_color)),
|
||
MouseEventKind::Drag(_) => self.draw_line(position),
|
||
_ => {}
|
||
}
|
||
self.mouse_position = Some(position);
|
||
}
|
||
|
||
/// Draw a line between the last point and the given position
|
||
fn draw_line(&mut self, position: Position) {
|
||
if let Some(start) = self.points.last() {
|
||
let (x0, y0) = (i32::from(start.0.x), i32::from(start.0.y));
|
||
let (x1, y1) = (i32::from(position.x), i32::from(position.y));
|
||
for (x, y) in line_drawing::Bresenham::new((x0, y0), (x1, y1)) {
|
||
let point = (Position::new(x as u16, y as u16), self.current_color);
|
||
self.points.push(point);
|
||
}
|
||
}
|
||
}
|
||
|
||
fn render(&self, frame: &mut Frame) {
|
||
// call order is important here as later elements are drawn on top of earlier elements
|
||
self.render_points(frame);
|
||
self.render_mouse_cursor(frame);
|
||
let value = "Mouse Example ('Esc' to quit. Click / drag to draw. 'Space' to change color)";
|
||
let title = Line::from(value).centered();
|
||
frame.render_widget(title, frame.area());
|
||
}
|
||
|
||
fn render_points(&self, frame: &mut Frame<'_>) {
|
||
for (position, color) in &self.points {
|
||
let area = Rect::from((*position, Size::new(1, 1))).clamp(frame.area());
|
||
frame.render_widget(symbols::block::FULL.fg(*color), area);
|
||
}
|
||
}
|
||
|
||
fn render_mouse_cursor(&self, frame: &mut Frame<'_>) {
|
||
if let Some(position) = self.mouse_position {
|
||
let area = Rect::from((position, Size::new(1, 1))).clamp(frame.area());
|
||
frame.render_widget("╳".bg(self.current_color), area);
|
||
}
|
||
}
|
||
}
|