-- Rectangle Graphics Element local util = require("scada-common.util") local element = require("graphics.element") ---@class rectangle_args ---@field border? graphics_border ---@field thin? boolean true to use extra thin even borders ---@field even_inner? boolean true to make the inner area of a border even ---@field parent graphics_element ---@field id? string element id ---@field x? integer 1 if omitted ---@field y? integer auto incremented if omitted ---@field width? integer parent width if omitted ---@field height? integer parent height if omitted ---@field gframe? graphics_frame frame instead of x/y/width/height ---@field fg_bg? cpair foreground/background colors ---@field hidden? boolean true to hide on initial draw -- new rectangle ---@param args rectangle_args ---@return graphics_element element, element_id id local function rectangle(args) element.assert(args.border ~= nil or args.thin ~= true, "thin requires border to be provided") -- if thin, then width will always need to be 1 if args.thin == true then args.border.width = 1 args.border.even = true end -- offset children local offset_x = 0 local offset_y = 0 if args.border ~= nil then offset_x = args.border.width offset_y = args.border.width -- slightly different y offset if the border is set to even if args.border.even then local width_x2 = (2 * args.border.width) offset_y = math.floor(width_x2 / 3) + util.trinary(width_x2 % 3 > 0, 1, 0) end end -- create new graphics element base object local e = element.new(args, nil, offset_x, offset_y) -- create content window for child elements e.content_window = window.create(e.window, 1 + offset_x, 1 + offset_y, e.frame.w - (2 * offset_x), e.frame.h - (2 * offset_y)) e.content_window.setBackgroundColor(e.fg_bg.bkg) e.content_window.setTextColor(e.fg_bg.fgd) e.content_window.clear() -- draw bordered box if requested -- element constructor will have drawn basic colored rectangle regardless if args.border ~= nil then e.w_set_cur(1, 1) local border_width = offset_x local border_height = offset_y local border_blit = colors.toBlit(args.border.color) local width_x2 = border_width * 2 local inner_width = e.frame.w - width_x2 -- check dimensions element.assert(width_x2 <= e.frame.w, "border too thick for width") element.assert(width_x2 <= e.frame.h, "border too thick for height") -- form the basic line strings and top/bottom blit strings local spaces = util.spaces(e.frame.w) local blit_fg = string.rep(e.fg_bg.blit_fgd, e.frame.w) local blit_fg_sides = blit_fg local blit_bg_sides = "" local blit_bg_top_bot = string.rep(border_blit, e.frame.w) -- partial bars local p_a, p_b, p_s if args.thin == true then if args.even_inner == true then p_a = "\x9c" .. string.rep("\x8c", inner_width) .. "\x93" p_b = "\x8d" .. string.rep("\x8c", inner_width) .. "\x8e" else p_a = "\x97" .. string.rep("\x83", inner_width) .. "\x94" p_b = "\x8a" .. string.rep("\x8f", inner_width) .. "\x85" end p_s = "\x95" .. util.spaces(inner_width) .. "\x95" else if args.even_inner == true then p_a = string.rep("\x83", inner_width + width_x2) p_b = string.rep("\x8f", inner_width + width_x2) else p_a = util.spaces(border_width) .. string.rep("\x8f", inner_width) .. util.spaces(border_width) p_b = util.spaces(border_width) .. string.rep("\x83", inner_width) .. util.spaces(border_width) end p_s = spaces end local p_inv_fg = string.rep(border_blit, border_width) .. string.rep(e.fg_bg.blit_bkg, inner_width) .. string.rep(border_blit, border_width) local p_inv_bg = string.rep(e.fg_bg.blit_bkg, border_width) .. string.rep(border_blit, inner_width) .. string.rep(e.fg_bg.blit_bkg, border_width) if args.thin == true then p_inv_fg = e.fg_bg.blit_bkg .. string.rep(e.fg_bg.blit_bkg, inner_width) .. string.rep(border_blit, border_width) p_inv_bg = border_blit .. string.rep(border_blit, inner_width) .. string.rep(e.fg_bg.blit_bkg, border_width) blit_fg_sides = border_blit .. string.rep(e.fg_bg.blit_bkg, inner_width) .. e.fg_bg.blit_bkg end -- form the body blit strings (sides are border, inside is normal) for x = 1, e.frame.w do -- edges get border color, center gets normal if x <= border_width or x > (e.frame.w - border_width) then if args.thin and x == 1 then blit_bg_sides = blit_bg_sides .. e.fg_bg.blit_bkg else blit_bg_sides = blit_bg_sides .. border_blit end else blit_bg_sides = blit_bg_sides .. e.fg_bg.blit_bkg end end -- draw rectangle with borders function e.redraw() for y = 1, e.frame.h do e.w_set_cur(1, y) -- top border if y <= border_height then -- partial pixel fill if args.border.even and y == border_height then if args.thin == true then e.w_blit(p_a, p_inv_bg, p_inv_fg) else local _fg = util.trinary(args.even_inner == true, string.rep(e.fg_bg.blit_bkg, e.frame.w), p_inv_bg) local _bg = util.trinary(args.even_inner == true, blit_bg_top_bot, p_inv_fg) if width_x2 % 3 == 1 then e.w_blit(p_b, _fg, _bg) elseif width_x2 % 3 == 2 then e.w_blit(p_a, _fg, _bg) else -- skip line e.w_blit(spaces, blit_fg, blit_bg_sides) end end else e.w_blit(spaces, blit_fg, blit_bg_top_bot) end -- bottom border elseif y > (e.frame.h - border_width) then -- partial pixel fill if args.border.even and y == ((e.frame.h - border_width) + 1) then if args.thin == true then if args.even_inner == true then e.w_blit(p_b, blit_bg_top_bot, string.rep(e.fg_bg.blit_bkg, e.frame.w)) else e.w_blit(p_b, string.rep(e.fg_bg.blit_bkg, e.frame.w), blit_bg_top_bot) end else local _fg = util.trinary(args.even_inner == true, blit_bg_top_bot, p_inv_fg) local _bg = util.trinary(args.even_inner == true, string.rep(e.fg_bg.blit_bkg, e.frame.w), blit_bg_top_bot) if width_x2 % 3 == 1 then e.w_blit(p_a, _fg, _bg) elseif width_x2 % 3 == 2 then e.w_blit(p_b, _fg, _bg) else -- skip line e.w_blit(spaces, blit_fg, blit_bg_sides) end end else e.w_blit(spaces, blit_fg, blit_bg_top_bot) end else if args.thin == true then e.w_blit(p_s, blit_fg_sides, blit_bg_sides) else e.w_blit(p_s, blit_fg, blit_bg_sides) end end end end -- initial draw of border e.redraw() end return e.complete() end return rectangle