diff --git a/coordinator/renderer.lua b/coordinator/renderer.lua
index 4aa0b53..ffb36bc 100644
--- a/coordinator/renderer.lua
+++ b/coordinator/renderer.lua
@@ -163,9 +163,9 @@ end
function renderer.ui_ready() return engine.ui_ready end
-- handle a touch event
----@param event mouse_interaction
+---@param event mouse_interaction|nil
function renderer.handle_mouse(event)
- if engine.ui_ready then
+ if engine.ui_ready and event ~= nil then
if event.monitor == engine.monitors.primary_name then
engine.ui.main_display.handle_mouse(event)
else
diff --git a/coordinator/startup.lua b/coordinator/startup.lua
index e5863c8..2944c14 100644
--- a/coordinator/startup.lua
+++ b/coordinator/startup.lua
@@ -20,7 +20,7 @@ local sounder = require("coordinator.sounder")
local apisessions = require("coordinator.session.apisessions")
-local COORDINATOR_VERSION = "v0.13.8"
+local COORDINATOR_VERSION = "v0.14.0"
local println = util.println
local println_ts = util.println_ts
@@ -359,7 +359,7 @@ local function main()
end
elseif event == "monitor_touch" then
-- handle a monitor touch event
- renderer.handle_mouse(core.events.touch(param1, param2, param3))
+ renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
elseif event == "speaker_audio_empty" then
-- handle speaker buffer emptied
sounder.continue()
diff --git a/coordinator/ui/components/boiler.lua b/coordinator/ui/components/boiler.lua
index c4a433b..9ed497c 100644
--- a/coordinator/ui/components/boiler.lua
+++ b/coordinator/ui/components/boiler.lua
@@ -9,8 +9,8 @@ local DataIndicator = require("graphics.elements.indicators.data")
local StateIndicator = require("graphics.elements.indicators.state")
local VerticalBar = require("graphics.elements.indicators.vbar")
-local cpair = core.graphics.cpair
-local border = core.graphics.border
+local cpair = core.cpair
+local border = core.border
-- new boiler view
---@param root graphics_element parent
diff --git a/coordinator/ui/components/imatrix.lua b/coordinator/ui/components/imatrix.lua
index 2910fbb..c94ff9f 100644
--- a/coordinator/ui/components/imatrix.lua
+++ b/coordinator/ui/components/imatrix.lua
@@ -13,10 +13,10 @@ local PowerIndicator = require("graphics.elements.indicators.power")
local StateIndicator = require("graphics.elements.indicators.state")
local VerticalBar = require("graphics.elements.indicators.vbar")
-local cpair = core.graphics.cpair
-local border = core.graphics.border
+local cpair = core.cpair
+local border = core.border
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-- new induction matrix view
---@param root graphics_element parent
diff --git a/coordinator/ui/components/processctl.lua b/coordinator/ui/components/processctl.lua
index 5b8d8ae..8719968 100644
--- a/coordinator/ui/components/processctl.lua
+++ b/coordinator/ui/components/processctl.lua
@@ -21,10 +21,10 @@ local HazardButton = require("graphics.elements.controls.hazard_button")
local RadioButton = require("graphics.elements.controls.radio_button")
local SpinboxNumeric = require("graphics.elements.controls.spinbox_numeric")
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-local cpair = core.graphics.cpair
-local border = core.graphics.border
+local cpair = core.cpair
+local border = core.border
local period = core.flasher.PERIOD
diff --git a/coordinator/ui/components/reactor.lua b/coordinator/ui/components/reactor.lua
index db75fb1..578bbba 100644
--- a/coordinator/ui/components/reactor.lua
+++ b/coordinator/ui/components/reactor.lua
@@ -11,8 +11,8 @@ local DataIndicator = require("graphics.elements.indicators.data")
local HorizontalBar = require("graphics.elements.indicators.hbar")
local StateIndicator = require("graphics.elements.indicators.state")
-local cpair = core.graphics.cpair
-local border = core.graphics.border
+local cpair = core.cpair
+local border = core.border
-- create new reactor view
---@param root graphics_element parent
diff --git a/coordinator/ui/components/turbine.lua b/coordinator/ui/components/turbine.lua
index e4d6967..3bfd731 100644
--- a/coordinator/ui/components/turbine.lua
+++ b/coordinator/ui/components/turbine.lua
@@ -12,8 +12,8 @@ local PowerIndicator = require("graphics.elements.indicators.power")
local StateIndicator = require("graphics.elements.indicators.state")
local VerticalBar = require("graphics.elements.indicators.vbar")
-local cpair = core.graphics.cpair
-local border = core.graphics.border
+local cpair = core.cpair
+local border = core.border
-- new turbine view
---@param root graphics_element parent
diff --git a/coordinator/ui/components/unit_detail.lua b/coordinator/ui/components/unit_detail.lua
index a29dd4b..7181430 100644
--- a/coordinator/ui/components/unit_detail.lua
+++ b/coordinator/ui/components/unit_detail.lua
@@ -26,10 +26,10 @@ local PushButton = require("graphics.elements.controls.push_button")
local RadioButton = require("graphics.elements.controls.radio_button")
local SpinboxNumeric = require("graphics.elements.controls.spinbox_numeric")
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-local cpair = core.graphics.cpair
-local border = core.graphics.border
+local cpair = core.cpair
+local border = core.border
local period = core.flasher.PERIOD
diff --git a/coordinator/ui/components/unit_overview.lua b/coordinator/ui/components/unit_overview.lua
index 24bc02e..bd341bf 100644
--- a/coordinator/ui/components/unit_overview.lua
+++ b/coordinator/ui/components/unit_overview.lua
@@ -14,9 +14,9 @@ local Div = require("graphics.elements.div")
local PipeNetwork = require("graphics.elements.pipenet")
local TextBox = require("graphics.elements.textbox")
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-local pipe = core.graphics.pipe
+local pipe = core.pipe
-- make a new unit overview window
---@param parent graphics_element parent
diff --git a/coordinator/ui/layout/main_view.lua b/coordinator/ui/layout/main_view.lua
index a7f8ae2..5510382 100644
--- a/coordinator/ui/layout/main_view.lua
+++ b/coordinator/ui/layout/main_view.lua
@@ -18,9 +18,9 @@ local TextBox = require("graphics.elements.textbox")
local DataIndicator = require("graphics.elements.indicators.data")
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-local cpair = core.graphics.cpair
+local cpair = core.cpair
-- create new main view
---@param main graphics_element main displaybox
diff --git a/coordinator/ui/style.lua b/coordinator/ui/style.lua
index 74923f2..b78fc91 100644
--- a/coordinator/ui/style.lua
+++ b/coordinator/ui/style.lua
@@ -6,7 +6,7 @@ local core = require("graphics.core")
local style = {}
-local cpair = core.graphics.cpair
+local cpair = core.cpair
-- GLOBAL --
diff --git a/graphics/core.lua b/graphics/core.lua
index d03e551..58b6b8c 100644
--- a/graphics/core.lua
+++ b/graphics/core.lua
@@ -1,96 +1,19 @@
--
--- Graphics Core Functions and Objects
+-- Graphics Core Types, Checks, and Constructors
--
+local events = require("graphics.events")
+local flasher = require("graphics.flasher")
+
local core = {}
-local flasher = require("graphics.flasher")
-
core.flasher = flasher
-
-local events = {}
-
----@enum click_type
-events.click_type = {
- VIRTUAL = 0,
- LEFT_BUTTON = 1,
- RIGHT_BUTTON = 2,
- MID_BUTTON = 3
-}
-
----@class mouse_interaction
----@field monitor string
----@field button integer
----@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_type.LEFT_BUTTON,
- x = x,
- y = y
- }
-end
-
--- create a new mouse click mouse interaction event
----@nodiscard
----@param button click_type
----@param x integer
----@param y integer
----@return mouse_interaction
-function events.click(button, x, y)
- return {
- monitor = "terminal",
- button = button,
- 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,
- x = new_x,
- y = new_y
- }
-end
-
--- create a new generic mouse interaction event
----@nodiscard
----@param monitor string
----@param button click_type
----@param x integer
----@param y integer
----@return mouse_interaction
-function events.mouse_generic(monitor, button, x, y)
- return {
- monitor = monitor,
- button = button,
- x = x,
- y = y
- }
-end
-
core.events = events
-local graphics = {}
+-- Core Types
---@enum TEXT_ALIGN
-graphics.TEXT_ALIGN = {
+core.TEXT_ALIGN = {
LEFT = 1,
CENTER = 2,
RIGHT = 3
@@ -109,7 +32,7 @@ graphics.TEXT_ALIGN = {
---@param color color border color
---@param even? boolean whether to pad width extra to account for rectangular pixels, defaults to false
---@return graphics_border
-function graphics.border(width, color, even)
+function core.border(width, color, even)
return {
width = width,
color = color,
@@ -130,7 +53,7 @@ end
---@param w integer
---@param h integer
---@return graphics_frame
-function graphics.gframe(x, y, w, h)
+function core.gframe(x, y, w, h)
return {
x = x,
y = y,
@@ -154,7 +77,7 @@ end
---@param a color
---@param b color
---@return cpair
-function graphics.cpair(a, b)
+function core.cpair(a, b)
return {
-- color pairs
color_a = a,
@@ -191,7 +114,7 @@ end
---@param thin? boolean true for 1 subpixel, false (default) for 2
---@param align_tr? boolean false to align bottom left (default), true to align top right
---@return pipe
-function graphics.pipe(x1, y1, x2, y2, color, thin, align_tr)
+function core.pipe(x1, y1, x2, y2, color, thin, align_tr)
return {
x1 = x1,
y1 = y1,
@@ -205,6 +128,4 @@ function graphics.pipe(x1, y1, x2, y2, color, thin, align_tr)
}
end
-core.graphics = graphics
-
return core
diff --git a/graphics/element.lua b/graphics/element.lua
index 04d4fd2..edcbdcc 100644
--- a/graphics/element.lua
+++ b/graphics/element.lua
@@ -28,6 +28,7 @@ local element = {}
---|sidebar_args
---|spinbox_args
---|switch_button_args
+---|tabbar_args
---|alarm_indicator_light
---|core_map_args
---|data_indicator_args
@@ -59,10 +60,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 = {}
@@ -73,15 +74,26 @@ function element.new(args)
enabled = true,
value = nil, ---@type any
window = nil, ---@type table
- fg_bg = core.graphics.cpair(colors.white, colors.black),
- frame = core.graphics.gframe(1, 1, 1, 1)
+ fg_bg = core.cpair(colors.white, colors.black),
+ 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 +150,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 +264,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 +433,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/elements/controls/hazard_button.lua b/graphics/elements/controls/hazard_button.lua
index 2b9baa6..4dca5c4 100644
--- a/graphics/elements/controls/hazard_button.lua
+++ b/graphics/elements/controls/hazard_button.lua
@@ -141,23 +141,26 @@ local function hazard_button(args)
end
-- handle mouse interaction
- function e.handle_mouse(_)
+ ---@param event mouse_interaction mouse event
+ function e.handle_mouse(event)
if e.enabled then
- -- change text color to indicate clicked
- e.window.setTextColor(args.accent)
- e.window.setCursorPos(3, 2)
- e.window.write(args.text)
+ if core.events.was_clicked(event.type) then
+ -- change text color to indicate clicked
+ e.window.setTextColor(args.accent)
+ e.window.setCursorPos(3, 2)
+ e.window.write(args.text)
- -- abort any other callbacks
- tcd.abort(on_timeout)
- tcd.abort(on_success)
- tcd.abort(on_failure)
+ -- abort any other callbacks
+ tcd.abort(on_timeout)
+ tcd.abort(on_success)
+ tcd.abort(on_failure)
- -- 1.5 second timeout
- tcd.dispatch(1.5, on_timeout)
+ -- 1.5 second timeout
+ tcd.dispatch(1.5, on_timeout)
- -- call the touch callback
- args.callback()
+ -- call the touch callback
+ args.callback()
+ end
end
end
@@ -165,18 +168,13 @@ local function hazard_button(args)
---@param result boolean true for success, false for failure
function e.response_callback(result)
tcd.abort(on_timeout)
-
- if result then
- on_success()
- else
- on_failure(0)
- end
+ if result then on_success() else on_failure(0) end
end
-- set the value (true simulates pressing the button)
---@param val boolean new value
function e.set_value(val)
- if val then e.handle_mouse(core.events.mouse_generic("", core.events.click_type.VIRTUAL, 1, 1)) end
+ if val then e.handle_mouse(core.events.mouse_generic(core.events.CLICK_TYPE.UP, 1, 1)) end
end
-- show the button as disabled
diff --git a/graphics/elements/controls/multi_button.lua b/graphics/elements/controls/multi_button.lua
index 2549e2b..e44bad0 100644
--- a/graphics/elements/controls/multi_button.lua
+++ b/graphics/elements/controls/multi_button.lua
@@ -2,13 +2,13 @@
local util = require("scada-common.util")
+local core = require("graphics.core")
local element = require("graphics.element")
---@class button_option
---@field text string
---@field fg_bg cpair
---@field active_fg_bg cpair
----@field _lpad integer automatically calculated left pad
---@field _start_x integer starting touch x range (inclusive)
---@field _end_x integer ending touch x range (inclusive)
@@ -62,9 +62,7 @@ local function multi_button(args)
local next_x = 2
for i = 1, #args.options do
local opt = args.options[i] ---@type button_option
- local w = string.len(opt.text)
- opt._lpad = math.floor((e.frame.w - w) / 2)
opt._start_x = next_x
opt._end_x = next_x + button_width - 1
@@ -92,20 +90,32 @@ local function multi_button(args)
end
end
+ -- check which button a given x is within
+ ---@return integer|nil button index or nil if not within a button
+ local function which_button(x)
+ for i = 1, #args.options do
+ local opt = args.options[i] ---@type button_option
+ if x >= opt._start_x and x <= opt._end_x then return i end
+ end
+
+ return nil
+ end
+
-- handle mouse interaction
---@param event mouse_interaction mouse event
----@diagnostic disable-next-line: unused-local
function e.handle_mouse(event)
- -- determine what was pressed
- if e.enabled and event.y == 1 then
- for i = 1, #args.options do
- local opt = args.options[i] ---@type button_option
+ -- if enabled and the button row was pressed...
+ if e.enabled and core.events.was_clicked(event.type) then
+ -- a button may have been pressed, which one was it?
+ local button_ini = which_button(event.initial.x)
+ local button_cur = which_button(event.current.x)
- if event.x >= opt._start_x and event.x <= opt._end_x then
- e.value = i
- draw()
- args.callback(e.value)
- end
+ -- mouse up must always have started with a mouse down on the same button to count as a click
+ -- tap always has identical coordinates, so this always passes for taps
+ if button_ini == button_cur and button_cur ~= nil then
+ e.value = button_cur
+ draw()
+ args.callback(e.value)
end
end
end
diff --git a/graphics/elements/controls/push_button.lua b/graphics/elements/controls/push_button.lua
index 9b5f971..ed0fc2a 100644
--- a/graphics/elements/controls/push_button.lua
+++ b/graphics/elements/controls/push_button.lua
@@ -5,6 +5,8 @@ local tcd = require("scada-common.tcallbackdsp")
local core = require("graphics.core")
local element = require("graphics.element")
+local CLICK_TYPE = core.events.CLICK_TYPE
+
---@class push_button_args
---@field text string button text
---@field callback function function to call on touch
@@ -24,6 +26,8 @@ local element = require("graphics.element")
local function push_button(args)
assert(type(args.text) == "string", "graphics.elements.controls.push_button: text is a required field")
assert(type(args.callback) == "function", "graphics.elements.controls.push_button: callback is a required field")
+ assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0),
+ "graphics.elements.controls.push_button: min_width must be nil or a number > 0")
local text_width = string.len(args.text)
@@ -47,36 +51,50 @@ local function push_button(args)
e.window.write(args.text)
end
+ -- draw the button as pressed (if active_fg_bg set)
+ local function show_pressed()
+ if e.enabled and args.active_fg_bg ~= nil then
+ e.value = true
+ e.window.setTextColor(args.active_fg_bg.fgd)
+ e.window.setBackgroundColor(args.active_fg_bg.bkg)
+ draw()
+ end
+ end
+
+ -- draw the button as unpressed (if active_fg_bg set)
+ local function show_unpressed()
+ if e.enabled and args.active_fg_bg ~= nil then
+ e.value = false
+ e.window.setTextColor(e.fg_bg.fgd)
+ e.window.setBackgroundColor(e.fg_bg.bkg)
+ draw()
+ end
+ end
+
-- handle mouse interaction
- function e.handle_mouse(_)
+ ---@param event mouse_interaction mouse event
+ function e.handle_mouse(event)
if e.enabled then
- if args.active_fg_bg ~= nil then
- -- show as pressed
- e.value = true
- e.window.setTextColor(args.active_fg_bg.fgd)
- e.window.setBackgroundColor(args.active_fg_bg.bkg)
- draw()
-
+ if event.type == CLICK_TYPE.TAP then
+ show_pressed()
-- show as unpressed in 0.25 seconds
- tcd.dispatch(0.25, function ()
- e.value = false
- if e.enabled then
- e.window.setTextColor(e.fg_bg.fgd)
- e.window.setBackgroundColor(e.fg_bg.bkg)
- end
- draw()
- end)
+ if args.active_fg_bg ~= nil then tcd.dispatch(0.25, show_unpressed) end
+ args.callback()
+ elseif event.type == CLICK_TYPE.DOWN then
+ show_pressed()
+ elseif event.type == CLICK_TYPE.UP then
+ show_unpressed()
+ args.callback()
+ elseif event.type == CLICK_TYPE.EXITED then
+ show_unpressed()
end
-
- -- call the touch callback
- args.callback()
end
end
-- set the value (true simulates pressing the button)
---@param val boolean new value
function e.set_value(val)
- if val then e.handle_mouse(core.events.mouse_generic("", core.events.click_type.VIRTUAL, 1, 1)) end
+ if val then e.handle_mouse(core.events.mouse_generic(core.events.CLICK_TYPE.UP, 1, 1)) end
end
-- show butten as enabled
diff --git a/graphics/elements/controls/radio_button.lua b/graphics/elements/controls/radio_button.lua
index 3b2a593..050bf39 100644
--- a/graphics/elements/controls/radio_button.lua
+++ b/graphics/elements/controls/radio_button.lua
@@ -1,5 +1,6 @@
-- Radio Button Graphics Element
+local core = require("graphics.core")
local element = require("graphics.element")
---@class radio_button_args
@@ -82,10 +83,10 @@ local function radio_button(args)
-- handle mouse interaction
---@param event mouse_interaction mouse event
function e.handle_mouse(event)
- -- determine what was pressed
- if e.enabled then
- if args.options[event.y] ~= nil then
- e.value = event.y
+ if e.enabled and core.events.was_clicked(event.type) and (event.initial.y == event.current.y) then
+ -- determine what was pressed
+ if args.options[event.current.y] ~= nil then
+ e.value = event.current.y
draw()
args.callback(e.value)
end
diff --git a/graphics/elements/controls/sidebar.lua b/graphics/elements/controls/sidebar.lua
index 885761d..997b372 100644
--- a/graphics/elements/controls/sidebar.lua
+++ b/graphics/elements/controls/sidebar.lua
@@ -2,8 +2,11 @@
local tcd = require("scada-common.tcallbackdsp")
+local core = require("graphics.core")
local element = require("graphics.element")
+local CLICK_TYPE = core.events.CLICK_TYPE
+
---@class sidebar_tab
---@field char string character identifier
---@field color cpair tab colors (fg/bg)
@@ -39,7 +42,10 @@ local function sidebar(args)
-- show the button state
---@param pressed boolean if the currently selected tab should appear as actively pressed
- local function draw(pressed)
+ ---@param pressed_idx? integer optional index to show as held (that is not yet selected)
+ local function draw(pressed, pressed_idx)
+ pressed_idx = pressed_idx or e.value
+
for i = 1, #args.tabs do
local tab = args.tabs[i] ---@type sidebar_tab
@@ -47,7 +53,7 @@ local function sidebar(args)
e.window.setCursorPos(1, y)
- if pressed and e.value == i then
+ if pressed and i == pressed_idx then
e.window.setTextColor(e.fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg)
else
@@ -74,16 +80,27 @@ local function sidebar(args)
function e.handle_mouse(event)
-- determine what was pressed
if e.enabled then
- local idx = math.ceil(event.y / 3)
+ local cur_idx = math.ceil(event.current.y / 3)
+ local ini_idx = math.ceil(event.initial.y / 3)
- if args.tabs[idx] ~= nil then
- e.value = idx
- draw(true)
-
- -- show as unpressed in 0.25 seconds
- tcd.dispatch(0.25, function () draw(false) end)
-
- args.callback(e.value)
+ if args.tabs[cur_idx] ~= nil then
+ if event.type == CLICK_TYPE.TAP then
+ e.value = cur_idx
+ draw(true)
+ -- show as unpressed in 0.25 seconds
+ tcd.dispatch(0.25, function () draw(false) end)
+ args.callback(e.value)
+ elseif event.type == CLICK_TYPE.DOWN then
+ draw(true, cur_idx)
+ elseif event.type == CLICK_TYPE.UP then
+ if cur_idx == ini_idx then
+ e.value = cur_idx
+ draw(false)
+ args.callback(e.value)
+ else draw(false) end
+ elseif event.type == CLICK_TYPE.EXITED then
+ draw(false)
+ end
end
end
end
diff --git a/graphics/elements/controls/spinbox_numeric.lua b/graphics/elements/controls/spinbox_numeric.lua
index 15e0e76..6b88c0e 100644
--- a/graphics/elements/controls/spinbox_numeric.lua
+++ b/graphics/elements/controls/spinbox_numeric.lua
@@ -2,6 +2,7 @@
local util = require("scada-common.util")
+local core = require("graphics.core")
local element = require("graphics.element")
---@class spinbox_args
@@ -130,19 +131,22 @@ local function spinbox(args)
---@param event mouse_interaction mouse event
function e.handle_mouse(event)
-- only handle if on an increment or decrement arrow
- if e.enabled and event.x ~= dec_point_x then
- local idx = util.trinary(event.x > dec_point_x, event.x - 1, event.x)
- if digits[idx] ~= nil then
- if event.y == 1 then
- -- increment
- digits[idx] = digits[idx] + 1
- elseif event.y == 3 then
- -- decrement
- digits[idx] = digits[idx] - 1
- end
+ if e.enabled and core.events.was_clicked(event.type) and
+ (event.current.x ~= dec_point_x) and (event.current.y ~= 2) then
+ if event.current.x == event.initial.x and event.current.y == event.initial.y then
+ local idx = util.trinary(event.current.x > dec_point_x, event.current.x - 1, event.current.x)
+ if digits[idx] ~= nil then
+ if event.current.y == 1 then
+ -- increment
+ digits[idx] = digits[idx] + 1
+ elseif event.current.y == 3 then
+ -- decrement
+ digits[idx] = digits[idx] - 1
+ end
- update_value()
- show_num()
+ update_value()
+ show_num()
+ end
end
end
end
diff --git a/graphics/elements/controls/switch_button.lua b/graphics/elements/controls/switch_button.lua
index 012872c..645bf8a 100644
--- a/graphics/elements/controls/switch_button.lua
+++ b/graphics/elements/controls/switch_button.lua
@@ -1,5 +1,6 @@
-- Button Graphics Element
+local core = require("graphics.core")
local element = require("graphics.element")
---@class switch_button_args
@@ -22,13 +23,15 @@ local function switch_button(args)
assert(type(args.text) == "string", "graphics.elements.controls.switch_button: text is a required field")
assert(type(args.callback) == "function", "graphics.elements.controls.switch_button: callback is a required field")
assert(type(args.active_fg_bg) == "table", "graphics.elements.controls.switch_button: active_fg_bg is a required field")
+ assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0),
+ "graphics.elements.controls.switch_button: min_width must be nil or a number > 0")
- -- single line
- args.height = 1
-
- -- determine widths
local text_width = string.len(args.text)
- args.width = math.max(text_width + 2, args.min_width)
+
+ -- single line height, calculate width
+ args.height = 1
+ args.min_width = args.min_width or 0
+ args.width = math.max(text_width, args.min_width)
-- create new graphics element base object
local e = element.new(args)
@@ -63,8 +66,9 @@ local function switch_button(args)
draw_state()
-- handle mouse interaction
- function e.handle_mouse(_)
- if e.enabled then
+ ---@param event mouse_interaction mouse event
+ function e.handle_mouse(event)
+ if e.enabled and core.events.was_clicked(event.type) then
-- toggle state
e.value = not e.value
draw_state()
diff --git a/graphics/elements/controls/tabbar.lua b/graphics/elements/controls/tabbar.lua
new file mode 100644
index 0000000..6249951
--- /dev/null
+++ b/graphics/elements/controls/tabbar.lua
@@ -0,0 +1,130 @@
+-- Tab Bar Graphics Element
+
+local util = require("scada-common.util")
+
+local core = require("graphics.core")
+local element = require("graphics.element")
+
+---@class tabbar_tab
+---@field name string tab name
+---@field color cpair tab colors (fg/bg)
+---@field _start_x integer starting touch x range (inclusive)
+---@field _end_x integer ending touch x range (inclusive)
+
+---@class tabbar_args
+---@field tabs table tab options
+---@field callback function function to call on tab change
+---@field min_width? integer text length + 2 if omitted
+---@field parent graphics_element
+---@field id? string element id
+---@field x? integer 1 if omitted
+---@field y? integer 1 if omitted
+---@field width? integer parent width if omitted
+---@field fg_bg? cpair foreground/background colors
+
+-- new tab selector
+---@param args tabbar_args
+---@return graphics_element element, element_id id
+local function tabbar(args)
+ assert(type(args.tabs) == "table", "graphics.elements.controls.tabbar: tabs is a required field")
+ assert(#args.tabs > 0, "graphics.elements.controls.tabbar: at least one tab is required")
+ assert(type(args.callback) == "function", "graphics.elements.controls.tabbar: callback is a required field")
+ assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0),
+ "graphics.elements.controls.tabbar: min_width must be nil or a number > 0")
+
+ -- always 1 tall
+ args.height = 1
+
+ -- determine widths
+ local max_width = 1
+ for i = 1, #args.tabs do
+ local opt = args.tabs[i] ---@type tabbar_tab
+ if string.len(opt.name) > max_width then
+ max_width = string.len(opt.name)
+ end
+ end
+
+ local button_width = math.max(max_width, args.min_width or 0)
+
+ -- create new graphics element base object
+ local e = element.new(args)
+
+ assert(e.frame.w >= (button_width * #args.tabs), "graphics.elements.controls.tabbar: width insufficent to display all tabs")
+
+ -- default to 1st tab
+ e.value = 1
+
+ -- calculate required tab dimension information
+ local next_x = 1
+ for i = 1, #args.tabs do
+ local tab = args.tabs[i] ---@type tabbar_tab
+
+ tab._start_x = next_x
+ tab._end_x = next_x + button_width - 1
+
+ next_x = next_x + button_width
+ end
+
+ -- show the tab state
+ local function draw()
+ for i = 1, #args.tabs do
+ local tab = args.tabs[i] ---@type tabbar_tab
+
+ e.window.setCursorPos(tab._start_x, 1)
+
+ if e.value == i then
+ e.window.setTextColor(tab.color.fgd)
+ e.window.setBackgroundColor(tab.color.bkg)
+ else
+ e.window.setTextColor(e.fg_bg.fgd)
+ e.window.setBackgroundColor(e.fg_bg.bkg)
+ end
+
+ e.window.write(util.pad(tab.name, button_width))
+ end
+ end
+
+ -- check which tab a given x is within
+ ---@return integer|nil button index or nil if not within a tab
+ local function which_tab(x)
+ for i = 1, #args.tabs do
+ local tab = args.tabs[i] ---@type tabbar_tab
+ if x >= tab._start_x and x <= tab._end_x then return i end
+ end
+
+ return nil
+ end
+
+ -- handle mouse interaction
+ ---@param event mouse_interaction mouse event
+ function e.handle_mouse(event)
+ -- determine what was pressed
+ if e.enabled and core.events.was_clicked(event.type) then
+ -- a button may have been pressed, which one was it?
+ local tab_ini = which_tab(event.initial.x)
+ local tab_cur = which_tab(event.current.x)
+
+ -- mouse up must always have started with a mouse down on the same tab to count as a click
+ -- tap always has identical coordinates, so this always passes for taps
+ if tab_ini == tab_cur and tab_cur ~= nil then
+ e.value = tab_cur
+ draw()
+ args.callback(e.value)
+ end
+ end
+ end
+
+ -- set the value
+ ---@param val integer new value
+ function e.set_value(val)
+ e.value = val
+ draw()
+ end
+
+ -- initial draw
+ draw()
+
+ return e.get()
+end
+
+return tabbar
diff --git a/graphics/elements/indicators/coremap.lua b/graphics/elements/indicators/coremap.lua
index 323e17c..05434a3 100644
--- a/graphics/elements/indicators/coremap.lua
+++ b/graphics/elements/indicators/coremap.lua
@@ -26,7 +26,7 @@ local function core_map(args)
args.height = 18
-- inherit only foreground color
- args.fg_bg = core.graphics.cpair(args.parent.get_fg_bg().fgd, colors.gray)
+ args.fg_bg = core.cpair(args.parent.get_fg_bg().fgd, colors.gray)
-- create new graphics element base object
local e = element.new(args)
diff --git a/graphics/elements/pipenet.lua b/graphics/elements/pipenet.lua
index 71ee9fd..8a1d29b 100644
--- a/graphics/elements/pipenet.lua
+++ b/graphics/elements/pipenet.lua
@@ -37,7 +37,7 @@ local function pipenet(args)
args.y = args.y or 1
if args.bg ~= nil then
- args.fg_bg = core.graphics.cpair(args.bg, args.bg)
+ args.fg_bg = core.cpair(args.bg, args.bg)
end
-- create new graphics element base object
@@ -55,7 +55,7 @@ local function pipenet(args)
e.window.setCursorPos(x, y)
- local c = core.graphics.cpair(pipe.color, e.fg_bg.bkg)
+ local c = core.cpair(pipe.color, e.fg_bg.bkg)
if pipe.align_tr then
-- cross width then height
diff --git a/graphics/elements/textbox.lua b/graphics/elements/textbox.lua
index c911677..9066deb 100644
--- a/graphics/elements/textbox.lua
+++ b/graphics/elements/textbox.lua
@@ -5,7 +5,7 @@ local util = require("scada-common.util")
local core = require("graphics.core")
local element = require("graphics.element")
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
---@class textbox_args
---@field text string text to show
diff --git a/graphics/events.lua b/graphics/events.lua
new file mode 100644
index 0000000..7370481
--- /dev/null
+++ b/graphics/events.lua
@@ -0,0 +1,162 @@
+--
+-- 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
+}
+
+-- 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
+
+---@class mouse_interaction
+---@field monitor string
+---@field button CLICK_BUTTON
+---@field type CLICK_TYPE
+---@field initial coordinate_2d
+---@field current coordinate_2d
+
+local handler = {
+ -- left, right, middle button down tracking
+ button_down = {
+ _coord2d(0, 0),
+ _coord2d(0, 0),
+ _coord2d(0, 0)
+ }
+}
+
+-- 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
+
+-- check if an event qualifies as a click (tap or up)
+---@nodiscard
+---@param t CLICK_TYPE
+function events.was_clicked(t) return t == events.CLICK_TYPE.TAP or t == events.CLICK_TYPE.UP 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] = _coord2d(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/install_manifest.json b/install_manifest.json
index 5b53c17..dea492c 100644
--- a/install_manifest.json
+++ b/install_manifest.json
@@ -1 +1 @@
-{"versions": {"installer": "v1.0", "bootloader": "0.2", "comms": "1.4.1", "reactor-plc": "v1.1.17", "rtu": "v1.0.5", "supervisor": "v0.15.7", "coordinator": "v0.13.8", "pocket": "alpha-v0.2.6"}, "files": {"system": ["initenv.lua", "startup.lua"], "common": ["scada-common/constants.lua", "scada-common/crypto.lua", "scada-common/types.lua", "scada-common/comms.lua", "scada-common/mqueue.lua", "scada-common/util.lua", "scada-common/psil.lua", "scada-common/crash.lua", "scada-common/rsio.lua", "scada-common/ppm.lua", "scada-common/tcallbackdsp.lua", "scada-common/log.lua"], "graphics": ["graphics/core.lua", "graphics/flasher.lua", "graphics/element.lua", "graphics/elements/div.lua", "graphics/elements/colormap.lua", "graphics/elements/textbox.lua", "graphics/elements/pipenet.lua", "graphics/elements/multipane.lua", "graphics/elements/displaybox.lua", "graphics/elements/rectangle.lua", "graphics/elements/tiling.lua", "graphics/elements/controls/radio_button.lua", "graphics/elements/controls/spinbox_numeric.lua", "graphics/elements/controls/switch_button.lua", "graphics/elements/controls/sidebar.lua", "graphics/elements/controls/multi_button.lua", "graphics/elements/controls/push_button.lua", "graphics/elements/controls/hazard_button.lua", "graphics/elements/animations/waiting.lua", "graphics/elements/indicators/ledpair.lua", "graphics/elements/indicators/state.lua", "graphics/elements/indicators/icon.lua", "graphics/elements/indicators/hbar.lua", "graphics/elements/indicators/light.lua", "graphics/elements/indicators/led.lua", "graphics/elements/indicators/data.lua", "graphics/elements/indicators/rad.lua", "graphics/elements/indicators/trilight.lua", "graphics/elements/indicators/vbar.lua", "graphics/elements/indicators/coremap.lua", "graphics/elements/indicators/power.lua", "graphics/elements/indicators/ledrgb.lua", "graphics/elements/indicators/alight.lua"], "lockbox": ["lockbox/LICENSE", "lockbox/init.lua", "lockbox/digest/sha1.lua", "lockbox/digest/sha2_256.lua", "lockbox/digest/sha2_224.lua", "lockbox/cipher/aes128.lua", "lockbox/cipher/aes192.lua", "lockbox/cipher/aes256.lua", "lockbox/cipher/mode/cfb.lua", "lockbox/cipher/mode/cbc.lua", "lockbox/cipher/mode/ofb.lua", "lockbox/cipher/mode/ctr.lua", "lockbox/mac/hmac.lua", "lockbox/kdf/pbkdf2.lua", "lockbox/padding/ansix923.lua", "lockbox/padding/isoiec7816.lua", "lockbox/padding/zero.lua", "lockbox/padding/pkcs7.lua", "lockbox/util/stream.lua", "lockbox/util/bit.lua", "lockbox/util/queue.lua", "lockbox/util/array.lua"], "reactor-plc": ["reactor-plc/config.lua", "reactor-plc/plc.lua", "reactor-plc/renderer.lua", "reactor-plc/databus.lua", "reactor-plc/startup.lua", "reactor-plc/threads.lua", "reactor-plc/panel/style.lua", "reactor-plc/panel/front_panel.lua"], "rtu": ["rtu/modbus.lua", "rtu/rtu.lua", "rtu/config.lua", "rtu/renderer.lua", "rtu/databus.lua", "rtu/startup.lua", "rtu/threads.lua", "rtu/dev/redstone_rtu.lua", "rtu/dev/sps_rtu.lua", "rtu/dev/turbinev_rtu.lua", "rtu/dev/envd_rtu.lua", "rtu/dev/boilerv_rtu.lua", "rtu/dev/imatrix_rtu.lua", "rtu/dev/sna_rtu.lua", "rtu/panel/style.lua", "rtu/panel/front_panel.lua"], "supervisor": ["supervisor/unit.lua", "supervisor/supervisor.lua", "supervisor/config.lua", "supervisor/startup.lua", "supervisor/facility.lua", "supervisor/unitlogic.lua", "supervisor/session/coordinator.lua", "supervisor/session/rtu.lua", "supervisor/session/plc.lua", "supervisor/session/svsessions.lua", "supervisor/session/pocket.lua", "supervisor/session/svqtypes.lua", "supervisor/session/rsctl.lua", "supervisor/session/rtu/redstone.lua", "supervisor/session/rtu/imatrix.lua", "supervisor/session/rtu/envd.lua", "supervisor/session/rtu/qtypes.lua", "supervisor/session/rtu/txnctrl.lua", "supervisor/session/rtu/turbinev.lua", "supervisor/session/rtu/sna.lua", "supervisor/session/rtu/unit_session.lua", "supervisor/session/rtu/boilerv.lua", "supervisor/session/rtu/sps.lua"], "coordinator": ["coordinator/coordinator.lua", "coordinator/iocontrol.lua", "coordinator/config.lua", "coordinator/renderer.lua", "coordinator/startup.lua", "coordinator/sounder.lua", "coordinator/process.lua", "coordinator/session/api.lua", "coordinator/session/apisessions.lua", "coordinator/ui/style.lua", "coordinator/ui/dialog.lua", "coordinator/ui/components/processctl.lua", "coordinator/ui/components/turbine.lua", "coordinator/ui/components/reactor.lua", "coordinator/ui/components/imatrix.lua", "coordinator/ui/components/boiler.lua", "coordinator/ui/components/unit_detail.lua", "coordinator/ui/components/unit_overview.lua", "coordinator/ui/layout/main_view.lua", "coordinator/ui/layout/unit_view.lua"], "pocket": ["pocket/config.lua", "pocket/renderer.lua", "pocket/startup.lua", "pocket/coreio.lua", "pocket/pocket.lua", "pocket/ui/style.lua", "pocket/ui/main.lua", "pocket/ui/components/reactor_page.lua", "pocket/ui/components/boiler_page.lua", "pocket/ui/components/conn_waiting.lua", "pocket/ui/components/home_page.lua", "pocket/ui/components/unit_page.lua", "pocket/ui/components/turbine_page.lua"]}, "depends": {"reactor-plc": ["system", "common", "graphics"], "rtu": ["system", "common", "graphics"], "supervisor": ["system", "common"], "coordinator": ["system", "common", "graphics"], "pocket": ["system", "common", "graphics"]}, "sizes": {"manifest": 5467, "system": 1991, "common": 90488, "graphics": 115520, "lockbox": 100797, "reactor-plc": 95520, "rtu": 100773, "supervisor": 283040, "coordinator": 196036, "pocket": 36123}}
\ No newline at end of file
+{"versions": {"installer": "v1.0", "bootloader": "0.2", "comms": "1.4.1", "reactor-plc": "v1.2.0", "rtu": "v1.1.0", "supervisor": "v0.15.6", "coordinator": "v0.14.0", "pocket": "alpha-v0.2.6"}, "files": {"system": ["initenv.lua", "startup.lua"], "common": ["scada-common/crypto.lua", "scada-common/ppm.lua", "scada-common/comms.lua", "scada-common/psil.lua", "scada-common/tcallbackdsp.lua", "scada-common/rsio.lua", "scada-common/constants.lua", "scada-common/mqueue.lua", "scada-common/crash.lua", "scada-common/log.lua", "scada-common/types.lua", "scada-common/util.lua"], "graphics": ["graphics/element.lua", "graphics/events.lua", "graphics/flasher.lua", "graphics/core.lua", "graphics/elements/textbox.lua", "graphics/elements/displaybox.lua", "graphics/elements/pipenet.lua", "graphics/elements/rectangle.lua", "graphics/elements/div.lua", "graphics/elements/multipane.lua", "graphics/elements/tiling.lua", "graphics/elements/colormap.lua", "graphics/elements/indicators/alight.lua", "graphics/elements/indicators/icon.lua", "graphics/elements/indicators/power.lua", "graphics/elements/indicators/rad.lua", "graphics/elements/indicators/state.lua", "graphics/elements/indicators/light.lua", "graphics/elements/indicators/vbar.lua", "graphics/elements/indicators/led.lua", "graphics/elements/indicators/coremap.lua", "graphics/elements/indicators/data.lua", "graphics/elements/indicators/ledpair.lua", "graphics/elements/indicators/hbar.lua", "graphics/elements/indicators/trilight.lua", "graphics/elements/indicators/ledrgb.lua", "graphics/elements/controls/switch_button.lua", "graphics/elements/controls/spinbox_numeric.lua", "graphics/elements/controls/hazard_button.lua", "graphics/elements/controls/push_button.lua", "graphics/elements/controls/radio_button.lua", "graphics/elements/controls/multi_button.lua", "graphics/elements/controls/tabbar.lua", "graphics/elements/controls/sidebar.lua", "graphics/elements/animations/waiting.lua"], "lockbox": ["lockbox/init.lua", "lockbox/LICENSE", "lockbox/kdf/pbkdf2.lua", "lockbox/util/bit.lua", "lockbox/util/array.lua", "lockbox/util/stream.lua", "lockbox/util/queue.lua", "lockbox/digest/sha2_224.lua", "lockbox/digest/sha1.lua", "lockbox/digest/sha2_256.lua", "lockbox/cipher/aes128.lua", "lockbox/cipher/aes256.lua", "lockbox/cipher/aes192.lua", "lockbox/cipher/mode/ofb.lua", "lockbox/cipher/mode/cbc.lua", "lockbox/cipher/mode/ctr.lua", "lockbox/cipher/mode/cfb.lua", "lockbox/mac/hmac.lua", "lockbox/padding/ansix923.lua", "lockbox/padding/pkcs7.lua", "lockbox/padding/zero.lua", "lockbox/padding/isoiec7816.lua"], "reactor-plc": ["reactor-plc/renderer.lua", "reactor-plc/threads.lua", "reactor-plc/databus.lua", "reactor-plc/plc.lua", "reactor-plc/config.lua", "reactor-plc/startup.lua", "reactor-plc/panel/front_panel.lua", "reactor-plc/panel/style.lua"], "rtu": ["rtu/renderer.lua", "rtu/threads.lua", "rtu/rtu.lua", "rtu/databus.lua", "rtu/modbus.lua", "rtu/config.lua", "rtu/startup.lua", "rtu/panel/front_panel.lua", "rtu/panel/style.lua", "rtu/dev/sps_rtu.lua", "rtu/dev/envd_rtu.lua", "rtu/dev/boilerv_rtu.lua", "rtu/dev/redstone_rtu.lua", "rtu/dev/sna_rtu.lua", "rtu/dev/imatrix_rtu.lua", "rtu/dev/turbinev_rtu.lua"], "supervisor": ["supervisor/supervisor.lua", "supervisor/unit.lua", "supervisor/config.lua", "supervisor/startup.lua", "supervisor/unitlogic.lua", "supervisor/facility.lua", "supervisor/session/coordinator.lua", "supervisor/session/svqtypes.lua", "supervisor/session/pocket.lua", "supervisor/session/svsessions.lua", "supervisor/session/rtu.lua", "supervisor/session/plc.lua", "supervisor/session/rsctl.lua", "supervisor/session/rtu/boilerv.lua", "supervisor/session/rtu/txnctrl.lua", "supervisor/session/rtu/unit_session.lua", "supervisor/session/rtu/turbinev.lua", "supervisor/session/rtu/envd.lua", "supervisor/session/rtu/imatrix.lua", "supervisor/session/rtu/sps.lua", "supervisor/session/rtu/qtypes.lua", "supervisor/session/rtu/sna.lua", "supervisor/session/rtu/redstone.lua"], "coordinator": ["coordinator/coordinator.lua", "coordinator/renderer.lua", "coordinator/iocontrol.lua", "coordinator/sounder.lua", "coordinator/config.lua", "coordinator/startup.lua", "coordinator/process.lua", "coordinator/ui/dialog.lua", "coordinator/ui/style.lua", "coordinator/ui/layout/main_view.lua", "coordinator/ui/layout/unit_view.lua", "coordinator/ui/components/reactor.lua", "coordinator/ui/components/processctl.lua", "coordinator/ui/components/unit_overview.lua", "coordinator/ui/components/boiler.lua", "coordinator/ui/components/unit_detail.lua", "coordinator/ui/components/imatrix.lua", "coordinator/ui/components/turbine.lua", "coordinator/session/api.lua", "coordinator/session/apisessions.lua"], "pocket": ["pocket/pocket.lua", "pocket/renderer.lua", "pocket/config.lua", "pocket/coreio.lua", "pocket/startup.lua", "pocket/ui/main.lua", "pocket/ui/style.lua", "pocket/ui/components/turbine_page.lua", "pocket/ui/components/reactor_page.lua", "pocket/ui/components/home_page.lua", "pocket/ui/components/unit_page.lua", "pocket/ui/components/boiler_page.lua", "pocket/ui/components/conn_waiting.lua"]}, "depends": {"reactor-plc": ["system", "common", "graphics"], "rtu": ["system", "common", "graphics"], "supervisor": ["system", "common"], "coordinator": ["system", "common", "graphics"], "pocket": ["system", "common", "graphics"]}, "sizes": {"manifest": 5530, "system": 1991, "common": 90553, "graphics": 126610, "lockbox": 100797, "reactor-plc": 95588, "rtu": 100851, "supervisor": 282569, "coordinator": 195894, "pocket": 36101}}
\ No newline at end of file
diff --git a/pocket/renderer.lua b/pocket/renderer.lua
index 1ecf3ab..fa25bcd 100644
--- a/pocket/renderer.lua
+++ b/pocket/renderer.lua
@@ -70,9 +70,9 @@ end
function renderer.ui_ready() return ui.display ~= nil end
-- handle a mouse event
----@param event mouse_interaction
+---@param event mouse_interaction|nil
function renderer.handle_mouse(event)
- if ui.display ~= nil then
+ if ui.display ~= nil and event ~= nil then
ui.display.handle_mouse(event)
end
end
diff --git a/pocket/startup.lua b/pocket/startup.lua
index 459dd8e..9ed088c 100644
--- a/pocket/startup.lua
+++ b/pocket/startup.lua
@@ -153,9 +153,9 @@ local function main()
-- got a packet
local packet = pocket_comms.parse_packet(param1, param2, param3, param4, param5)
pocket_comms.handle_packet(packet)
- elseif event == "mouse_click" then
+ elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then
-- handle a monitor touch event
- renderer.handle_mouse(core.events.touch(param1, param2, param3))
+ renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
end
-- check for termination request
diff --git a/pocket/ui/components/boiler_page.lua b/pocket/ui/components/boiler_page.lua
index fd0eca1..ec13d59 100644
--- a/pocket/ui/components/boiler_page.lua
+++ b/pocket/ui/components/boiler_page.lua
@@ -5,9 +5,9 @@ local core = require("graphics.core")
local Div = require("graphics.elements.div")
local TextBox = require("graphics.elements.textbox")
--- local cpair = core.graphics.cpair
+-- local cpair = core.cpair
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-- new boiler page view
---@param root graphics_element parent
diff --git a/pocket/ui/components/conn_waiting.lua b/pocket/ui/components/conn_waiting.lua
index cd08652..9bbbfc0 100644
--- a/pocket/ui/components/conn_waiting.lua
+++ b/pocket/ui/components/conn_waiting.lua
@@ -11,9 +11,9 @@ local TextBox = require("graphics.elements.textbox")
local WaitingAnim = require("graphics.elements.animations.waiting")
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-local cpair = core.graphics.cpair
+local cpair = core.cpair
-- create a waiting view
---@param parent graphics_element parent
diff --git a/pocket/ui/components/home_page.lua b/pocket/ui/components/home_page.lua
index 5287cac..a31cae8 100644
--- a/pocket/ui/components/home_page.lua
+++ b/pocket/ui/components/home_page.lua
@@ -5,9 +5,9 @@ local core = require("graphics.core")
local Div = require("graphics.elements.div")
local TextBox = require("graphics.elements.textbox")
--- local cpair = core.graphics.cpair
+-- local cpair = core.cpair
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-- new home page view
---@param root graphics_element parent
diff --git a/pocket/ui/components/reactor_page.lua b/pocket/ui/components/reactor_page.lua
index 50b1939..c7a2e96 100644
--- a/pocket/ui/components/reactor_page.lua
+++ b/pocket/ui/components/reactor_page.lua
@@ -5,9 +5,9 @@ local core = require("graphics.core")
local Div = require("graphics.elements.div")
local TextBox = require("graphics.elements.textbox")
--- local cpair = core.graphics.cpair
+-- local cpair = core.cpair
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-- new reactor page view
---@param root graphics_element parent
diff --git a/pocket/ui/components/turbine_page.lua b/pocket/ui/components/turbine_page.lua
index 9fd7af5..527e419 100644
--- a/pocket/ui/components/turbine_page.lua
+++ b/pocket/ui/components/turbine_page.lua
@@ -5,9 +5,9 @@ local core = require("graphics.core")
local Div = require("graphics.elements.div")
local TextBox = require("graphics.elements.textbox")
--- local cpair = core.graphics.cpair
+-- local cpair = core.cpair
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-- new turbine page view
---@param root graphics_element parent
diff --git a/pocket/ui/components/unit_page.lua b/pocket/ui/components/unit_page.lua
index 2e24df3..a0718e6 100644
--- a/pocket/ui/components/unit_page.lua
+++ b/pocket/ui/components/unit_page.lua
@@ -5,9 +5,9 @@ local core = require("graphics.core")
local Div = require("graphics.elements.div")
local TextBox = require("graphics.elements.textbox")
--- local cpair = core.graphics.cpair
+-- local cpair = core.cpair
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-- new unit page view
---@param root graphics_element parent
diff --git a/pocket/ui/main.lua b/pocket/ui/main.lua
index 5af9ce8..1b7be88 100644
--- a/pocket/ui/main.lua
+++ b/pocket/ui/main.lua
@@ -22,9 +22,9 @@ local TextBox = require("graphics.elements.textbox")
local Sidebar = require("graphics.elements.controls.sidebar")
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-local cpair = core.graphics.cpair
+local cpair = core.cpair
-- create new main view
---@param main graphics_element main displaybox
diff --git a/pocket/ui/style.lua b/pocket/ui/style.lua
index b9a09fc..2fb7526 100644
--- a/pocket/ui/style.lua
+++ b/pocket/ui/style.lua
@@ -6,7 +6,7 @@ local core = require("graphics.core")
local style = {}
-local cpair = core.graphics.cpair
+local cpair = core.cpair
-- GLOBAL --
diff --git a/reactor-plc/panel/front_panel.lua b/reactor-plc/panel/front_panel.lua
index 70c80ef..c000a3d 100644
--- a/reactor-plc/panel/front_panel.lua
+++ b/reactor-plc/panel/front_panel.lua
@@ -22,10 +22,10 @@ local LED = require("graphics.elements.indicators.led")
local LEDPair = require("graphics.elements.indicators.ledpair")
local RGBLED = require("graphics.elements.indicators.ledrgb")
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-local cpair = core.graphics.cpair
-local border = core.graphics.border
+local cpair = core.cpair
+local border = core.border
-- create new main view
---@param panel graphics_element main displaybox
diff --git a/reactor-plc/panel/style.lua b/reactor-plc/panel/style.lua
index 31039d4..996453c 100644
--- a/reactor-plc/panel/style.lua
+++ b/reactor-plc/panel/style.lua
@@ -6,7 +6,7 @@ local core = require("graphics.core")
local style = {}
-local cpair = core.graphics.cpair
+local cpair = core.cpair
-- GLOBAL --
diff --git a/reactor-plc/renderer.lua b/reactor-plc/renderer.lua
index 0700ebe..9830751 100644
--- a/reactor-plc/renderer.lua
+++ b/reactor-plc/renderer.lua
@@ -70,9 +70,9 @@ end
function renderer.ui_ready() return ui.display ~= nil end
-- handle a mouse event
----@param event mouse_interaction
+---@param event mouse_interaction|nil
function renderer.handle_mouse(event)
- if ui.display ~= nil then
+ if ui.display ~= nil and event ~= nil then
ui.display.handle_mouse(event)
end
end
diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua
index 680fdee..183fbf3 100644
--- a/reactor-plc/startup.lua
+++ b/reactor-plc/startup.lua
@@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc")
local renderer = require("reactor-plc.renderer")
local threads = require("reactor-plc.threads")
-local R_PLC_VERSION = "v1.1.17"
+local R_PLC_VERSION = "v1.2.0"
local println = util.println
local println_ts = util.println_ts
diff --git a/reactor-plc/threads.lua b/reactor-plc/threads.lua
index 8470430..8dbea6d 100644
--- a/reactor-plc/threads.lua
+++ b/reactor-plc/threads.lua
@@ -257,9 +257,9 @@ function threads.thread__main(smem, init)
-- update indicators
databus.tx_hw_status(plc_state)
- elseif event == "mouse_click" then
- -- handle a monitor touch event
- renderer.handle_mouse(core.events.click(param1, param2, param3))
+ elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then
+ -- handle a mouse event
+ renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
elseif event == "clock_start" then
-- start loop clock
loop_clock.start()
diff --git a/rtu/panel/front_panel.lua b/rtu/panel/front_panel.lua
index f6014da..8dcaa1f 100644
--- a/rtu/panel/front_panel.lua
+++ b/rtu/panel/front_panel.lua
@@ -16,9 +16,9 @@ local TextBox = require("graphics.elements.textbox")
local LED = require("graphics.elements.indicators.led")
local RGBLED = require("graphics.elements.indicators.ledrgb")
-local TEXT_ALIGN = core.graphics.TEXT_ALIGN
+local TEXT_ALIGN = core.TEXT_ALIGN
-local cpair = core.graphics.cpair
+local cpair = core.cpair
local UNIT_TYPE_LABELS = {
"UNKNOWN",
diff --git a/rtu/panel/style.lua b/rtu/panel/style.lua
index 31039d4..996453c 100644
--- a/rtu/panel/style.lua
+++ b/rtu/panel/style.lua
@@ -6,7 +6,7 @@ local core = require("graphics.core")
local style = {}
-local cpair = core.graphics.cpair
+local cpair = core.cpair
-- GLOBAL --
diff --git a/rtu/renderer.lua b/rtu/renderer.lua
index f79f19e..490959c 100644
--- a/rtu/renderer.lua
+++ b/rtu/renderer.lua
@@ -71,9 +71,9 @@ end
function renderer.ui_ready() return ui.display ~= nil end
-- handle a mouse event
----@param event mouse_interaction
+---@param event mouse_interaction|nil
function renderer.handle_mouse(event)
- if ui.display ~= nil then
+ if ui.display ~= nil and event ~= nil then
ui.display.handle_mouse(event)
end
end
diff --git a/rtu/startup.lua b/rtu/startup.lua
index 3038310..0693f9f 100644
--- a/rtu/startup.lua
+++ b/rtu/startup.lua
@@ -28,7 +28,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
local sps_rtu = require("rtu.dev.sps_rtu")
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
-local RTU_VERSION = "v1.0.5"
+local RTU_VERSION = "v1.1.0"
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
diff --git a/rtu/threads.lua b/rtu/threads.lua
index 9dac9f1..046525c 100644
--- a/rtu/threads.lua
+++ b/rtu/threads.lua
@@ -229,9 +229,9 @@ function threads.thread__main(smem)
end
end
end
- elseif event == "mouse_click" then
- -- handle a monitor touch event
- renderer.handle_mouse(core.events.click(param1, param2, param3))
+ elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then
+ -- handle a mouse event
+ renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
end
-- check for termination request
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