rustcrosstermratatui

Detecting mouse click events on Blocks when using Ratatui


The ratatui library for building terminal apps has a widget for rendering rectangles on screen called Block. https://docs.rs/ratatui/latest/ratatui/widgets/block/struct.Block.html.

I would like to detect mouse click events on a Block when using ratatui with crossterm, i.e. invoke a callback when a click falls anywhere within the bounds of the block. I'm aware that this functionality isn't natively provided "out of the box" by either library, so I'm mostly asking

  1. Is there a third party library/widget which adds expanded click detection?, or
  2. Is there a way to implement generic click detection logic for blocks that doesn't require doing a bunch of complicated mouse event math every time?

Solution

  • Because ratatui is an immediate-mode rendering framework, the most natural way to do this is to detect mouse events in render() and pass the events as a parameter to your widgets during the render loop:

    pub struct MyWidget {
      pub event: Option<MouseEvent>
    }
    

    Then in the implementation of your widget, you can check whether the event falls within the bounds that you're drawing:

    impl Widget for MyWidget {
      fn render(self, area: Rect, buf: &mut Buffer) {
        let Block = Block::default();
        let target = block.inner(area);
        if self.event.kind == MouseEventKind::Up(MouseButton::Left) &&
          target.contains(Position::new(self.event.column, self.event.row)) {
          // Handle click event
        }
        block.render(area, buf);
      }
    }
    

    Unlike with a traditional retained-mode UI framework, it's standard to combine event handling code inline with drawing code in this manner.