Skip to content

Commit

Permalink
Add RawMouseEvent which allows negative positions (fixes a bug)
Browse files Browse the repository at this point in the history
Since the mouse position is relative to the origin of a widget,
mouse positions can be negative, previously they were clamped to 0,
which led to "always hovering" situations even if the mouse was above,
or left to a widget. This should fix the issue.
  • Loading branch information
Philipp-M committed Feb 10, 2024
1 parent b8999d7 commit fb80bcf
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 36 deletions.
2 changes: 1 addition & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ impl<T: Send + 'static, V: View<T> + 'static> App<T, V> {
code: KeyCode::Esc, ..
})) => Event::Quit,
Ok(CxEvent::Key(key_event)) => Event::Key(key_event),
Ok(CxEvent::Mouse(mouse_event)) => Event::Mouse(mouse_event),
Ok(CxEvent::Mouse(mouse_event)) => Event::Mouse(mouse_event.into()),
Ok(CxEvent::FocusGained) => Event::FocusGained,
Ok(CxEvent::FocusLost) => Event::FocusLost,
// CxEvent::Paste(_) => todo!(),
Expand Down
3 changes: 1 addition & 2 deletions src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ mod text;
mod weighted_linear_layout;

pub use self::core::{
AnyWidget, ChangeFlags, CxState, Event, EventCx, LayoutCx, LifeCycleCx, Message, PaintCx, Pod,
Widget,
AnyWidget, ChangeFlags, CxState, EventCx, LayoutCx, LifeCycleCx, Message, PaintCx, Pod, Widget,
};
pub(crate) use self::core::{PodFlags, WidgetState};
pub(crate) use border::Border;
Expand Down
27 changes: 4 additions & 23 deletions src/widget/core.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use super::{BoxConstraints, LifeCycle};
use super::{BoxConstraints, Event, LifeCycle};
use crate::geometry::{Point, Rect, Size};
use bitflags::bitflags;
pub use crossterm::event::MouseEvent;
use crossterm::event::{KeyEvent, MouseEventKind};
use crossterm::event::MouseEventKind;
use ratatui::Terminal;
use std::{any::Any, ops::DerefMut};
use xilem_core::{message, Id};
Expand All @@ -18,24 +17,6 @@ use std::io::Stdout;

message!(Send);

#[derive(Debug, Clone)]
pub enum Event {
/// Only sent once at the start of the application
Start,
Quit,
/// Sent e.g. when a future requests waking up the application
Wake,
FocusLost,
FocusGained,
Resize {
width: u16,
height: u16,
},
// TODO create a custom type...
Mouse(MouseEvent),
Key(KeyEvent),
}

/// Static state that is shared between most contexts.
pub struct CxState<'a> {
messages: &'a mut Vec<Message>,
Expand Down Expand Up @@ -471,8 +452,8 @@ impl Pod {
{
let mut mouse_event = *mouse_event;
let (x, y) = (
self.state.origin.x.round() as u16,
self.state.origin.y.round() as u16,
self.state.origin.x.round() as i16,
self.state.origin.y.round() as i16,
);
mouse_event.column = mouse_event.column.saturating_sub(x);
mouse_event.row = mouse_event.row.saturating_sub(y);
Expand Down
58 changes: 48 additions & 10 deletions src/widget/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,26 @@ use ratatui::style::Style;

use super::{
core::{IdPath, PaintCx},
Event, EventCx, LayoutCx, Message, Pod, Widget,
EventCx, LayoutCx, Message, Pod, Widget,
};

#[derive(Debug, Clone)]
pub enum Event {
/// Only sent once at the start of the application
Start,
Quit,
/// Sent e.g. when a future requests waking up the application
Wake,
FocusLost,
FocusGained,
Resize {
width: u16,
height: u16,
},
Mouse(RawMouseEvent),
Key(crossterm::event::KeyEvent),
}

#[derive(Debug)]
pub enum LifeCycle {
HotChanged(bool),
Expand All @@ -24,6 +41,25 @@ pub struct ViewContext {
pub mouse_position: Option<Point>,
}

#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
pub struct RawMouseEvent {
pub kind: MouseEventKind,
pub column: i16,
pub row: i16,
pub modifiers: crossterm::event::KeyModifiers,
}

impl From<crossterm::event::MouseEvent> for RawMouseEvent {
fn from(event: crossterm::event::MouseEvent) -> Self {
RawMouseEvent {
kind: event.kind,
column: event.column as i16,
row: event.row as i16,
modifiers: event.modifiers,
}
}
}

impl ViewContext {
pub fn translate_to(&self, new_origin: Point) -> ViewContext {
// TODO I think the clip calculation is buggy in xilem (width/height?)
Expand All @@ -42,19 +78,21 @@ impl ViewContext {
}
}

// TODO separate the widgets etc. into its own module?

#[derive(Debug)]
/// A message representing a mouse event.
pub struct MouseEvent {
pub over_element: bool,
pub is_active: bool,
pub kind: MouseEventKind,
pub column: u16,
pub row: u16,
pub column: i16,
pub row: i16,
pub modifiers: crossterm::event::KeyModifiers,
}

impl MouseEvent {
fn new(event: crossterm::event::MouseEvent, over_element: bool, is_active: bool) -> Self {
fn new(event: RawMouseEvent, over_element: bool, is_active: bool) -> Self {
MouseEvent {
over_element,
is_active,
Expand Down Expand Up @@ -108,7 +146,7 @@ impl<E: Widget> Widget for OnMouse<E> {

match event {
Event::Mouse(
event @ crossterm::event::MouseEvent {
event @ RawMouseEvent {
kind: MouseEventKind::Down(button),
..
},
Expand All @@ -128,7 +166,7 @@ impl<E: Widget> Widget for OnMouse<E> {
));
}
}
Event::Mouse(event @ crossterm::event::MouseEvent { kind, .. }) => {
Event::Mouse(event @ RawMouseEvent { kind, .. }) => {
let is_active = cx.is_active();
if matches!(kind, MouseEventKind::Up(_)) {
cx.set_active(false);
Expand Down Expand Up @@ -188,7 +226,7 @@ impl<E: Widget> Widget for OnClick<E> {
fn event(&mut self, cx: &mut EventCx, event: &Event) {
self.element.event(cx, event);

if let Event::Mouse(crossterm::event::MouseEvent {
if let Event::Mouse(RawMouseEvent {
kind: MouseEventKind::Down(MouseButton::Left),
..
}) = event
Expand All @@ -197,7 +235,7 @@ impl<E: Widget> Widget for OnClick<E> {
}

// TODO handle other events like e.g. FocusLost
if let Event::Mouse(crossterm::event::MouseEvent {
if let Event::Mouse(RawMouseEvent {
kind: MouseEventKind::Up(MouseButton::Left),
..
}) = event
Expand Down Expand Up @@ -374,14 +412,14 @@ impl Widget for StyleOnPressed {
self.element.event(cx, event);

match event {
Event::Mouse(crossterm::event::MouseEvent {
Event::Mouse(RawMouseEvent {
kind: MouseEventKind::Down(MouseButton::Left),
..
}) => {
cx.request_paint();
cx.set_active(cx.is_hot());
}
Event::Mouse(crossterm::event::MouseEvent {
Event::Mouse(RawMouseEvent {
kind: MouseEventKind::Up(MouseButton::Left) | MouseEventKind::Moved,
..
})
Expand Down

0 comments on commit fb80bcf

Please sign in to comment.