2022-06-14 16:02:42 +00:00
|
|
|
--
|
2023-05-07 01:27:36 +00:00
|
|
|
-- Graphics Core Types, Checks, and Constructors
|
2022-06-14 16:02:42 +00:00
|
|
|
--
|
|
|
|
|
2023-05-10 00:29:07 +00:00
|
|
|
local events = require("graphics.events")
|
|
|
|
local flasher = require("graphics.flasher")
|
2023-04-09 01:33:54 +00:00
|
|
|
|
2023-05-10 00:29:07 +00:00
|
|
|
local core = {}
|
2022-06-06 19:42:39 +00:00
|
|
|
|
2023-09-17 01:06:16 +00:00
|
|
|
core.version = "2.0.0"
|
2023-06-18 04:40:01 +00:00
|
|
|
|
2023-05-10 00:29:07 +00:00
|
|
|
core.flasher = flasher
|
2022-06-06 19:42:39 +00:00
|
|
|
core.events = events
|
|
|
|
|
2023-05-07 01:27:36 +00:00
|
|
|
-- Core Types
|
2022-06-06 19:42:39 +00:00
|
|
|
|
2023-02-25 00:50:01 +00:00
|
|
|
---@enum TEXT_ALIGN
|
2023-09-17 01:06:16 +00:00
|
|
|
core.TEXT_ALIGN = { LEFT = 1, CENTER = 2, RIGHT = 3 }
|
2022-06-06 19:42:39 +00:00
|
|
|
|
2022-06-08 16:27:28 +00:00
|
|
|
---@class graphics_border
|
|
|
|
---@field width integer
|
|
|
|
---@field color color
|
2022-06-08 17:08:48 +00:00
|
|
|
---@field even boolean
|
2022-06-08 16:27:28 +00:00
|
|
|
|
2022-07-28 14:09:34 +00:00
|
|
|
---@alias element_id string|integer
|
|
|
|
|
2022-06-08 17:08:48 +00:00
|
|
|
-- create a new border definition
|
2023-02-25 00:50:01 +00:00
|
|
|
---@nodiscard
|
2022-06-08 17:08:48 +00:00
|
|
|
---@param width integer border width
|
|
|
|
---@param color color border color
|
2022-06-16 15:31:52 +00:00
|
|
|
---@param even? boolean whether to pad width extra to account for rectangular pixels, defaults to false
|
2022-06-08 16:27:28 +00:00
|
|
|
---@return graphics_border
|
2023-05-07 01:27:36 +00:00
|
|
|
function core.border(width, color, even)
|
2023-08-31 01:11:57 +00:00
|
|
|
return { width = width, color = color, even = even or false }
|
2022-06-08 16:27:28 +00:00
|
|
|
end
|
|
|
|
|
2022-06-06 19:42:39 +00:00
|
|
|
---@class graphics_frame
|
|
|
|
---@field x integer
|
|
|
|
---@field y integer
|
|
|
|
---@field w integer
|
|
|
|
---@field h integer
|
|
|
|
|
2022-06-08 17:08:48 +00:00
|
|
|
-- create a new graphics frame definition
|
2023-02-25 00:50:01 +00:00
|
|
|
---@nodiscard
|
2022-06-06 19:42:39 +00:00
|
|
|
---@param x integer
|
|
|
|
---@param y integer
|
|
|
|
---@param w integer
|
|
|
|
---@param h integer
|
|
|
|
---@return graphics_frame
|
2023-05-07 01:27:36 +00:00
|
|
|
function core.gframe(x, y, w, h)
|
2023-08-31 01:11:57 +00:00
|
|
|
return { x = x, y = y, w = w, h = h }
|
2022-06-06 19:42:39 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
---@class cpair
|
2022-06-08 17:08:48 +00:00
|
|
|
---@field color_a color
|
|
|
|
---@field color_b color
|
|
|
|
---@field blit_a string
|
|
|
|
---@field blit_b string
|
2022-06-06 19:42:39 +00:00
|
|
|
---@field fgd color
|
|
|
|
---@field bkg color
|
|
|
|
---@field blit_fgd string
|
|
|
|
---@field blit_bkg string
|
|
|
|
|
2022-06-08 17:08:48 +00:00
|
|
|
-- create a new color pair definition
|
2023-02-25 00:50:01 +00:00
|
|
|
---@nodiscard
|
2022-06-08 17:08:48 +00:00
|
|
|
---@param a color
|
|
|
|
---@param b color
|
2022-06-06 19:42:39 +00:00
|
|
|
---@return cpair
|
2023-05-07 01:27:36 +00:00
|
|
|
function core.cpair(a, b)
|
2022-06-06 19:42:39 +00:00
|
|
|
return {
|
2022-06-08 17:08:48 +00:00
|
|
|
-- color pairs
|
2023-09-17 01:06:16 +00:00
|
|
|
color_a = a, color_b = b, blit_a = colors.toBlit(a), blit_b = colors.toBlit(b),
|
2022-06-08 17:08:48 +00:00
|
|
|
-- aliases
|
2023-09-17 01:06:16 +00:00
|
|
|
fgd = a, bkg = b, blit_fgd = colors.toBlit(a), blit_bkg = colors.toBlit(b)
|
2022-06-06 19:42:39 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2022-06-29 21:40:08 +00:00
|
|
|
---@class pipe
|
|
|
|
---@field x1 integer starting x, origin is 0
|
|
|
|
---@field y1 integer starting y, origin is 0
|
|
|
|
---@field x2 integer ending x, origin is 0
|
|
|
|
---@field y2 integer ending y, origin is 0
|
|
|
|
---@field w integer width
|
|
|
|
---@field h integer height
|
|
|
|
---@field color color pipe color
|
2022-07-28 14:09:34 +00:00
|
|
|
---@field thin boolean true for 1 subpixel, false (default) for 2
|
2022-06-29 21:40:08 +00:00
|
|
|
---@field align_tr boolean false to align bottom left (default), true to align top right
|
|
|
|
|
2023-02-25 00:50:01 +00:00
|
|
|
-- create a new pipe<br>
|
2022-06-29 21:40:08 +00:00
|
|
|
-- note: pipe coordinate origin is (0, 0)
|
2023-02-25 00:50:01 +00:00
|
|
|
---@nodiscard
|
2022-06-29 21:40:08 +00:00
|
|
|
---@param x1 integer starting x, origin is 0
|
|
|
|
---@param y1 integer starting y, origin is 0
|
|
|
|
---@param x2 integer ending x, origin is 0
|
|
|
|
---@param y2 integer ending y, origin is 0
|
|
|
|
---@param color color pipe color
|
2022-07-28 14:09:34 +00:00
|
|
|
---@param thin? boolean true for 1 subpixel, false (default) for 2
|
2022-06-29 21:40:08 +00:00
|
|
|
---@param align_tr? boolean false to align bottom left (default), true to align top right
|
|
|
|
---@return pipe
|
2023-05-07 01:27:36 +00:00
|
|
|
function core.pipe(x1, y1, x2, y2, color, thin, align_tr)
|
2022-06-29 21:40:08 +00:00
|
|
|
return {
|
|
|
|
x1 = x1,
|
|
|
|
y1 = y1,
|
|
|
|
x2 = x2,
|
|
|
|
y2 = y2,
|
2022-07-02 19:08:24 +00:00
|
|
|
w = math.abs(x2 - x1) + 1,
|
|
|
|
h = math.abs(y2 - y1) + 1,
|
2022-06-29 21:40:08 +00:00
|
|
|
color = color,
|
|
|
|
thin = thin or false,
|
|
|
|
align_tr = align_tr or false
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2023-09-23 16:49:31 +00:00
|
|
|
-- Interactive Field Manager
|
|
|
|
|
|
|
|
---@param e graphics_base
|
|
|
|
---@param max_len any
|
|
|
|
---@param fg_bg any
|
|
|
|
---@param dis_fg_bg any
|
|
|
|
function core.new_ifield(e, max_len, fg_bg, dis_fg_bg)
|
|
|
|
local self = {
|
|
|
|
frame_start = 1,
|
|
|
|
visible_text = e.value,
|
|
|
|
cursor_pos = string.len(e.value) + 1,
|
|
|
|
selected_all = false
|
|
|
|
}
|
|
|
|
|
|
|
|
-- update visible text
|
|
|
|
local function _update_visible()
|
|
|
|
self.visible_text = string.sub(e.value, self.frame_start, self.frame_start + math.min(string.len(e.value), e.frame.w) - 1)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- try shifting frame left
|
|
|
|
local function _try_lshift()
|
|
|
|
if self.frame_start > 1 then
|
|
|
|
self.frame_start = self.frame_start - 1
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- try shifting frame right
|
|
|
|
local function _try_rshift()
|
2023-09-23 17:45:00 +00:00
|
|
|
if (self.frame_start + e.frame.w - 1) <= string.len(e.value) then
|
2023-09-23 16:49:31 +00:00
|
|
|
self.frame_start = self.frame_start + 1
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
---@class ifield
|
|
|
|
local public = {}
|
|
|
|
|
|
|
|
-- show the field
|
|
|
|
function public.show()
|
|
|
|
_update_visible()
|
|
|
|
|
|
|
|
if e.enabled then
|
|
|
|
e.w_set_bkg(fg_bg.bkg)
|
|
|
|
e.w_set_fgd(fg_bg.fgd)
|
|
|
|
else
|
|
|
|
e.w_set_bkg(dis_fg_bg.bkg)
|
|
|
|
e.w_set_fgd(dis_fg_bg.fgd)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- clear and print
|
|
|
|
e.w_set_cur(1, 1)
|
|
|
|
e.w_write(string.rep(" ", e.frame.w))
|
|
|
|
e.w_set_cur(1, 1)
|
|
|
|
|
|
|
|
if e.is_focused() and e.enabled then
|
|
|
|
-- write text with cursor
|
|
|
|
if self.selected_all then
|
|
|
|
e.w_set_bkg(fg_bg.fgd)
|
|
|
|
e.w_set_fgd(fg_bg.bkg)
|
|
|
|
e.w_write(self.visible_text)
|
2023-09-23 17:45:00 +00:00
|
|
|
elseif self.cursor_pos >= (string.len(self.visible_text) + 1) then
|
2023-09-23 16:49:31 +00:00
|
|
|
-- write text with cursor at the end, no need to blit
|
|
|
|
e.w_write(self.visible_text)
|
|
|
|
e.w_set_fgd(colors.lightGray)
|
|
|
|
e.w_write("_")
|
|
|
|
else
|
|
|
|
local a, b = "", ""
|
|
|
|
|
|
|
|
if self.cursor_pos <= string.len(self.visible_text) then
|
|
|
|
a = fg_bg.blit_bkg
|
|
|
|
b = fg_bg.blit_fgd
|
|
|
|
end
|
|
|
|
|
|
|
|
local b_fgd = string.rep(fg_bg.blit_fgd, self.cursor_pos - 1) .. a .. string.rep(fg_bg.blit_fgd, string.len(self.visible_text) - self.cursor_pos)
|
|
|
|
local b_bkg = string.rep(fg_bg.blit_bkg, self.cursor_pos - 1) .. b .. string.rep(fg_bg.blit_bkg, string.len(self.visible_text) - self.cursor_pos)
|
|
|
|
|
|
|
|
e.w_blit(self.visible_text, b_fgd, b_bkg)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
self.selected_all = false
|
|
|
|
|
|
|
|
-- write text without cursor
|
|
|
|
e.w_write(self.visible_text)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- move cursor to x
|
|
|
|
---@param x integer
|
|
|
|
function public.move_cursor(x)
|
|
|
|
self.selected_all = false
|
|
|
|
self.cursor_pos = math.min(x, string.len(self.visible_text) + 1)
|
|
|
|
public.show()
|
|
|
|
end
|
|
|
|
|
|
|
|
-- select all text
|
|
|
|
function public.select_all()
|
|
|
|
self.selected_all = true
|
|
|
|
public.show()
|
|
|
|
end
|
|
|
|
|
|
|
|
-- set field value
|
|
|
|
---@param val string
|
|
|
|
function public.set_value(val)
|
|
|
|
e.value = string.sub(val, 1, math.min(max_len, string.len(val)))
|
|
|
|
|
|
|
|
self.selected_all = false
|
2023-09-23 17:45:00 +00:00
|
|
|
self.frame_start = math.max(1, string.len(e.value) - e.frame.w + 2)
|
2023-09-23 16:49:31 +00:00
|
|
|
|
|
|
|
_update_visible()
|
|
|
|
self.cursor_pos = string.len(self.visible_text) + 1
|
|
|
|
|
|
|
|
public.show()
|
|
|
|
end
|
|
|
|
|
|
|
|
-- try to insert a character if there is space
|
|
|
|
---@param char string
|
|
|
|
function public.try_insert_char(char)
|
|
|
|
-- limit length
|
|
|
|
if string.len(e.value) >= max_len then return end
|
|
|
|
|
|
|
|
-- replace if selected all, insert otherwise
|
|
|
|
if self.selected_all then
|
|
|
|
self.selected_all = false
|
|
|
|
self.cursor_pos = 2
|
|
|
|
self.frame_start = 1
|
|
|
|
|
|
|
|
e.value = char
|
|
|
|
public.show()
|
|
|
|
else
|
|
|
|
e.value = string.sub(e.value, 1, self.frame_start + self.cursor_pos - 2) .. char .. string.sub(e.value, self.frame_start + self.cursor_pos - 1, string.len(e.value))
|
|
|
|
_update_visible()
|
2023-09-23 17:45:00 +00:00
|
|
|
public.nav_right()
|
2023-09-23 16:49:31 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- remove charcter before cursor if there is anything to remove, or delete all if selected all
|
|
|
|
function public.backspace()
|
|
|
|
if self.selected_all then
|
|
|
|
self.selected_all = false
|
|
|
|
e.value = ""
|
|
|
|
self.cursor_pos = 1
|
|
|
|
self.frame_start = 1
|
|
|
|
public.show()
|
|
|
|
else
|
|
|
|
if self.frame_start + self.cursor_pos > 2 then
|
|
|
|
e.value = string.sub(e.value, 1, self.frame_start + self.cursor_pos - 3) .. string.sub(e.value, self.frame_start + self.cursor_pos - 1, string.len(e.value))
|
|
|
|
if self.cursor_pos > 1 then
|
|
|
|
self.cursor_pos = self.cursor_pos - 1
|
|
|
|
public.show()
|
|
|
|
elseif _try_lshift() then public.show() end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- move cursor left by one
|
|
|
|
function public.nav_left()
|
|
|
|
if self.cursor_pos > 1 then
|
|
|
|
self.cursor_pos = self.cursor_pos - 1
|
|
|
|
public.show()
|
|
|
|
elseif _try_lshift() then public.show() end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- move cursor right by one
|
|
|
|
function public.nav_right()
|
2023-09-23 17:45:00 +00:00
|
|
|
if self.cursor_pos < math.min(string.len(self.visible_text) + 1, e.frame.w) then
|
2023-09-23 16:49:31 +00:00
|
|
|
self.cursor_pos = self.cursor_pos + 1
|
|
|
|
public.show()
|
|
|
|
elseif _try_rshift() then public.show() end
|
|
|
|
end
|
|
|
|
|
|
|
|
return public
|
|
|
|
end
|
|
|
|
|
2022-06-06 19:42:39 +00:00
|
|
|
return core
|