Files
galaxy/crates/warpui_core/src/event.rs
T

417 lines
14 KiB
Rust

use std::ops::Range;
use crate::platform::keyboard::KeyCode;
use crate::{
elements::{Point, ZIndex},
keymap::Keystroke,
zoom::{Scale, ZoomFactor},
EventContext,
};
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
#[derive(Debug)]
pub struct DispatchedEvent {
event: Event,
}
impl DispatchedEvent {
/// Filters out event types that most-likely shouldn't be handled if this
/// event is being received by an element at the given z-index
pub fn at_z_index(&self, z_index: ZIndex, ctx: &EventContext) -> Option<&Event> {
match self.event {
Event::KeyDown { .. } => Some(&self.event),
Event::ScrollWheel { position, .. }
| Event::LeftMouseDown { position, .. }
| Event::LeftMouseUp { position, .. }
| Event::LeftMouseDragged { position, .. }
| Event::MiddleMouseDown { position, .. }
| Event::RightMouseDown { position, .. }
| Event::BackMouseDown { position, .. }
| Event::ForwardMouseDown { position, .. } => {
if !ctx.is_covered(Point::from_vec2f(position, z_index)) {
Some(&self.event)
} else {
None
}
}
Event::MouseMoved { .. } => Some(&self.event),
Event::ModifierStateChanged { .. } => Some(&self.event),
Event::ModifierKeyChanged { .. } => Some(&self.event),
Event::TypedCharacters { .. } => Some(&self.event),
Event::DragAndDropFiles { .. } => Some(&self.event),
Event::DragFiles { .. } => Some(&self.event),
Event::DragFileExit => Some(&self.event),
Event::SetMarkedText { .. } => Some(&self.event),
Event::ClearMarkedText => Some(&self.event),
}
}
/// Returns the raw event - note that an element at a higher z-index
/// may already have handled it.
pub fn raw_event(&self) -> &Event {
&self.event
}
}
impl From<Event> for DispatchedEvent {
fn from(event: Event) -> Self {
Self { event }
}
}
/// Additional metadata about key events.
#[derive(Clone, Debug, Default)]
pub struct KeyEventDetails {
// allows distinguishing between left and right alt
pub left_alt: bool,
pub right_alt: bool,
/// The key that would have been produced without any modifiers (including Shift).
pub key_without_modifiers: Option<String>,
}
#[derive(Copy, Clone, Debug, Default)]
pub struct ModifiersState {
pub alt: bool,
pub cmd: bool,
pub shift: bool,
pub ctrl: bool,
/// The function key, often labeled as "fn" on keyboards.
/// We use "func" to avoid clashing with the `fn` keyword.
/// Note this is NOT fully implemented for non-Mac platforms yet.
pub func: bool,
}
#[derive(Copy, Clone, Debug)]
pub enum KeyState {
Pressed,
Released,
}
/// TODO: for the events that have modifiers (e.g. cmd, shift), we should
/// combine these into a Modifiers struct and pass these along from fn to fn.
#[derive(Clone, Debug, strum_macros::EnumDiscriminants)]
pub enum Event {
/// Gets fired when a key is pressed. The keystroke attribute contains the raw
/// key code and its modifiers.
KeyDown {
keystroke: Keystroke,
chars: String,
details: KeyEventDetails,
is_composing: bool,
},
ScrollWheel {
position: Vector2F,
delta: Vector2F,
precise: bool,
modifiers: ModifiersState,
},
LeftMouseDown {
position: Vector2F,
modifiers: ModifiersState,
click_count: u32,
/// Whether this is the first mouse down event on an inactive window
/// that is causing the window to activate.
is_first_mouse: bool,
},
LeftMouseUp {
position: Vector2F,
modifiers: ModifiersState,
},
LeftMouseDragged {
position: Vector2F,
modifiers: ModifiersState,
},
MiddleMouseDown {
position: Vector2F,
cmd: bool,
shift: bool,
click_count: u32,
},
RightMouseDown {
position: Vector2F,
cmd: bool,
shift: bool,
click_count: u32,
},
/// One of the side buttons often found on external mice.
/// It cycles forward between tabs by default.
ForwardMouseDown {
position: Vector2F,
cmd: bool,
shift: bool,
click_count: u32,
},
/// One of the side buttons often found on external mice.
/// It cycles backward between tabs by default.
BackMouseDown {
position: Vector2F,
cmd: bool,
shift: bool,
click_count: u32,
},
MouseMoved {
position: Vector2F,
cmd: bool,
shift: bool,
/// Whether this mouse event was initiated by the user or by Warp synthetically.
/// We create such synthetic mouse events for certain behaviors behaviors within
/// Warp, such as triggering the correct tab to be "hovered" when the user closes
/// a tab to the left. In this case, there's no user-initiated mouse event, however,
/// we create a synthetic mouse event so that we carry over the "hovered" state from
/// the old tab (closed) to the new tab (shifted). Certain elements within Warp,
/// such as the alt-screen, may not want such synthetic mouse events since it can
/// interfere with actions such as mouse dragging.
is_synthetic: bool,
},
/// Gets fired when the modifier flag states changed -- this could happen either
/// when a user presses down on or releases a modifier key.
ModifierStateChanged {
// Note that in web framework modifier keypresses do not contain mouse
// position information. But we also have cases where modifier flag state
// is closely coupled with mouse position for determine whether certain events
// should be fired. This position meta data will be kept in the event for now,
// we could always remove it in the future if this does not fit.
mouse_position: Vector2F,
modifiers: ModifiersState,
/// The specific key code for the event. Can be used to identify which modifier key
/// was pressed or released.
key_code: Option<KeyCode>,
},
/// Gets fired when a modifier key is pressed/released. Contains details on whether the
/// key was pressed or released and the key code.
ModifierKeyChanged {
key_code: KeyCode,
state: KeyState,
},
/// Gets fired when a printable character is produced by the text input system and
/// the corresponding KeyDown event is not handled. This event is not dispatched
/// when intermediary keys are pressed (e.g. dead keys or key presses in the IME).
/// TypedCharacter needs to be of type String because CJK languages represent words
/// with a set of characters and thus the output from IME could be more than one single character.
TypedCharacters {
chars: String,
},
/// Gets fired when user drags a file or folder into Warp. Note that there could exist
/// multiple file paths in one event as user could drag and drop multiple targets.
DragAndDropFiles {
paths: Vec<String>,
location: Vector2F,
},
DragFiles {
location: Vector2F,
},
DragFileExit,
SetMarkedText {
marked_text: String,
selected_range: Range<usize>,
},
ClearMarkedText,
}
impl Event {
/// Returns the mouse-down position of the event,
/// iff the event is one of the many `*MouseDown` events.
pub fn mouse_down_position(&self) -> Option<Vector2F> {
match self {
Self::LeftMouseDown { position, .. }
| Self::RightMouseDown { position, .. }
| Self::MiddleMouseDown { position, .. }
| Self::ForwardMouseDown { position, .. }
| Self::BackMouseDown { position, .. } => Some(*position),
_ => None,
}
}
/// Returns a copy of the event marked as synthetic, if this is a
/// `MouseMoved` event, otherwise returns [`None`].
pub fn to_synthetic_mouse_move_event(&self) -> Option<Self> {
if let Event::MouseMoved {
cmd,
shift,
position,
is_synthetic: _,
} = self
{
Some(Event::MouseMoved {
cmd: *cmd,
shift: *shift,
position: *position,
is_synthetic: true,
})
} else {
None
}
}
}
pub trait InBoundsExt {
/// Check whether something occurred within the given bounding box.
fn in_bounds(&self, bounds: RectF) -> bool;
}
impl InBoundsExt for Event {
fn in_bounds(&self, bounds: RectF) -> bool {
use Event::*;
match self {
ScrollWheel { position, .. }
| LeftMouseDown { position, .. }
| LeftMouseUp { position, .. }
| LeftMouseDragged { position, .. }
| RightMouseDown { position, .. }
| MouseMoved { position, .. }
| MiddleMouseDown { position, .. } => bounds.contains_point(*position),
ModifierStateChanged { mouse_position, .. } => bounds.contains_point(*mouse_position),
DragAndDropFiles { location, .. } | DragFiles { location } => {
bounds.contains_point(*location)
}
// This trait is meant to check whether the Self is within the bounds,
// however in this implementation Self is Event that may not always be related to the
// mouse - so for all such cases, lets just return true.
_ => true,
}
}
}
impl Scale for Event {
fn scale_up(self, zoom_factor: ZoomFactor) -> Self {
match self {
Event::ScrollWheel {
position,
delta,
precise,
modifiers,
} => Event::ScrollWheel {
position: position.scale_up(zoom_factor),
delta,
precise,
modifiers,
},
Event::LeftMouseDown {
position,
modifiers,
click_count,
is_first_mouse,
} => Event::LeftMouseDown {
position: position.scale_up(zoom_factor),
modifiers,
click_count,
is_first_mouse,
},
Event::LeftMouseUp {
position,
modifiers,
} => Event::LeftMouseUp {
position: position.scale_up(zoom_factor),
modifiers,
},
Event::LeftMouseDragged {
position,
modifiers,
} => Event::LeftMouseDragged {
position: position.scale_up(zoom_factor),
modifiers,
},
Event::MiddleMouseDown {
position,
cmd,
shift,
click_count,
} => Event::MiddleMouseDown {
position: position.scale_up(zoom_factor),
cmd,
shift,
click_count,
},
Event::RightMouseDown {
position,
cmd,
shift,
click_count,
} => Event::RightMouseDown {
position: position.scale_up(zoom_factor),
cmd,
shift,
click_count,
},
Event::ForwardMouseDown {
position,
cmd,
shift,
click_count,
} => Event::ForwardMouseDown {
position: position.scale_up(zoom_factor),
cmd,
shift,
click_count,
},
Event::BackMouseDown {
position,
cmd,
shift,
click_count,
} => Event::BackMouseDown {
position: position.scale_up(zoom_factor),
cmd,
shift,
click_count,
},
Event::MouseMoved {
position,
cmd,
shift,
is_synthetic,
} => Event::MouseMoved {
position: position.scale_up(zoom_factor),
cmd,
shift,
is_synthetic,
},
Event::ModifierStateChanged {
mouse_position,
modifiers,
key_code,
} => Event::ModifierStateChanged {
mouse_position: mouse_position.scale_up(zoom_factor),
modifiers,
key_code,
},
Event::DragAndDropFiles { paths, location } => Event::DragAndDropFiles {
paths,
location: location.scale_up(zoom_factor),
},
Event::DragFiles { location } => Event::DragFiles {
location: location.scale_up(zoom_factor),
},
Event::DragFileExit => Event::DragFileExit,
Event::KeyDown {
keystroke,
chars,
details,
is_composing,
} => Event::KeyDown {
keystroke,
chars,
details,
is_composing,
},
Event::ModifierKeyChanged { key_code, state } => {
Event::ModifierKeyChanged { key_code, state }
}
Event::TypedCharacters { chars } => Event::TypedCharacters { chars },
Event::SetMarkedText {
marked_text,
selected_range,
} => Event::SetMarkedText {
marked_text,
selected_range,
},
Event::ClearMarkedText => Event::ClearMarkedText,
}
}
}