645 lines
18 KiB
Rust
645 lines
18 KiB
Rust
use crate::display_object::InteractiveObject;
|
|
use serde::Deserialize;
|
|
use swf::ClipEventFlag;
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub enum PlayerEvent {
|
|
KeyDown {
|
|
key_code: KeyCode,
|
|
key_char: Option<char>,
|
|
},
|
|
KeyUp {
|
|
key_code: KeyCode,
|
|
key_char: Option<char>,
|
|
},
|
|
MouseMove {
|
|
x: f64,
|
|
y: f64,
|
|
},
|
|
MouseUp {
|
|
x: f64,
|
|
y: f64,
|
|
button: MouseButton,
|
|
},
|
|
MouseDown {
|
|
x: f64,
|
|
y: f64,
|
|
button: MouseButton,
|
|
},
|
|
MouseLeave,
|
|
MouseWheel {
|
|
delta: MouseWheelDelta,
|
|
},
|
|
TextInput {
|
|
codepoint: char,
|
|
},
|
|
TextControl {
|
|
code: TextControlCode,
|
|
},
|
|
}
|
|
|
|
/// The distance scrolled by the mouse wheel.
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub enum MouseWheelDelta {
|
|
Lines(f64),
|
|
Pixels(f64),
|
|
}
|
|
|
|
impl MouseWheelDelta {
|
|
const MOUSE_WHEEL_SCALE: f64 = 100.0;
|
|
|
|
/// Returns the number of lines that this delta represents.
|
|
pub fn lines(self) -> f64 {
|
|
// TODO: Should we always return an integer here?
|
|
match self {
|
|
Self::Lines(delta) => delta,
|
|
Self::Pixels(delta) => delta / Self::MOUSE_WHEEL_SCALE,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialEq for MouseWheelDelta {
|
|
fn eq(&self, rhs: &Self) -> bool {
|
|
match (self, rhs) {
|
|
(Self::Lines(s), Self::Lines(r))
|
|
| (Self::Pixels(s), Self::Pixels(r))
|
|
| (Self::Pixels(s), Self::Lines(r))
|
|
| (Self::Lines(s), Self::Pixels(r))
|
|
if s.is_nan() && r.is_nan() =>
|
|
{
|
|
true
|
|
}
|
|
(Self::Lines(s), Self::Lines(r)) => s == r,
|
|
(Self::Pixels(s), Self::Pixels(r)) => s == r,
|
|
(Self::Pixels(s), Self::Lines(r)) => *s == r * Self::MOUSE_WHEEL_SCALE,
|
|
(Self::Lines(s), Self::Pixels(r)) => s * Self::MOUSE_WHEEL_SCALE == *r,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Eq for MouseWheelDelta {}
|
|
|
|
/// Whether this button event was handled by some child.
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
pub enum ClipEventResult {
|
|
NotHandled,
|
|
Handled,
|
|
}
|
|
|
|
/// An event type that can be handled by a movie clip instance.
|
|
///
|
|
/// Clip events come in three flavors: broadcast, anycast and targeted. An
|
|
/// anycast event is provided to the first `DisplayObject` that claims it, in
|
|
/// render list order. Targeted events are sent to a particular object and are
|
|
/// lost if not handled by the object. Broadcast events are delivered to all
|
|
/// objects in the display list tree.
|
|
///
|
|
/// These events are consumed both by display objects themselves as well as
|
|
/// event handlers in AVM1 and AVM2. These have slightly different event
|
|
/// handling semantics:
|
|
///
|
|
/// * AVM1 delivers broadcasts via `ClipEvent` or system listeners
|
|
/// * AVM2 delivers broadcasts to all registered `EventDispatcher`s
|
|
/// * Anycast events are not delivered to AVM2
|
|
/// * Targeted events are supported and consumed by both VMs
|
|
/// * AVM2 additionally supports bubble/capture, which AVM1 and
|
|
/// `InteractiveObject` itself does not support
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
pub enum ClipEvent<'gc> {
|
|
Construct,
|
|
Data,
|
|
|
|
/// Mouse moved out of a display object while the primary button is held
|
|
/// down.
|
|
///
|
|
/// This is a targeted equivalent to `MouseMove` and is available in both
|
|
/// AVM1 and AVM2. In AVM2, it is dispatched identically to `RollOut`, with
|
|
/// the only difference being that the `buttonDown` flag is set to true.
|
|
DragOut {
|
|
to: Option<InteractiveObject<'gc>>,
|
|
},
|
|
|
|
/// Mouse moved into of a display object while the primary button is held
|
|
/// down.
|
|
///
|
|
/// This is a targeted equivalent to `MouseMove` and is available in both
|
|
/// AVM1 and AVM2. In AVM2, it is dispatched identically to `RollOver`,
|
|
/// with the only difference being that the `buttonDown` flag is set to
|
|
/// true.
|
|
DragOver {
|
|
from: Option<InteractiveObject<'gc>>,
|
|
},
|
|
EnterFrame,
|
|
Initialize,
|
|
KeyUp,
|
|
KeyDown,
|
|
KeyPress {
|
|
key_code: ButtonKeyCode,
|
|
},
|
|
Load,
|
|
|
|
/// Mouse button was released.
|
|
///
|
|
/// This is an anycast event.
|
|
MouseUp,
|
|
|
|
/// Mouse button was released inside this current display object.
|
|
///
|
|
/// This is a targeted equivalent to `MouseUp` and corresponds directly to
|
|
/// the AVM2 `mouseUp` event, which has no AVM1 equivalent. The target of
|
|
/// this event is determined by the position of the mouse cursor.
|
|
MouseUpInside,
|
|
|
|
/// Mouse button was pressed.
|
|
///
|
|
/// This is an anycast event.
|
|
MouseDown,
|
|
|
|
/// Mouse was moved.
|
|
///
|
|
/// This is an anycast event.
|
|
MouseMove,
|
|
|
|
/// Mouse was moved inside this current display object.
|
|
///
|
|
/// This is a targeted equivalent to `MouseMove` to support AVM2's
|
|
/// `mouseMove` event, since AVM2 cannot consume anycast events.
|
|
MouseMoveInside,
|
|
|
|
/// Mouse button was pressed inside this current display object.
|
|
///
|
|
/// This is a targeted equivalent to `MouseDown` and is available in both
|
|
/// AVM1 and AVM2. The target of this event is determined by the position
|
|
/// of the mouse cursor.
|
|
Press,
|
|
|
|
/// Mouse moved out of a display object.
|
|
///
|
|
/// This is a targeted equivalent to `MouseMove` and is available in both
|
|
/// AVM1 and AVM2. Confusingly, it covers both `mouseOut` and `rollOut`,
|
|
/// the difference being that the former bubbles, while the latter only
|
|
/// fires when the cursor has left the parent *and* it's children.
|
|
///
|
|
/// The parameter `to` is the current object that is now under the cursor.
|
|
RollOut {
|
|
to: Option<InteractiveObject<'gc>>,
|
|
},
|
|
|
|
/// Mouse moved into a display object.
|
|
///
|
|
/// This is a targeted equivalent to `MouseMove` and is available in both
|
|
/// AVM1 and AVM2. Confusingly, it covers both `mouseOver` and `rollOver`,
|
|
/// the difference being that the former bubbles, while the latter only
|
|
/// fires when the cursor has left the parent *and* it's children.
|
|
///
|
|
/// The parameter `from` is the previous object that was under the cursor
|
|
/// before this one.
|
|
RollOver {
|
|
from: Option<InteractiveObject<'gc>>,
|
|
},
|
|
|
|
/// Mouse button was released inside a previously-pressed display object.
|
|
///
|
|
/// This is a targeted equivalent to `MouseUp` and is available in both
|
|
/// AVM1 and AVM2. The target of this event is the last target of the
|
|
/// `Press` event.
|
|
Release,
|
|
|
|
/// Mouse button was released outside a previously-pressed display object.
|
|
///
|
|
/// This is a targeted equivalent to `MouseUp` and is available in both
|
|
/// AVM1 and AVM2. The target of this event is the last target of the
|
|
/// `Press` event.
|
|
ReleaseOutside,
|
|
Unload,
|
|
|
|
/// Mouse wheel was turned over a particular display object.
|
|
///
|
|
/// This is a targeted event with no anycast equivalent. It is targeted to
|
|
/// any interactive object under the mouse cursor, including the stage
|
|
/// itself. Only AVM2 can receive these events.
|
|
MouseWheel {
|
|
delta: MouseWheelDelta,
|
|
},
|
|
}
|
|
|
|
impl<'gc> ClipEvent<'gc> {
|
|
/// Method names for button event handles.
|
|
pub const BUTTON_EVENT_METHODS: [&'static str; 7] = [
|
|
"onDragOver",
|
|
"onDragOut",
|
|
"onPress",
|
|
"onRelease",
|
|
"onReleaseOutside",
|
|
"onRollOut",
|
|
"onRollOver",
|
|
];
|
|
|
|
pub const BUTTON_EVENT_FLAGS: ClipEventFlag = ClipEventFlag::from_bits_truncate(
|
|
ClipEventFlag::DRAG_OUT.bits()
|
|
| ClipEventFlag::DRAG_OVER.bits()
|
|
| ClipEventFlag::KEY_PRESS.bits()
|
|
| ClipEventFlag::PRESS.bits()
|
|
| ClipEventFlag::ROLL_OUT.bits()
|
|
| ClipEventFlag::ROLL_OVER.bits()
|
|
| ClipEventFlag::RELEASE.bits()
|
|
| ClipEventFlag::RELEASE_OUTSIDE.bits(),
|
|
);
|
|
|
|
/// Returns the `swf::ClipEventFlag` corresponding to this event type.
|
|
pub const fn flag(self) -> Option<ClipEventFlag> {
|
|
match self {
|
|
ClipEvent::Construct => Some(ClipEventFlag::CONSTRUCT),
|
|
ClipEvent::Data => Some(ClipEventFlag::DATA),
|
|
ClipEvent::DragOut { .. } => Some(ClipEventFlag::DRAG_OUT),
|
|
ClipEvent::DragOver { .. } => Some(ClipEventFlag::DRAG_OVER),
|
|
ClipEvent::EnterFrame => Some(ClipEventFlag::ENTER_FRAME),
|
|
ClipEvent::Initialize => Some(ClipEventFlag::INITIALIZE),
|
|
ClipEvent::KeyDown => Some(ClipEventFlag::KEY_DOWN),
|
|
ClipEvent::KeyPress { .. } => Some(ClipEventFlag::KEY_PRESS),
|
|
ClipEvent::KeyUp => Some(ClipEventFlag::KEY_UP),
|
|
ClipEvent::Load => Some(ClipEventFlag::LOAD),
|
|
ClipEvent::MouseDown => Some(ClipEventFlag::MOUSE_DOWN),
|
|
ClipEvent::MouseMove => Some(ClipEventFlag::MOUSE_MOVE),
|
|
ClipEvent::MouseUp => Some(ClipEventFlag::MOUSE_UP),
|
|
ClipEvent::Press => Some(ClipEventFlag::PRESS),
|
|
ClipEvent::RollOut { .. } => Some(ClipEventFlag::ROLL_OUT),
|
|
ClipEvent::RollOver { .. } => Some(ClipEventFlag::ROLL_OVER),
|
|
ClipEvent::Release => Some(ClipEventFlag::RELEASE),
|
|
ClipEvent::ReleaseOutside => Some(ClipEventFlag::RELEASE_OUTSIDE),
|
|
ClipEvent::Unload => Some(ClipEventFlag::UNLOAD),
|
|
ClipEvent::MouseWheel { .. }
|
|
| ClipEvent::MouseMoveInside
|
|
| ClipEvent::MouseUpInside => None,
|
|
}
|
|
}
|
|
|
|
/// Indicates that the event should be propagated down to children.
|
|
pub const fn propagates(self) -> bool {
|
|
matches!(
|
|
self,
|
|
Self::MouseUp
|
|
| Self::MouseDown
|
|
| Self::MouseMove
|
|
| Self::KeyPress { .. }
|
|
| Self::KeyDown
|
|
| Self::KeyUp
|
|
)
|
|
}
|
|
|
|
/// Indicates whether this is an event type used by Buttons (i.e., on that can be used in an `on` handler in Flash).
|
|
pub const fn is_button_event(self) -> bool {
|
|
if let Some(flag) = self.flag() {
|
|
flag.intersects(Self::BUTTON_EVENT_FLAGS)
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
/// Indicates whether this is a keyboard event type (keyUp, keyDown, keyPress).
|
|
pub const fn is_key_event(self) -> bool {
|
|
matches!(self, Self::KeyDown | Self::KeyUp | Self::KeyPress { .. })
|
|
}
|
|
|
|
/// Returns the method name of the event handler for this event.
|
|
///
|
|
/// `ClipEvent::Data` returns `None` rather than `onData` because its behavior
|
|
/// differs from the other events: the method must fire before the SWF-defined
|
|
/// event handler, so we'll explicitly call `onData` in the appropriate places.
|
|
pub const fn method_name(self) -> Option<&'static str> {
|
|
match self {
|
|
ClipEvent::Construct => None,
|
|
ClipEvent::Data => None,
|
|
ClipEvent::DragOut { .. } => Some("onDragOut"),
|
|
ClipEvent::DragOver { .. } => Some("onDragOver"),
|
|
ClipEvent::EnterFrame => Some("onEnterFrame"),
|
|
ClipEvent::Initialize => None,
|
|
ClipEvent::KeyDown => Some("onKeyDown"),
|
|
ClipEvent::KeyPress { .. } => None,
|
|
ClipEvent::KeyUp => Some("onKeyUp"),
|
|
ClipEvent::Load => Some("onLoad"),
|
|
ClipEvent::MouseDown => Some("onMouseDown"),
|
|
ClipEvent::MouseMove => Some("onMouseMove"),
|
|
ClipEvent::MouseUp => Some("onMouseUp"),
|
|
ClipEvent::Press => Some("onPress"),
|
|
ClipEvent::RollOut { .. } => Some("onRollOut"),
|
|
ClipEvent::RollOver { .. } => Some("onRollOver"),
|
|
ClipEvent::Release => Some("onRelease"),
|
|
ClipEvent::ReleaseOutside => Some("onReleaseOutside"),
|
|
ClipEvent::Unload => Some("onUnload"),
|
|
ClipEvent::MouseWheel { .. }
|
|
| ClipEvent::MouseMoveInside
|
|
| ClipEvent::MouseUpInside => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Control inputs to a text field
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
|
|
pub enum TextControlCode {
|
|
// TODO: Add control codes for Ctrl+Arrows and Home/End keys
|
|
MoveLeft,
|
|
MoveRight,
|
|
SelectLeft,
|
|
SelectRight,
|
|
SelectAll,
|
|
Copy,
|
|
Paste,
|
|
Cut,
|
|
Backspace,
|
|
Enter,
|
|
Delete,
|
|
}
|
|
|
|
impl TextControlCode {
|
|
/// Indicates whether this is an event that edits the text content
|
|
pub fn is_edit_input(self) -> bool {
|
|
matches!(
|
|
self,
|
|
Self::Paste | Self::Cut | Self::Backspace | Self::Enter | Self::Delete
|
|
)
|
|
}
|
|
}
|
|
|
|
/// Flash virtual keycode.
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive)]
|
|
pub enum KeyCode {
|
|
Unknown = 0,
|
|
MouseLeft = 1,
|
|
MouseRight = 2,
|
|
MouseMiddle = 4,
|
|
Backspace = 8,
|
|
Tab = 9,
|
|
Return = 13,
|
|
Command = 15,
|
|
Shift = 16,
|
|
Control = 17,
|
|
Alt = 18,
|
|
Pause = 19,
|
|
CapsLock = 20,
|
|
Numpad = 21,
|
|
Escape = 27,
|
|
Space = 32,
|
|
PgUp = 33,
|
|
PgDown = 34,
|
|
End = 35,
|
|
Home = 36,
|
|
Left = 37,
|
|
Up = 38,
|
|
Right = 39,
|
|
Down = 40,
|
|
Insert = 45,
|
|
Delete = 46,
|
|
Key0 = 48,
|
|
Key1 = 49,
|
|
Key2 = 50,
|
|
Key3 = 51,
|
|
Key4 = 52,
|
|
Key5 = 53,
|
|
Key6 = 54,
|
|
Key7 = 55,
|
|
Key8 = 56,
|
|
Key9 = 57,
|
|
A = 65,
|
|
B = 66,
|
|
C = 67,
|
|
D = 68,
|
|
E = 69,
|
|
F = 70,
|
|
G = 71,
|
|
H = 72,
|
|
I = 73,
|
|
J = 74,
|
|
K = 75,
|
|
L = 76,
|
|
M = 77,
|
|
N = 78,
|
|
O = 79,
|
|
P = 80,
|
|
Q = 81,
|
|
R = 82,
|
|
S = 83,
|
|
T = 84,
|
|
U = 85,
|
|
V = 86,
|
|
W = 87,
|
|
X = 88,
|
|
Y = 89,
|
|
Z = 90,
|
|
Numpad0 = 96,
|
|
Numpad1 = 97,
|
|
Numpad2 = 98,
|
|
Numpad3 = 99,
|
|
Numpad4 = 100,
|
|
Numpad5 = 101,
|
|
Numpad6 = 102,
|
|
Numpad7 = 103,
|
|
Numpad8 = 104,
|
|
Numpad9 = 105,
|
|
Multiply = 106,
|
|
Plus = 107,
|
|
NumpadEnter = 108,
|
|
NumpadMinus = 109,
|
|
NumpadPeriod = 110,
|
|
NumpadSlash = 111,
|
|
F1 = 112,
|
|
F2 = 113,
|
|
F3 = 114,
|
|
F4 = 115,
|
|
F5 = 116,
|
|
F6 = 117,
|
|
F7 = 118,
|
|
F8 = 119,
|
|
F9 = 120,
|
|
F10 = 121,
|
|
F11 = 122,
|
|
F12 = 123,
|
|
F13 = 124,
|
|
F14 = 125,
|
|
F15 = 126,
|
|
ScrollLock = 145,
|
|
Semicolon = 186,
|
|
Equals = 187,
|
|
Comma = 188,
|
|
Minus = 189,
|
|
Period = 190,
|
|
Slash = 191,
|
|
Grave = 192,
|
|
LBracket = 219,
|
|
Backslash = 220,
|
|
RBracket = 221,
|
|
Apostrophe = 222,
|
|
}
|
|
|
|
impl KeyCode {
|
|
pub fn from_u8(n: u8) -> Option<Self> {
|
|
num_traits::FromPrimitive::from_u8(n)
|
|
}
|
|
}
|
|
|
|
/// Subset of `KeyCode` that contains only mouse buttons.
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
pub enum MouseButton {
|
|
Unknown = KeyCode::Unknown as isize,
|
|
Left = KeyCode::MouseLeft as isize,
|
|
Right = KeyCode::MouseRight as isize,
|
|
Middle = KeyCode::MouseMiddle as isize,
|
|
}
|
|
|
|
impl From<MouseButton> for KeyCode {
|
|
fn from(button: MouseButton) -> Self {
|
|
match button {
|
|
MouseButton::Unknown => Self::Unknown,
|
|
MouseButton::Left => Self::MouseLeft,
|
|
MouseButton::Right => Self::MouseRight,
|
|
MouseButton::Middle => Self::MouseMiddle,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Key codes for SWF4 keyPress button handlers. These are annoyingly different than
|
|
/// `Key.isDown` key codes.
|
|
/// TODO: After 18, these are mostly ASCII... should we just use u8? How are different
|
|
/// keyboard layouts/languages handled?
|
|
/// SWF19 pp. 198-199
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, FromPrimitive)]
|
|
pub enum ButtonKeyCode {
|
|
Unknown = 0,
|
|
Left = 1,
|
|
Right = 2,
|
|
Home = 3,
|
|
End = 4,
|
|
Insert = 5,
|
|
Delete = 6,
|
|
Backspace = 8,
|
|
Return = 13,
|
|
Up = 14,
|
|
Down = 15,
|
|
PgUp = 16,
|
|
PgDown = 17,
|
|
Tab = 18,
|
|
Escape = 19,
|
|
Space = 32,
|
|
Exclamation = 33,
|
|
DoubleQuote = 34,
|
|
NumberSign = 35,
|
|
Dollar = 36,
|
|
Percent = 37,
|
|
Ampersand = 38,
|
|
SingleQuote = 39,
|
|
LParen = 40,
|
|
RParen = 41,
|
|
Asterisk = 42,
|
|
Plus = 43,
|
|
Comma = 44,
|
|
Minus = 45,
|
|
Period = 46,
|
|
Slash = 47,
|
|
Zero = 48,
|
|
One = 49,
|
|
Two = 50,
|
|
Three = 51,
|
|
Four = 52,
|
|
Five = 53,
|
|
Six = 54,
|
|
Seven = 55,
|
|
Eight = 56,
|
|
Nine = 57,
|
|
Colon = 58,
|
|
Semicolon = 59,
|
|
LessThan = 60,
|
|
Equals = 61,
|
|
GreaterThan = 62,
|
|
Question = 63,
|
|
At = 64,
|
|
UppercaseA = 65,
|
|
UppercaseB = 66,
|
|
UppercaseC = 67,
|
|
UppercaseD = 68,
|
|
UppercaseE = 69,
|
|
UppercaseF = 70,
|
|
UppercaseG = 71,
|
|
UppercaseH = 72,
|
|
UppercaseI = 73,
|
|
UppercaseJ = 74,
|
|
UppercaseK = 75,
|
|
UppercaseL = 76,
|
|
UppercaseM = 77,
|
|
UppercaseN = 78,
|
|
UppercaseO = 79,
|
|
UppercaseP = 80,
|
|
UppercaseQ = 81,
|
|
UppercaseR = 82,
|
|
UppercaseS = 83,
|
|
UppercaseT = 84,
|
|
UppercaseU = 85,
|
|
UppercaseV = 86,
|
|
UppercaseW = 87,
|
|
UppercaseX = 88,
|
|
UppercaseY = 89,
|
|
UppercaseZ = 90,
|
|
LBracket = 91,
|
|
Backslash = 92,
|
|
RBracket = 93,
|
|
Caret = 94,
|
|
Underscore = 95,
|
|
Backquote = 96,
|
|
A = 97,
|
|
B = 98,
|
|
C = 99,
|
|
D = 100,
|
|
E = 101,
|
|
F = 102,
|
|
G = 103,
|
|
H = 104,
|
|
I = 105,
|
|
J = 106,
|
|
K = 107,
|
|
L = 108,
|
|
M = 109,
|
|
N = 110,
|
|
O = 111,
|
|
P = 112,
|
|
Q = 113,
|
|
R = 114,
|
|
S = 115,
|
|
T = 116,
|
|
U = 117,
|
|
V = 118,
|
|
W = 119,
|
|
X = 120,
|
|
Y = 121,
|
|
Z = 122,
|
|
LBrace = 123,
|
|
Pipe = 124,
|
|
RBrace = 125,
|
|
Tilde = 126,
|
|
}
|
|
|
|
impl ButtonKeyCode {
|
|
pub fn from_u8(n: u8) -> Option<Self> {
|
|
num_traits::FromPrimitive::from_u8(n)
|
|
}
|
|
}
|
|
|
|
pub fn key_code_to_button_key_code(key_code: KeyCode) -> Option<ButtonKeyCode> {
|
|
let out = match key_code {
|
|
KeyCode::Left => ButtonKeyCode::Left,
|
|
KeyCode::Right => ButtonKeyCode::Right,
|
|
KeyCode::Home => ButtonKeyCode::Home,
|
|
KeyCode::End => ButtonKeyCode::End,
|
|
KeyCode::Insert => ButtonKeyCode::Insert,
|
|
KeyCode::Delete => ButtonKeyCode::Delete,
|
|
KeyCode::Backspace => ButtonKeyCode::Backspace,
|
|
KeyCode::Return => ButtonKeyCode::Return,
|
|
KeyCode::Up => ButtonKeyCode::Up,
|
|
KeyCode::Down => ButtonKeyCode::Down,
|
|
KeyCode::PgUp => ButtonKeyCode::PgUp,
|
|
KeyCode::PgDown => ButtonKeyCode::PgDown,
|
|
KeyCode::Escape => ButtonKeyCode::Escape,
|
|
KeyCode::Tab => ButtonKeyCode::Tab,
|
|
_ => return None,
|
|
};
|
|
Some(out)
|
|
}
|