Added lua for UI and fixed compile_commands.json in Makefile
parent
62d3de16f7
commit
efec93031f
@ -0,0 +1,74 @@
|
||||
#ifndef UI_LUA_H
|
||||
#define UI_LUA_H
|
||||
|
||||
#include "ui.h"
|
||||
|
||||
// Registers the `ui` global table and input constants in the Lua state
|
||||
void ui_lua_register(lua_State* L);
|
||||
|
||||
// Loads a script into a sandboxed environment, stores the env ref in c->script_env
|
||||
VkResult ui_lua_load_container(
|
||||
lua_State* L,
|
||||
Container* c,
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
const char* path);
|
||||
|
||||
// Called by the engine input path. Each returns true if the container's script
|
||||
// defined the handler and it returned a truthy value (event consumed).
|
||||
bool ui_lua_dispatch_button(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element,
|
||||
float x,
|
||||
float y,
|
||||
int button,
|
||||
int action,
|
||||
int mods);
|
||||
|
||||
bool ui_lua_dispatch_cursor(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element,
|
||||
float x,
|
||||
float y);
|
||||
|
||||
bool ui_lua_dispatch_scroll(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element,
|
||||
double x,
|
||||
double y);
|
||||
|
||||
bool ui_lua_dispatch_key(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element,
|
||||
int key,
|
||||
int action,
|
||||
int mods);
|
||||
|
||||
bool ui_lua_dispatch_text(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element,
|
||||
unsigned int codepoint);
|
||||
|
||||
bool ui_lua_dispatch_deselect(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element);
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,204 @@
|
||||
-- Color picker logic, ported from the C callbacks that used to live in editor.c
|
||||
--
|
||||
-- Layer 0 elements:
|
||||
-- 0 background
|
||||
-- 1 sv square (HSV rect, hue in color channel 0)
|
||||
-- 2 hue bar (HSV rect)
|
||||
-- 3 sv select outline
|
||||
-- 4 sv select (HSV rect)
|
||||
-- 5 hex string input area
|
||||
-- 6 hue select bar
|
||||
-- 7-18 saved color slots
|
||||
|
||||
local state = {
|
||||
hsv = {0, 0, 0},
|
||||
rgb = {0, 0, 0, 1},
|
||||
saved = {},
|
||||
hex_string = "#000000FF",
|
||||
hex_len = 8,
|
||||
}
|
||||
for i = 1, 12 do state.saved[i] = {0, 0, 0, 0} end
|
||||
|
||||
local function update_hex_string()
|
||||
state.hex_string = string.format("#%02X%02X%02X%02X",
|
||||
math.floor(state.rgb[1]*255 + 0.5),
|
||||
math.floor(state.rgb[2]*255 + 0.5),
|
||||
math.floor(state.rgb[3]*255 + 0.5),
|
||||
math.floor(state.rgb[4]*255 + 0.5))
|
||||
state.hex_len = 8
|
||||
ui.set_string(0, 0, state.hex_string)
|
||||
end
|
||||
|
||||
local function sv_pick(s, v)
|
||||
s = math.max(0, math.min(1, s))
|
||||
v = math.max(0, math.min(1, v))
|
||||
state.hsv[2] = s
|
||||
state.hsv[3] = v
|
||||
|
||||
ui.set_pos(0, 4, s*130 - 2, 130 - v*130 - 2)
|
||||
for corner = 0, 3 do
|
||||
ui.set_corner_color(0, 4, corner, state.hsv[1], s, v, 1)
|
||||
end
|
||||
ui.set_pos(0, 3, s*130 - 3, 130 - v*130 - 3)
|
||||
|
||||
state.rgb[1], state.rgb[2], state.rgb[3] =
|
||||
ui.hsv_to_rgb(state.hsv[1], state.hsv[2], state.hsv[3])
|
||||
update_hex_string()
|
||||
end
|
||||
|
||||
local function hue_set(h)
|
||||
h = math.max(0, math.min(1, h))
|
||||
state.hsv[1] = h
|
||||
|
||||
-- sv square and sv select keep their per-corner s/v, only hue changes
|
||||
for _, drawable in ipairs({1, 4}) do
|
||||
for corner = 0, 3 do
|
||||
local _, s, v, a = ui.get_corner_color(0, drawable, corner)
|
||||
ui.set_corner_color(0, drawable, corner, h, s, v, a)
|
||||
end
|
||||
end
|
||||
ui.set_pos(0, 6, 134, 2 + h*130)
|
||||
|
||||
state.rgb[1], state.rgb[2], state.rgb[3] =
|
||||
ui.hsv_to_rgb(state.hsv[1], state.hsv[2], state.hsv[3])
|
||||
update_hex_string()
|
||||
end
|
||||
|
||||
local function hex_string_highlight(value)
|
||||
for corner = 0, 3 do
|
||||
local r, g, _, _ = ui.get_corner_color(0, 5, corner)
|
||||
ui.set_corner_color(0, 5, corner, r, g, value, value)
|
||||
end
|
||||
end
|
||||
|
||||
local function set_saved(index, color)
|
||||
state.saved[index] = {color[1], color[2], color[3], color[4]}
|
||||
local element = 6 + index
|
||||
for corner = 0, 3 do
|
||||
local _, _, _, a = ui.get_corner_color(0, element, corner)
|
||||
ui.set_corner_color(0, element, corner, color[1], color[2], color[3], a)
|
||||
end
|
||||
end
|
||||
|
||||
function on_button(layer, element, x, y, button, action, mods)
|
||||
if element == 1 then -- sv square
|
||||
if action == PRESS and button == MOUSE_LEFT then
|
||||
ui.focus(layer, element)
|
||||
sv_pick(x, 1 - y)
|
||||
elseif action == RELEASE and button == MOUSE_LEFT then
|
||||
ui.blur()
|
||||
end
|
||||
return true
|
||||
|
||||
elseif element == 2 then -- hue bar
|
||||
if action == PRESS and button == MOUSE_LEFT then
|
||||
ui.focus(layer, element)
|
||||
hue_set(y)
|
||||
elseif action == RELEASE and button == MOUSE_LEFT then
|
||||
ui.blur()
|
||||
end
|
||||
return true
|
||||
|
||||
elseif element == 5 then -- hex string input
|
||||
if action == PRESS and button == MOUSE_LEFT then
|
||||
ui.focus(layer, element)
|
||||
hex_string_highlight(1)
|
||||
end
|
||||
return true
|
||||
|
||||
elseif element >= 7 and element <= 18 then -- saved color slots
|
||||
local index = element - 6
|
||||
if action == PRESS then
|
||||
if button == MOUSE_LEFT then
|
||||
local saved = state.saved[index]
|
||||
state.rgb = {saved[1], saved[2], saved[3], saved[4]}
|
||||
state.hsv[1], state.hsv[2], state.hsv[3] =
|
||||
ui.rgb_to_hsv(state.rgb[1], state.rgb[2], state.rgb[3])
|
||||
hue_set(state.hsv[1])
|
||||
sv_pick(state.hsv[2], state.hsv[3])
|
||||
elseif button == MOUSE_RIGHT then
|
||||
set_saved(index, state.rgb)
|
||||
elseif button == MOUSE_MIDDLE then
|
||||
set_saved(index, {0, 0, 0, 0})
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function on_cursor(layer, element, x, y)
|
||||
if ui.is_focused(0, 1) then -- dragging the sv square
|
||||
sv_pick(x, 1 - y)
|
||||
return true
|
||||
elseif ui.is_focused(0, 2) then -- dragging the hue bar
|
||||
hue_set(y)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function on_scroll(layer, element, x, y)
|
||||
if element == 2 then -- hue bar
|
||||
hue_set(state.hsv[1] + y*0.01)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function on_key(layer, element, key, action, mods)
|
||||
if element ~= 5 then return false end
|
||||
|
||||
if action == PRESS then
|
||||
if key == KEY_ESCAPE then
|
||||
state.rgb[1], state.rgb[2], state.rgb[3] =
|
||||
ui.hsv_to_rgb(state.hsv[1], state.hsv[2], state.hsv[3])
|
||||
update_hex_string()
|
||||
ui.blur()
|
||||
elseif key == KEY_ENTER then
|
||||
local s = state.hex_string
|
||||
state.rgb[1] = (tonumber(s:sub(2, 3), 16) or 0) / 255
|
||||
state.rgb[2] = (tonumber(s:sub(4, 5), 16) or 0) / 255
|
||||
state.rgb[3] = (tonumber(s:sub(6, 7), 16) or 0) / 255
|
||||
state.rgb[4] = (tonumber(s:sub(8, 9), 16) or 0) / 255
|
||||
state.hsv[1], state.hsv[2], state.hsv[3] =
|
||||
ui.rgb_to_hsv(state.rgb[1], state.rgb[2], state.rgb[3])
|
||||
hue_set(state.hsv[1])
|
||||
sv_pick(state.hsv[2], state.hsv[3])
|
||||
ui.blur()
|
||||
elseif key == KEY_BACKSPACE then
|
||||
if state.hex_len > 0 then
|
||||
state.hex_string = state.hex_string:sub(1, state.hex_len)
|
||||
state.hex_len = state.hex_len - 1
|
||||
ui.set_string(0, 0, state.hex_string)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- consume all keys while the hex input is focused
|
||||
return true
|
||||
end
|
||||
|
||||
function on_text(layer, element, codepoint)
|
||||
if element ~= 5 then return false end
|
||||
|
||||
if codepoint < 128 then
|
||||
local ch = string.char(codepoint):upper()
|
||||
if ch:match("[0-9A-F]") and state.hex_len < 8 then
|
||||
state.hex_string = state.hex_string .. ch
|
||||
state.hex_len = state.hex_len + 1
|
||||
ui.set_string(0, 0, state.hex_string)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function on_deselect(layer, element)
|
||||
if element == 5 then
|
||||
hex_string_highlight(0)
|
||||
end
|
||||
end
|
||||
|
||||
-- runs once at container load
|
||||
ui.set_string(0, 0, state.hex_string)
|
||||
@ -0,0 +1,390 @@
|
||||
#include "ui_lua.h"
|
||||
#include "gpu.h"
|
||||
#include "hsv.h"
|
||||
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define UI_LUA_CONTAINER "ui_container"
|
||||
#define UI_LUA_CONTEXT "ui_context"
|
||||
#define UI_LUA_GPU "ui_gpu"
|
||||
|
||||
static void* registry_pointer(lua_State* L, const char* key) {
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, key);
|
||||
void* ptr = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void set_registry_pointers(lua_State* L, Container* c, UIContext* ui, RenderContext* gpu) {
|
||||
lua_pushlightuserdata(L, c);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, UI_LUA_CONTAINER);
|
||||
lua_pushlightuserdata(L, ui);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, UI_LUA_CONTEXT);
|
||||
lua_pushlightuserdata(L, gpu);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, UI_LUA_GPU);
|
||||
}
|
||||
|
||||
static GPUDrawable* check_drawable(lua_State* L, Container* c, uint32_t layer, uint32_t drawable) {
|
||||
if(layer >= c->layer_count) {
|
||||
luaL_error(L, "layer %d out of range", layer);
|
||||
}
|
||||
if(drawable >= c->layers[layer].data.num_drawables) {
|
||||
luaL_error(L, "drawable %d out of range in layer %d", drawable, layer);
|
||||
}
|
||||
return &c->layers[layer].drawables_buffer[drawable];
|
||||
}
|
||||
|
||||
// ui.set_pos(layer, drawable, x, y)
|
||||
static int lua_ui_set_pos(lua_State* L) {
|
||||
Container* container = registry_pointer(L, UI_LUA_CONTAINER);
|
||||
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
||||
uint32_t layer = luaL_checkinteger(L, 1);
|
||||
uint32_t drawable = luaL_checkinteger(L, 2);
|
||||
GPUDrawable* d = check_drawable(L, container, layer, drawable);
|
||||
|
||||
d->pos[0] = luaL_checknumber(L, 3);
|
||||
d->pos[1] = luaL_checknumber(L, 4);
|
||||
|
||||
add_transfers(
|
||||
&d->pos,
|
||||
container->layers[layer].drawables,
|
||||
drawable*sizeof(GPUDrawable) + offsetof(GPUDrawable, pos),
|
||||
sizeof(vec2),
|
||||
gpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ui.set_corner_color(layer, drawable, corner, r, g, b, a)
|
||||
static int lua_ui_set_corner_color(lua_State* L) {
|
||||
Container* container = registry_pointer(L, UI_LUA_CONTAINER);
|
||||
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
||||
uint32_t layer = luaL_checkinteger(L, 1);
|
||||
uint32_t drawable = luaL_checkinteger(L, 2);
|
||||
uint32_t corner = luaL_checkinteger(L, 3);
|
||||
if(corner > 3) {
|
||||
return luaL_error(L, "corner %d out of range", corner);
|
||||
}
|
||||
GPUDrawable* d = check_drawable(L, container, layer, drawable);
|
||||
|
||||
d->color[corner][0] = luaL_checknumber(L, 4);
|
||||
d->color[corner][1] = luaL_checknumber(L, 5);
|
||||
d->color[corner][2] = luaL_checknumber(L, 6);
|
||||
d->color[corner][3] = luaL_checknumber(L, 7);
|
||||
|
||||
add_transfers(
|
||||
&d->color[corner],
|
||||
container->layers[layer].drawables,
|
||||
drawable*sizeof(GPUDrawable) + offsetof(GPUDrawable, color) + corner*sizeof(vec4),
|
||||
sizeof(vec4),
|
||||
gpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ui.get_corner_color(layer, drawable, corner) -> r, g, b, a
|
||||
static int lua_ui_get_corner_color(lua_State* L) {
|
||||
Container* container = registry_pointer(L, UI_LUA_CONTAINER);
|
||||
uint32_t layer = luaL_checkinteger(L, 1);
|
||||
uint32_t drawable = luaL_checkinteger(L, 2);
|
||||
uint32_t corner = luaL_checkinteger(L, 3);
|
||||
if(corner > 3) {
|
||||
return luaL_error(L, "corner %d out of range", corner);
|
||||
}
|
||||
GPUDrawable* d = check_drawable(L, container, layer, drawable);
|
||||
|
||||
lua_pushnumber(L, d->color[corner][0]);
|
||||
lua_pushnumber(L, d->color[corner][1]);
|
||||
lua_pushnumber(L, d->color[corner][2]);
|
||||
lua_pushnumber(L, d->color[corner][3]);
|
||||
return 4;
|
||||
}
|
||||
|
||||
// ui.set_string(layer, string_index, text)
|
||||
static int lua_ui_set_string(lua_State* L) {
|
||||
Container* container = registry_pointer(L, UI_LUA_CONTAINER);
|
||||
UIContext* ui = registry_pointer(L, UI_LUA_CONTEXT);
|
||||
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
||||
uint32_t layer = luaL_checkinteger(L, 1);
|
||||
uint32_t string_index = luaL_checkinteger(L, 2);
|
||||
const char* text = luaL_checkstring(L, 3);
|
||||
if(layer >= container->layer_count) {
|
||||
return luaL_error(L, "layer %d out of range", layer);
|
||||
}
|
||||
if(string_index >= container->layers[layer].data.max_strings) {
|
||||
return luaL_error(L, "string %d out of range in layer %d", string_index, layer);
|
||||
}
|
||||
|
||||
update_ui_string(text, container, layer, string_index, ui, gpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ui.focus(layer, element)
|
||||
static int lua_ui_focus(lua_State* L) {
|
||||
Container* container = registry_pointer(L, UI_LUA_CONTAINER);
|
||||
UIContext* ui = registry_pointer(L, UI_LUA_CONTEXT);
|
||||
uint32_t layer = luaL_checkinteger(L, 1);
|
||||
uint32_t element = luaL_checkinteger(L, 2);
|
||||
|
||||
set_active_element(container->id, layer, element, ui);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ui.blur()
|
||||
static int lua_ui_blur(lua_State* L) {
|
||||
UIContext* ui = registry_pointer(L, UI_LUA_CONTEXT);
|
||||
RenderContext* gpu = registry_pointer(L, UI_LUA_GPU);
|
||||
|
||||
clear_active_element(ui, gpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ui.is_focused(layer, element) -> bool
|
||||
static int lua_ui_is_focused(lua_State* L) {
|
||||
Container* container = registry_pointer(L, UI_LUA_CONTAINER);
|
||||
UIContext* ui = registry_pointer(L, UI_LUA_CONTEXT);
|
||||
uint32_t layer = luaL_checkinteger(L, 1);
|
||||
uint32_t element = luaL_checkinteger(L, 2);
|
||||
|
||||
lua_pushboolean(L,
|
||||
ui->active_container == container
|
||||
&& ui->active_layer == layer
|
||||
&& ui->active_element == element);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ui.hsv_to_rgb(h, s, v) -> r, g, b
|
||||
static int lua_ui_hsv_to_rgb(lua_State* L) {
|
||||
double hsv[3] = {
|
||||
luaL_checknumber(L, 1),
|
||||
luaL_checknumber(L, 2),
|
||||
luaL_checknumber(L, 3),
|
||||
};
|
||||
vec3 rgb;
|
||||
hsv_to_rgb(hsv, rgb);
|
||||
lua_pushnumber(L, rgb[0]);
|
||||
lua_pushnumber(L, rgb[1]);
|
||||
lua_pushnumber(L, rgb[2]);
|
||||
return 3;
|
||||
}
|
||||
|
||||
// ui.rgb_to_hsv(r, g, b) -> h, s, v
|
||||
static int lua_ui_rgb_to_hsv(lua_State* L) {
|
||||
vec3 rgb = {
|
||||
luaL_checknumber(L, 1),
|
||||
luaL_checknumber(L, 2),
|
||||
luaL_checknumber(L, 3),
|
||||
};
|
||||
double hsv[3];
|
||||
rgb_to_hsv(rgb, hsv);
|
||||
lua_pushnumber(L, hsv[0]);
|
||||
lua_pushnumber(L, hsv[1]);
|
||||
lua_pushnumber(L, hsv[2]);
|
||||
return 3;
|
||||
}
|
||||
|
||||
void ui_lua_register(lua_State* L) {
|
||||
static const luaL_Reg ui_funcs[] = {
|
||||
{"set_pos", lua_ui_set_pos},
|
||||
{"set_corner_color", lua_ui_set_corner_color},
|
||||
{"get_corner_color", lua_ui_get_corner_color},
|
||||
{"set_string", lua_ui_set_string},
|
||||
{"focus", lua_ui_focus},
|
||||
{"blur", lua_ui_blur},
|
||||
{"is_focused", lua_ui_is_focused},
|
||||
{"hsv_to_rgb", lua_ui_hsv_to_rgb},
|
||||
{"rgb_to_hsv", lua_ui_rgb_to_hsv},
|
||||
{NULL, NULL},
|
||||
};
|
||||
luaL_newlib(L, ui_funcs);
|
||||
lua_setglobal(L, "ui");
|
||||
|
||||
lua_pushinteger(L, GLFW_PRESS); lua_setglobal(L, "PRESS");
|
||||
lua_pushinteger(L, GLFW_RELEASE); lua_setglobal(L, "RELEASE");
|
||||
lua_pushinteger(L, GLFW_MOUSE_BUTTON_LEFT); lua_setglobal(L, "MOUSE_LEFT");
|
||||
lua_pushinteger(L, GLFW_MOUSE_BUTTON_RIGHT); lua_setglobal(L, "MOUSE_RIGHT");
|
||||
lua_pushinteger(L, GLFW_MOUSE_BUTTON_MIDDLE); lua_setglobal(L, "MOUSE_MIDDLE");
|
||||
lua_pushinteger(L, GLFW_KEY_ESCAPE); lua_setglobal(L, "KEY_ESCAPE");
|
||||
lua_pushinteger(L, GLFW_KEY_ENTER); lua_setglobal(L, "KEY_ENTER");
|
||||
lua_pushinteger(L, GLFW_KEY_BACKSPACE); lua_setglobal(L, "KEY_BACKSPACE");
|
||||
}
|
||||
|
||||
VkResult ui_lua_load_container(
|
||||
lua_State* L,
|
||||
Container* c,
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
const char* path) {
|
||||
|
||||
if(luaL_loadfile(L, path) != LUA_OK) {
|
||||
fprintf(stderr, "ui_lua_load_container: %s\n", lua_tostring(L, -1));
|
||||
lua_pop(L, 1);
|
||||
return VK_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
// Sandboxed environment table: env = setmetatable({}, {__index = _G}),
|
||||
// set as the chunk's _ENV so script globals land in env, not _G
|
||||
lua_newtable(L);
|
||||
lua_newtable(L);
|
||||
lua_getglobal(L, "_G");
|
||||
lua_setfield(L, -2, "__index");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setupvalue(L, -3, 1);
|
||||
|
||||
// Ref the env first so the chunk (now at the top) can be called
|
||||
c->script_env = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
|
||||
// Top-level script code may call ui.* during execution
|
||||
set_registry_pointers(L, c, ui, gpu);
|
||||
|
||||
if(lua_pcall(L, 0, 0, 0) != LUA_OK) {
|
||||
fprintf(stderr, "ui_lua_load_container: %s\n", lua_tostring(L, -1));
|
||||
lua_pop(L, 1);
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, c->script_env);
|
||||
c->script_env = 0;
|
||||
return VK_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
// Pushes the container's handler onto the stack, ready for args.
|
||||
// Returns false if the container has no script or no such handler.
|
||||
static bool ui_lua_begin_dispatch(
|
||||
lua_State* L,
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
const char* handler) {
|
||||
if(c == NULL || c->script_env == 0) return false;
|
||||
|
||||
set_registry_pointers(L, c, ui, gpu);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, c->script_env);
|
||||
lua_getfield(L, -1, handler);
|
||||
lua_remove(L, -2);
|
||||
if(!lua_isfunction(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calls the handler with nargs already on the stack, returns its boolean result
|
||||
static bool ui_lua_finish_dispatch(lua_State* L, const char* handler, int nargs) {
|
||||
if(lua_pcall(L, nargs, 1, 0) != LUA_OK) {
|
||||
fprintf(stderr, "%s: %s\n", handler, lua_tostring(L, -1));
|
||||
lua_pop(L, 1);
|
||||
return false;
|
||||
}
|
||||
bool consumed = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return consumed;
|
||||
}
|
||||
|
||||
bool ui_lua_dispatch_button(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element,
|
||||
float x,
|
||||
float y,
|
||||
int button,
|
||||
int action,
|
||||
int mods) {
|
||||
lua_State* L = ui->lua;
|
||||
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_button")) return false;
|
||||
lua_pushinteger(L, layer);
|
||||
lua_pushinteger(L, element);
|
||||
lua_pushnumber(L, x);
|
||||
lua_pushnumber(L, y);
|
||||
lua_pushinteger(L, button);
|
||||
lua_pushinteger(L, action);
|
||||
lua_pushinteger(L, mods);
|
||||
return ui_lua_finish_dispatch(L, "on_button", 7);
|
||||
}
|
||||
|
||||
bool ui_lua_dispatch_cursor(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element,
|
||||
float x,
|
||||
float y) {
|
||||
lua_State* L = ui->lua;
|
||||
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_cursor")) return false;
|
||||
lua_pushinteger(L, layer);
|
||||
lua_pushinteger(L, element);
|
||||
lua_pushnumber(L, x);
|
||||
lua_pushnumber(L, y);
|
||||
return ui_lua_finish_dispatch(L, "on_cursor", 4);
|
||||
}
|
||||
|
||||
bool ui_lua_dispatch_scroll(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element,
|
||||
double x,
|
||||
double y) {
|
||||
lua_State* L = ui->lua;
|
||||
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_scroll")) return false;
|
||||
lua_pushinteger(L, layer);
|
||||
lua_pushinteger(L, element);
|
||||
lua_pushnumber(L, x);
|
||||
lua_pushnumber(L, y);
|
||||
return ui_lua_finish_dispatch(L, "on_scroll", 4);
|
||||
}
|
||||
|
||||
bool ui_lua_dispatch_key(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element,
|
||||
int key,
|
||||
int action,
|
||||
int mods) {
|
||||
lua_State* L = ui->lua;
|
||||
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_key")) return false;
|
||||
lua_pushinteger(L, layer);
|
||||
lua_pushinteger(L, element);
|
||||
lua_pushinteger(L, key);
|
||||
lua_pushinteger(L, action);
|
||||
lua_pushinteger(L, mods);
|
||||
return ui_lua_finish_dispatch(L, "on_key", 5);
|
||||
}
|
||||
|
||||
bool ui_lua_dispatch_text(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element,
|
||||
unsigned int codepoint) {
|
||||
lua_State* L = ui->lua;
|
||||
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_text")) return false;
|
||||
lua_pushinteger(L, layer);
|
||||
lua_pushinteger(L, element);
|
||||
lua_pushinteger(L, codepoint);
|
||||
return ui_lua_finish_dispatch(L, "on_text", 3);
|
||||
}
|
||||
|
||||
bool ui_lua_dispatch_deselect(
|
||||
UIContext* ui,
|
||||
RenderContext* gpu,
|
||||
Container* c,
|
||||
uint32_t layer,
|
||||
uint32_t element) {
|
||||
lua_State* L = ui->lua;
|
||||
if(!ui_lua_begin_dispatch(L, ui, gpu, c, "on_deselect")) return false;
|
||||
lua_pushinteger(L, layer);
|
||||
lua_pushinteger(L, element);
|
||||
return ui_lua_finish_dispatch(L, "on_deselect", 2);
|
||||
}
|
||||
Loading…
Reference in New Issue