From b8a8da1ac4d1969d11a2c487199db74f64c50e46 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Tue, 9 May 2023 20:29:07 -0400 Subject: [PATCH] #226 graphics core changes for mouse events --- graphics/core.lua | 97 ++------------------------ graphics/element.lua | 44 +++++++----- graphics/events.lua | 152 +++++++++++++++++++++++++++++++++++++++++ graphics/graphics.lua | 30 -------- scada-common/types.lua | 4 ++ 5 files changed, 188 insertions(+), 139 deletions(-) create mode 100644 graphics/events.lua delete mode 100644 graphics/graphics.lua diff --git a/graphics/core.lua b/graphics/core.lua index 1ef315f..58b6b8c 100644 --- a/graphics/core.lua +++ b/graphics/core.lua @@ -2,101 +2,12 @@ -- Graphics Core Types, Checks, and Constructors -- +local events = require("graphics.events") +local flasher = require("graphics.flasher") + local core = {} --- Core Events - -local events = {} - ----@enum click_button -events.click_button = { - VIRTUAL = 0, - LEFT_BUTTON = 1, - RIGHT_BUTTON = 2, - MID_BUTTON = 3 -} - ----@enum click_type -events.click_type = { - TAP = 0, - DOWN = 1, - DRAG = 2, - UP = 3 -} - ----@class mouse_interaction ----@field monitor string ----@field button click_button ----@field type click_type ----@field x integer ----@field y integer - --- create a new monitor touch mouse interaction event ----@nodiscard ----@param monitor string ----@param x integer ----@param y integer ----@return mouse_interaction -function events.touch(monitor, x, y) - return { - monitor = monitor, - button = events.click_button.LEFT_BUTTON, - type = events.click_type.TAP, - x = x, - y = y - } -end - --- create a new mouse click mouse interaction event ----@nodiscard ----@param button click_button ----@param x integer ----@param y integer ----@return mouse_interaction -function events.click(button, x, y) - return { - monitor = "terminal", - button = button, - type = events.click_type.UP, - x = x, - y = y - } -end - --- create a new transposed mouse interaction event using the event's monitor/button fields ----@nodiscard ----@param event mouse_interaction ----@param new_x integer ----@param new_y integer ----@return mouse_interaction -function events.mouse_transposed(event, new_x, new_y) - return { - monitor = event.monitor, - button = event.button, - type = event.type, - x = new_x, - y = new_y - } -end - --- create a new generic mouse interaction event ----@nodiscard ----@param monitor string ----@param button click_button ----@param type click_type ----@param x integer ----@param y integer ----@return mouse_interaction -function events.mouse_generic(monitor, button, type, x, y) - return { - monitor = monitor, - button = button, - type = type, - x = x, - y = y - } -end - +core.flasher = flasher core.events = events -- Core Types diff --git a/graphics/element.lua b/graphics/element.lua index 13baec3..2f8dabd 100644 --- a/graphics/element.lua +++ b/graphics/element.lua @@ -59,10 +59,10 @@ function element.new(args) id = -1, elem_type = debug.getinfo(2).name, define_completed = false, - p_window = nil, ---@type table - position = { x = 1, y = 1 }, + p_window = nil, ---@type table + position = { x = 1, y = 1 }, ---@type coordinate_2d child_offset = { x = 0, y = 0 }, - bounds = { x1 = 1, y1 = 1, x2 = 1, y2 = 1}, + bounds = { x1 = 1, y1 = 1, x2 = 1, y2 = 1 }, ---@class element_bounds next_y = 1, children = {}, mt = {} @@ -77,11 +77,22 @@ function element.new(args) frame = core.gframe(1, 1, 1, 1) } + local name_brief = "graphics.element{" .. self.elem_type .. "}: " + -- element as string function self.mt.__tostring() return "graphics.element{" .. self.elem_type .. "} @ " .. tostring(self) end + -- check if a coordinate is within the bounds of this element + ---@param x integer + ---@param y integer + local function _in_bounds(x, y) + local in_x = x >= self.bounds.x1 and x <= self.bounds.x2 + local in_y = y >= self.bounds.y1 and y <= self.bounds.y2 + return in_x and in_y + end + ---@class graphics_element local public = {} @@ -138,10 +149,10 @@ function element.new(args) end -- check frame - assert(f.x >= 1, "graphics.element{" .. self.elem_type .. "}: frame x not >= 1") - assert(f.y >= 1, "graphics.element{" .. self.elem_type .. "}: frame y not >= 1") - assert(f.w >= 1, "graphics.element{" .. self.elem_type .. "}: frame width not >= 1") - assert(f.h >= 1, "graphics.element{" .. self.elem_type .. "}: frame height not >= 1") + assert(f.x >= 1, name_brief .. "frame x not >= 1") + assert(f.y >= 1, name_brief .. "frame y not >= 1") + assert(f.w >= 1, name_brief .. "frame width not >= 1") + assert(f.h >= 1, name_brief .. "frame height not >= 1") -- create window protected.window = window.create(self.p_window, f.x, f.y, f.w, f.h, true) @@ -252,7 +263,7 @@ function element.new(args) end -- check window - assert(self.p_window, "graphics.element{" .. self.elem_type .. "}: no parent window provided") + assert(self.p_window, name_brief .. "no parent window provided") -- prepare the template if args.parent == nil then @@ -421,17 +432,18 @@ function element.new(args) -- handle a monitor touch or mouse click ---@param event mouse_interaction mouse interaction event function public.handle_mouse(event) - local in_x = event.x >= self.bounds.x1 and event.x <= self.bounds.x2 - local in_y = event.y >= self.bounds.y1 and event.y <= self.bounds.y2 + local x_ini, y_ini, x_cur, y_cur = event.initial.x, event.initial.y, event.current.x, event.current.y - if in_x and in_y then - local event_T = core.events.mouse_transposed(event, (event.x - self.position.x) + 1, (event.y - self.position.y) + 1) + local ini_in = _in_bounds(x_ini, y_ini) + local cur_in = _in_bounds(x_cur, y_cur) - -- handle the touch event, transformed into the window frame + if ini_in then + local event_T = core.events.mouse_transposed(event, self.position.x, self.position.y) + if not cur_in then event_T.type = core.events.CLICK_TYPE.EXITED end + + -- handle the mouse event then pass to children protected.handle_mouse(event_T) - - -- pass on touch event to children - for _, val in pairs(self.children) do val.handle_mouse(event_T) end + for _, child in pairs(self.children) do child.handle_mouse(event_T) end end end diff --git a/graphics/events.lua b/graphics/events.lua new file mode 100644 index 0000000..1b5219c --- /dev/null +++ b/graphics/events.lua @@ -0,0 +1,152 @@ +-- +-- Graphics Events and Event Handlers +-- + +local util = require("scada-common.util") + +local events = {} + +---@enum CLICK_BUTTON +events.CLICK_BUTTON = { + GENERIC = 0, + LEFT_BUTTON = 1, + RIGHT_BUTTON = 2, + MID_BUTTON = 3 +} + +---@enum CLICK_TYPE +events.CLICK_TYPE = { + TAP = 1, -- screen tap (complete click) + DOWN = 2, -- button down + UP = 3, -- button up (completed a click) + DRAG = 4, -- mouse dragged + SCROLL_DOWN = 5, -- scroll down + SCROLL_UP = 6, -- scroll up + EXITED = 7 -- cursor exited bounds of element +} + +---@class mouse_interaction +---@field monitor string +---@field button CLICK_BUTTON +---@field type CLICK_TYPE +---@field initial coordinate_2d +---@field current coordinate_2d + +local handler = { + button_down = { { 0, 0 }, { 0, 0 }, { 0, 0 } } -- left, right, middle button down tracking +} + +-- create a new 2D coordinate +---@param x integer +---@param y integer +---@return coordinate_2d +local function _coord2d(x, y) return { x = x, y = y } end + +-- create a new monitor touch mouse interaction event +---@nodiscard +---@param monitor string +---@param x integer +---@param y integer +---@return mouse_interaction +local function _monitor_touch(monitor, x, y) + return { + monitor = monitor, + button = events.CLICK_BUTTON.GENERIC, + type = events.CLICK_TYPE.TAP, + initial = _coord2d(x, y), + current = _coord2d(x, y) + } +end + +-- create a new mouse button mouse interaction event +---@nodiscard +---@param button CLICK_BUTTON mouse button +---@param type CLICK_TYPE click type +---@param x1 integer initial x +---@param y1 integer initial y +---@param x2 integer current x +---@param y2 integer current y +---@return mouse_interaction +local function _mouse_event(button, type, x1, y1, x2, y2) + return { + monitor = "terminal", + button = button, + type = type, + initial = _coord2d(x1, y1), + current = _coord2d(x2, y2) + } +end + +-- create a new generic mouse interaction event +---@nodiscard +---@param type CLICK_TYPE +---@param x integer +---@param y integer +---@return mouse_interaction +function events.mouse_generic(type, x, y) + return { + monitor = "", + button = events.CLICK_BUTTON.GENERIC, + type = type, + initial = _coord2d(x, y), + current = _coord2d(x, y) + } +end + +-- create a new transposed mouse interaction event using the event's monitor/button fields +---@nodiscard +---@param event mouse_interaction +---@param elem_pos_x integer element's x position: new x = (event x - element x) + 1 +---@param elem_pos_y integer element's y position: new y = (event y - element y) + 1 +---@return mouse_interaction +function events.mouse_transposed(event, elem_pos_x, elem_pos_y) + return { + monitor = event.monitor, + button = event.button, + type = event.type, + initial = _coord2d((event.initial.x - elem_pos_x) + 1, (event.initial.y - elem_pos_y) + 1), + current = _coord2d((event.current.x - elem_pos_x) + 1, (event.current.y - elem_pos_y) + 1) + } +end + +-- create a new mouse event to pass onto graphics renderer
+-- supports: mouse_click, mouse_up, mouse_drag, mouse_scroll, and monitor_touch +---@param event_type os_event OS event to handle +---@param opt integer|string button, scroll direction, or monitor for monitor touch +---@param x integer x coordinate +---@param y integer y coordinate +---@return mouse_interaction|nil +function events.new_mouse_event(event_type, opt, x, y) + if event_type == "mouse_click" then + ---@cast opt 1|2|3 + handler.button_down[opt] = { x, y } + return _mouse_event(opt, events.CLICK_TYPE.DOWN, x, y, x, y) + elseif event_type == "mouse_up" then + ---@cast opt 1|2|3 + local initial = handler.button_down[opt] ---@type coordinate_2d + return _mouse_event(opt, events.CLICK_TYPE.UP, initial.x, initial.y, x, y) + elseif event_type == "monitor_touch" then + ---@cast opt string + return _monitor_touch(opt, x, y) + elseif event_type == "mouse_drag" then + ---@cast opt 1|2|3 + local initial = handler.button_down[opt] ---@type coordinate_2d + return _mouse_event(opt, events.CLICK_TYPE.DRAG, initial.x, initial.y, x, y) + elseif event_type == "mouse_scroll" then + ---@cast opt 1|-1 + local scroll_direction = util.trinary(opt == 1, events.CLICK_TYPE.SCROLL_DOWN, events.CLICK_TYPE.SCROLL_UP) + return _mouse_event(events.CLICK_BUTTON.GENERIC, scroll_direction, x, y, x, y) + end +end + +-- create a new key event to pass onto graphics renderer
+-- supports: char, key, and key_up +---@param event_type os_event +function events.new_key_event(event_type) + if event_type == "char" then + elseif event_type == "key" then + elseif event_type == "key_up" then + end +end + +return events diff --git a/graphics/graphics.lua b/graphics/graphics.lua deleted file mode 100644 index 11b04ff..0000000 --- a/graphics/graphics.lua +++ /dev/null @@ -1,30 +0,0 @@ - - -local flasher = require("graphics.flasher") -local core = require("graphics.core") - -local graphics = {} - -graphics.flasher = flasher - --- pass mouse events to graphics engine --- supports: mouse_click, mouse_up, mouse_drag, mouse_scroll, and monitor_touch ----@param event_type os_event -function graphics.handle_mouse(event_type) - if event_type == "mouse_click" then - elseif event_type == "mouse_up" or event_type == "monitor_touch" then - elseif event_type == "mouse_drag" then - elseif event_type == "mouse_scroll" then - end -end - --- pass char, key, or key_up event to graphics engine ----@param event_type os_event -function graphics.handle_key(event_type) - if event_type == "char" then - elseif event_type == "key" then - elseif event_type == "key_up" then - end -end - -return graphics diff --git a/scada-common/types.lua b/scada-common/types.lua index 9beb1e6..8df01c1 100644 --- a/scada-common/types.lua +++ b/scada-common/types.lua @@ -39,6 +39,10 @@ function types.new_radiation_reading(r, u) return { radiation = r, unit = u } en ---@return radiation_reading function types.new_zero_radiation_reading() return { radiation = 0, unit = "nSv" } end +---@class coordinate_2d +---@field x integer +---@field y integer + ---@class coordinate ---@field x integer ---@field y integer