[lua] Add onkeydown/onkeyup events to canvas widget

This commit is contained in:
David Capello 2023-01-04 14:40:10 -03:00
parent 420278d5a4
commit 17825921e0
6 changed files with 290 additions and 3 deletions

View File

@ -182,6 +182,7 @@ if(ENABLE_SCRIPTING)
script/image_iterator_class.cpp
script/image_spec_class.cpp
script/images_class.cpp
script/keys.cpp
script/layer_class.cpp
script/layers_class.cpp
script/luacpp.cpp

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (c) 2022 Igara Studio S.A.
// Copyright (c) 2022-2023 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -29,6 +29,15 @@ ui::WidgetType Canvas::Type()
return type;
}
// static
bool Canvas::s_stopKeyEventPropagation = false;
// static
void Canvas::stopKeyEventPropagation()
{
s_stopKeyEventPropagation = true;
}
Canvas::Canvas() : ui::Widget(Type())
{
}
@ -64,6 +73,26 @@ bool Canvas::onProcessMessage(ui::Message* msg)
{
switch (msg->type()) {
case ui::kKeyDownMessage:
if (hasFocus()) {
s_stopKeyEventPropagation = false;
auto keyMsg = static_cast<ui::KeyMessage*>(msg);
KeyDown(keyMsg);
if (s_stopKeyEventPropagation)
return true;
}
break;
case ui::kKeyUpMessage:
if (hasFocus()) {
s_stopKeyEventPropagation = false;
auto keyMsg = static_cast<ui::KeyMessage*>(msg);
KeyUp(keyMsg);
if (s_stopKeyEventPropagation)
return true;
}
break;
case ui::kSetCursorMessage:
ui::set_mouse_cursor(m_cursorType);
return true;
@ -78,6 +107,9 @@ bool Canvas::onProcessMessage(ui::Message* msg)
if (!hasCapture())
captureMouse();
if (isFocusStop() && !hasFocus())
requestFocus();
auto mouseMsg = static_cast<ui::MouseMessage*>(msg);
MouseDown(mouseMsg);
break;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (c) 2022 Igara Studio S.A.
// Copyright (c) 2022-2023 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -36,13 +36,19 @@ public:
}
obs::signal<void(GraphicsContext&)> Paint;
obs::signal<void(ui::KeyMessage*)> KeyDown;
obs::signal<void(ui::KeyMessage*)> KeyUp;
obs::signal<void(ui::MouseMessage*)> MouseMove;
obs::signal<void(ui::MouseMessage*)> MouseDown;
obs::signal<void(ui::MouseMessage*)> MouseUp;
obs::signal<void(ui::MouseMessage*)> Wheel;
obs::signal<void(ui::TouchMessage*)> TouchMagnify;
static void stopKeyEventPropagation();
private:
static bool s_stopKeyEventPropagation;
void onInitTheme(ui::InitThemeEvent& ev) override;
bool onProcessMessage(ui::Message* msg) override;
void onResize(ui::ResizeEvent& ev) override;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
@ -16,6 +16,7 @@
#include "app/script/canvas_widget.h"
#include "app/script/engine.h"
#include "app/script/graphics_context.h"
#include "app/script/keys.h"
#include "app/script/luacpp.h"
#include "app/ui/color_button.h"
#include "app/ui/color_shades.h"
@ -914,6 +915,7 @@ int Dialog_canvas(lua_State* L)
widget->setSizeHint(sz);
bool handleKeyEvents = false;
if (lua_istable(L, 2)) {
int type = lua_getfield(L, 2, "onpaint");
if (type == LUA_TFUNCTION) {
@ -927,6 +929,47 @@ int Dialog_canvas(lua_State* L)
lua_pop(L, 1);
// Auxiliary callbacks used in Canvas events
auto keyCallback =
[](lua_State* L, ui::KeyMessage* msg) {
ASSERT(msg->recipient());
if (!msg->recipient())
return;
// Key modifiers
lua_pushboolean(L, msg->modifiers() & ui::kKeyAltModifier);
lua_setfield(L, -2, "altKey");
lua_pushboolean(L, msg->modifiers() & (ui::kKeyCmdModifier | ui::kKeyWinModifier));
lua_setfield(L, -2, "metaKey");
lua_pushboolean(L, msg->modifiers() & ui::kKeyCtrlModifier);
lua_setfield(L, -2, "ctrlKey");
lua_pushboolean(L, msg->modifiers() & ui::kKeyShiftModifier);
lua_setfield(L, -2, "shiftKey");
lua_pushboolean(L, msg->modifiers() & ui::kKeySpaceModifier);
lua_setfield(L, -2, "spaceKey");
// KeyMessage specifics
lua_pushinteger(L, msg->repeat());
lua_setfield(L, -2, "repeat");
// TODO improve this (create an Event metatable)
lua_pushcfunction(L, [](lua_State*) -> int {
Canvas::stopKeyEventPropagation();
return 0;
});
lua_setfield(L, -2, "stopPropagation");
std::wstring keyString(1, (wchar_t)msg->unicodeChar());
lua_pushstring(L, base::to_utf8(keyString).c_str());
lua_setfield(L, -2, "key");
lua_pushstring(L, vkcode_to_code(msg->scancode()));
lua_setfield(L, -2, "code");
};
auto mouseCallback =
[](lua_State* L, ui::MouseMessage* msg) {
ASSERT(msg->recipient());
@ -970,6 +1013,20 @@ int Dialog_canvas(lua_State* L)
lua_setfield(L, -2, "magnification");
};
type = lua_getfield(L, 2, "onkeydown");
if (type == LUA_TFUNCTION) {
Dialog_connect_signal(L, 1, widget->KeyDown, keyCallback);
handleKeyEvents = true;
}
lua_pop(L, 1);
type = lua_getfield(L, 2, "onkeyup");
if (type == LUA_TFUNCTION) {
Dialog_connect_signal(L, 1, widget->KeyUp, keyCallback);
handleKeyEvents = true;
}
lua_pop(L, 1);
type = lua_getfield(L, 2, "onmousemove");
if (type == LUA_TFUNCTION) {
Dialog_connect_signal(L, 1, widget->MouseMove, mouseCallback);
@ -1000,6 +1057,11 @@ int Dialog_canvas(lua_State* L)
}
lua_pop(L, 1);
}
// If this canvas handle keydown/up events, we set it as a focus
// stop.
if (handleKeyEvents)
widget->setFocusStop(true);
}
return Dialog_add_widget(L, widget);

165
src/app/script/keys.cpp Normal file
View File

@ -0,0 +1,165 @@
// Aseprite
// Copyright (C) 2023 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ui/keys.h"
namespace app {
namespace script {
// Same order that os::KeyScancode
// Based on code values of the KeyboardEvent on web code:
// https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values
static const char* vkcode_to_code_table[] = {
"Unidentified",
"KeyA",
"KeyB",
"KeyC",
"KeyD",
"KeyE",
"KeyF",
"KeyG",
"KeyH",
"KeyI",
"KeyJ",
"KeyK",
"KeyL",
"KeyM",
"KeyN",
"KeyO",
"KeyP",
"KeyQ",
"KeyR",
"KeyS",
"KeyT",
"KeyU",
"KeyV",
"KeyW",
"KeyX",
"KeyY",
"KeyZ",
"Digit0",
"Digit1",
"Digit2",
"Digit3",
"Digit4",
"Digit5",
"Digit6",
"Digit7",
"Digit8",
"Digit9",
"Numpad0",
"Numpad1",
"Numpad2",
"Numpad3",
"Numpad4",
"Numpad5",
"Numpad6",
"Numpad7",
"Numpad8",
"Numpad9",
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
"Escape",
"Backquote",
"Minus",
"Equal",
"Backspace",
"Tab",
"BracketLeft",
"BracketRight",
"Enter",
"Semicolon",
"Quote",
"Backslash",
nullptr, // kKeyBackslash2,
"Comma",
"Period",
"Slash",
"Space",
"Insert",
"Delete",
"Home",
"End",
"PageUp",
"PageDown",
"ArrowLeft",
"ArrowRight",
"ArrowUp",
"ArrowDown",
"NumpadDivide",
"NumpadMultiply",
"NumpadSubtract",
"NumpadAdd",
"NumpadComma",
"NumpadEnter",
"PrintScreen",
"Pause",
nullptr, // kKeyAbntC1
"IntlYen",
"KanaMode",
"Convert",
"NonConvert",
nullptr, // kKeyAt
nullptr, // kKeyCircumflex
nullptr, // kKeyColon2
nullptr, // kKeyKanji
"NumpadEqual", // kKeyEqualsPad
"Backquote",
nullptr, // kKeySemicolon
nullptr, // kKeyUnknown1
nullptr, // kKeyUnknown2
nullptr, // kKeyUnknown3
nullptr, // kKeyUnknown4
nullptr, // kKeyUnknown5
nullptr, // kKeyUnknown6
nullptr, // kKeyUnknown7
nullptr, // kKeyUnknown8
"ShiftLeft",
"ShiftRight",
"ControlLeft",
"ControlRight",
"AltLeft",
"AltRight"
"MetaLeft",
"MetaRight",
"ContextMenu",
"MetaLeft", // kKeyCommand
"ScrollLock",
"NumLock",
"CapsLock",
};
static int vkcode_to_code_table_size =
sizeof(vkcode_to_code_table) / sizeof(vkcode_to_code_table[0]);
const char* vkcode_to_code(const ui::KeyScancode vkcode)
{
if (vkcode >= 0 &&
vkcode < vkcode_to_code_table_size &&
vkcode_to_code_table[vkcode]) {
return vkcode_to_code_table[vkcode];
}
else {
return vkcode_to_code_table[0];
}
}
} // namespace script
} // namespace app

21
src/app/script/keys.h Normal file
View File

@ -0,0 +1,21 @@
// Aseprite
// Copyright (C) 2023 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_SCRIPT_KEYS_H_INCLUDED
#define APP_SCRIPT_KEYS_H_INCLUDED
#pragma once
#include "ui/keys.h"
namespace app {
namespace script {
const char* vkcode_to_code(const ui::KeyScancode vkcode);
} // namespace script
} // namespace app
#endif