improved number field precision handling and limited decimal precision of timeouts #396

This commit is contained in:
Mikayla Fischler
2023-12-30 14:41:03 -05:00
parent 2a85a438ba
commit 42cd9fff0c
6 changed files with 74 additions and 27 deletions

View File

@ -7,7 +7,7 @@ local flasher = require("graphics.flasher")
local core = {}
core.version = "2.0.9"
core.version = "2.1.0"
core.flasher = flasher
core.events = events

View File

@ -1,5 +1,7 @@
-- Numeric Value Entry Graphics Element
local util = require("scada-common.util")
local core = require("graphics.core")
local element = require("graphics.element")
@ -8,9 +10,11 @@ local MOUSE_CLICK = core.events.MOUSE_CLICK
---@class number_field_args
---@field default? number default value, defaults to 0
---@field min? number minimum, forced on unfocus
---@field max? number maximum, forced on unfocus
---@field max_digits? integer maximum number of digits, defaults to width
---@field min? number minimum, enforced on unfocus
---@field max? number maximum, enforced on unfocus
---@field max_chars? integer maximum number of characters, defaults to width
---@field max_int_digits? integer maximum number of integer digits, enforced on unfocus
---@field max_frac_digits? integer maximum number of fractional digits, enforced on unfocus
---@field allow_decimal? boolean true to allow decimals
---@field allow_negative? boolean true to allow negative numbers
---@field dis_fg_bg? cpair foreground/background colors when disabled
@ -26,6 +30,9 @@ local MOUSE_CLICK = core.events.MOUSE_CLICK
---@param args number_field_args
---@return graphics_element element, element_id id
local function number_field(args)
element.assert(args.max_int_digits == nil or (util.is_int(args.max_int_digits) and args.max_int_digits > 0), "max_int_digits must be an integer greater than zero if supplied")
element.assert(args.max_frac_digits == nil or (util.is_int(args.max_frac_digits) and args.max_frac_digits > 0), "max_frac_digits must be an integer greater than zero if supplied")
args.height = 1
args.can_focus = true
@ -34,13 +41,13 @@ local function number_field(args)
local has_decimal = false
args.max_digits = args.max_digits or e.frame.w
args.max_chars = args.max_chars or e.frame.w
-- set initial value
e.value = "" .. (args.default or 0)
-- make an interactive field manager
local ifield = core.new_ifield(e, args.max_digits, args.fg_bg, args.dis_fg_bg)
local ifield = core.new_ifield(e, args.max_chars, args.fg_bg, args.dis_fg_bg)
-- handle mouse interaction
---@param event mouse_interaction mouse event
@ -62,7 +69,7 @@ local function number_field(args)
-- handle keyboard interaction
---@param event key_interaction key event
function e.handle_key(event)
if event.type == KEY_CLICK.CHAR and string.len(e.value) < args.max_digits then
if event.type == KEY_CLICK.CHAR and string.len(e.value) < args.max_chars then
if tonumber(event.name) then
if e.value == 0 then e.value = "" end
ifield.try_insert_char(event.name)
@ -127,6 +134,37 @@ local function number_field(args)
local min = tonumber(args.min)
if type(val) == "number" then
if util.is_int(args.max_int_digits) or args.max_frac_digits then
local str = e.value
local ceil = false
if string.find(str, "-") then str = string.sub(e.value, 2) end
local parts = util.strtok(str, ".")
if parts[1] and args.max_int_digits then
if string.len(parts[1]) > args.max_int_digits then
parts[1] = string.rep("9", args.max_int_digits)
ceil = true
end
end
if args.allow_decimal and args.max_frac_digits then
if ceil then
parts[2] = string.rep("9", args.max_frac_digits)
elseif parts[2] and (string.len(parts[2]) > args.max_frac_digits) then
-- add a half of the highest precision fractional value in order to round using floor
local scaled = math.fmod(val, 1) * (10 ^ (args.max_frac_digits))
local value = math.floor(scaled + 0.5)
local unscaled = value * (10 ^ (-args.max_frac_digits))
parts[2] = string.sub(tostring(unscaled), 3) -- remove starting "0."
end
end
if parts[2] then parts[2] = "." .. parts[2] else parts[2] = "" end
val = tonumber((parts[1] or "") .. parts[2])
end
if type(args.max) == "number" and val > max then
e.value = "" .. max
ifield.nav_start()