From fd9244d4ebd549e8b4d3de055e3cf378f9142596 Mon Sep 17 00:00:00 2001 From: petrucci4prez Date: Thu, 11 Aug 2016 23:50:45 -0400 Subject: [PATCH] Squashed 'core/' content from commit 455d15f git-subtree-dir: core git-subtree-split: 455d15f24a60bad013d07712457318e45885de75 --- func/json.lua | 245 ++++++++ func/util.lua | 176 ++++++ super/Color.lua | 55 ++ super/Gradient.lua | 20 + super/Super.lua | 58 ++ super/Widget.lua | 1112 ++++++++++++++++++++++++++++++++++ widget/CR.lua | 5 + widget/arc/Arc.lua | 31 + widget/arc/CompoundDial.lua | 34 ++ widget/arc/Dial.lua | 30 + widget/image/Image.lua | 31 + widget/image/ScaledImage.lua | 31 + widget/plot/LabelPlot.lua | 110 ++++ widget/plot/Plot.lua | 147 +++++ widget/plot/ScalePlot.lua | 89 +++ widget/poly/Bar.lua | 36 ++ widget/poly/CompoundBar.lua | 34 ++ widget/poly/Line.lua | 19 + widget/poly/Poly.lua | 39 ++ widget/rect/FillRect.lua | 24 + widget/rect/Rect.lua | 19 + widget/text/CriticalText.lua | 29 + widget/text/Table.lua | 73 +++ widget/text/Text.lua | 84 +++ widget/text/TextColumn.lua | 38 ++ 25 files changed, 2569 insertions(+) create mode 100644 func/json.lua create mode 100644 func/util.lua create mode 100644 super/Color.lua create mode 100644 super/Gradient.lua create mode 100644 super/Super.lua create mode 100644 super/Widget.lua create mode 100644 widget/CR.lua create mode 100644 widget/arc/Arc.lua create mode 100644 widget/arc/CompoundDial.lua create mode 100644 widget/arc/Dial.lua create mode 100644 widget/image/Image.lua create mode 100644 widget/image/ScaledImage.lua create mode 100644 widget/plot/LabelPlot.lua create mode 100644 widget/plot/Plot.lua create mode 100644 widget/plot/ScalePlot.lua create mode 100644 widget/poly/Bar.lua create mode 100644 widget/poly/CompoundBar.lua create mode 100644 widget/poly/Line.lua create mode 100644 widget/poly/Poly.lua create mode 100644 widget/rect/FillRect.lua create mode 100644 widget/rect/Rect.lua create mode 100644 widget/text/CriticalText.lua create mode 100644 widget/text/Table.lua create mode 100644 widget/text/Text.lua create mode 100644 widget/text/TextColumn.lua diff --git a/func/json.lua b/func/json.lua new file mode 100644 index 0000000..88acde2 --- /dev/null +++ b/func/json.lua @@ -0,0 +1,245 @@ +local c = {} + +local _STRING_GSUB = string.gsub +local _STRING_CHAR = string.char +local _STRING_FIND = string.find +local _STRING_SUB = string.sub +local _TABLE_CONCAT = table.concat +local _MATH_FLOOR = math.floor +local _PAIRS = pairs +local _TONUMBER = tonumber + +local decode -- to ref this before definition + +local decode_scanWhitespace = function(s, startPos) + local whitespace = " \n\r\t" + local stringLen = #s + + while (_STRING_FIND(whitespace, _STRING_SUB(s, startPos, startPos), 1, true) and + startPos <= stringLen) do + startPos = startPos + 1 + end + return startPos +end + +local decode_scanArray = function(s, startPos) + local array = {} + local stringLen = #s + + --~ assert(_STRING_SUB(s, startPos, startPos) == '[', + --~ 'decode_scanArray called but array does not start at position ' .. startPos .. + --~ ' in string:\n'..s ) + + startPos = startPos + 1 + + repeat + startPos = decode_scanWhitespace(s, startPos) + --~ assert(startPos <= stringLen, 'JSON String ended unexpectedly scanning array.') + + local curChar = _STRING_SUB(s,startPos,startPos) + + if (curChar == ']') then + return array, startPos + 1 + end + + if (curChar == ',') then + startPos = decode_scanWhitespace(s, startPos + 1) + end + + --~ assert(startPos <= stringLen, 'JSON String ended unexpectedly scanning array.') + object, startPos = decode(s, startPos) + array[#array + 1] = object + until false +end + +local decode_scanComment = function(s, startPos) + --~ assert(_STRING_SUB(s, startPos, startPos + 1) == '/*', + --~ "decode_scanComment called but comment does not start at position " .. startPos) + + local endPos = _STRING_FIND(s, '*/', startPos + 2) + --~ assert(endPos ~= nil, "Unterminated comment in string at " .. startPos) + return endPos + 2 +end + +local decode_scanConstant = function(s, startPos) + local consts = {["true"] = true, ["false"] = false, ["null"] = nil} + local constNames = {"true", "false", "null"} + + for _, k in _PAIRS(constNames) do + if _STRING_SUB(s, startPos, startPos + #k - 1 ) == k then + return consts[k], startPos + #k + end + end + --~ assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. + --~ startPos) +end + +local decode_scanNumber = function(s, startPos) + local endPos = startPos + 1 + local stringLen = #s + local acceptableChars = "+-0123456789.e" + + while (_STRING_FIND(acceptableChars, _STRING_SUB(s, endPos, endPos), 1, true) + and endPos <= stringLen) do + endPos = endPos + 1 + end + + local numberString = _STRING_GSUB(_STRING_SUB(s, startPos, endPos - 1), '+', '') + return _TONUMBER(numberString), endPos +end + +local decode_scanObject = function(s, startPos) + local object = {} + local stringLen = #s + local key, value + + --~ assert(_STRING_SUB(s, startPos, startPos) == '{', + --~ 'decode_scanObject called but object does not start at position ' .. startPos .. + --~ ' in string:\n' .. s) + + startPos = startPos + 1 + + repeat + startPos = decode_scanWhitespace(s, startPos) + + --~ assert(startPos <= stringLen, 'JSON string ended unexpectedly while scanning object.') + + local curChar = _STRING_SUB(s, startPos, startPos) + + if (curChar == '}') then + return object, startPos + 1 + end + + if (curChar == ',') then + startPos = decode_scanWhitespace(s, startPos + 1) + end + + --~ assert(startPos <= stringLen, 'JSON string ended unexpectedly scanning object.') + + -- Scan the key + key, startPos = decode(s, startPos) + + --~ assert(startPos <= stringLen, + --~ 'JSON string ended unexpectedly searching for value of key ' .. key) + + startPos = decode_scanWhitespace(s, startPos) + + --~ assert(startPos <= stringLen, + --~ 'JSON string ended unexpectedly searching for value of key ' .. key) + + --~ assert(_STRING_SUB(s, startPos, startPos) == ':', + --~ 'JSON object key-value assignment mal-formed at ' .. startPos) + + startPos = decode_scanWhitespace(s, startPos + 1) + + --~ assert(startPos <= stringLen, + --~ 'JSON string ended unexpectedly searching for value of key ' .. key) + + value, startPos = decode(s, startPos) + + object[key] = value + until false +end + +local escapeSequences = { + ["\\t"] = "\t", + ["\\f"] = "\f", + ["\\r"] = "\r", + ["\\n"] = "\n", + ["\\b"] = "\b" +} + +setmetatable(escapeSequences, {__index = function(t, k) return _STRING_SUB(k, 2) end})--skip "\" + +local decode_scanString = function (s, startPos) + --~ assert(startPos, 'decode_scanString(..) called without start position') + + local startChar = _STRING_SUB(s, startPos, startPos) + + --~ assert(startChar == [["]] or startChar == [[']], + --~ 'decode_scanString called for a non-string') + + local t = {} + local i, j = startPos, startPos + + while _STRING_FIND(s, startChar, j + 1) ~= j + 1 do + local oldj = j + local x, y = _STRING_FIND(s, startChar, oldj + 1) + + i, j = _STRING_FIND(s, "\\.", j + 1) + + if not i or x < i then i, j = x, y - 1 end + + --~ table.insert(t, _STRING_SUB(s, oldj + 1, i - 1)) + t[#t + 1] = _STRING_SUB(s, oldj + 1, i - 1) + + if _STRING_SUB(s, i, j) == "\\u" then + local a = _STRING_SUB(s, j + 1, j + 4) + local n = _TONUMBER(a, 16) + local x + + j = j + 4 + + --~ assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. + --~ i .. " : " .. j) + + if n < 0x80 then + x = _STRING_CHAR(n % 0x80) + elseif n < 0x800 then + x = _STRING_CHAR(0xC0 + (_MATH_FLOOR(n / 64) % 0x20), 0x80 + (n % 0x40)) + else + x = _STRING_CHAR(0xE0 + (_MATH_FLOOR(n / 4096) % 0x10), 0x80 + + (_MATH_FLOOR(n / 64) % 0x40), 0x80 + (n % 0x40)) + end + + --~ table.insert(t, x) + t[#t + 1] = x + else + --~ table.insert(t, escapeSequences[_STRING_SUB(s, i, j)]) + t[#t + 1] = escapeSequences[_STRING_SUB(s, i, j)] + end + end + --~ table.insert(t, _STRING_SUB(j, j + 1)) + t[#t + 1] = _STRING_SUB(j, j + 1) + + --~ assert(_STRING_FIND(s, startChar, j + 1), "String decoding failed: missing closing " .. + --~ startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")") + + return _TABLE_CONCAT(t, ""), j + 2 +end + +decode = function(s, startPos) + startPos = startPos or 1 + startPos = decode_scanWhitespace(s, startPos) + + --~ assert(startPos <= #s, + --~ 'Unterminated JSON encoded object found at position in [' .. s .. ']') + + local curChar = _STRING_SUB(s, startPos, startPos) + + if curChar == '{' then + return decode_scanObject(s, startPos) + end + + if curChar == '[' then + return decode_scanArray(s, startPos) + end + + if _STRING_FIND("+-0123456789.e", curChar, 1, true) then + return decode_scanNumber(s, startPos) + end + + if curChar == [["]] or curChar == [[']] then + return decode_scanString(s, startPos) + end + + if _STRING_SUB(s, startPos, startPos + 1) == '/*' then + return decode(s, decode_scanComment(s, startPos)) + end + + return decode_scanConstant(s, startPos) +end + +c.decode = decode + +return c diff --git a/func/util.lua b/func/util.lua new file mode 100644 index 0000000..3a5253d --- /dev/null +++ b/func/util.lua @@ -0,0 +1,176 @@ +local c = {} + +local _PAIRS = pairs +local _TYPE = type +local _TONUMBER = tonumber +local _TOSTRING = tostring +local _IO_POPEN = io.popen +local _IO_OPEN = io.open +local _MATH_FLOOR = math.floor +local _MATH_CEIL = math.ceil +local _STRING_SUB = string.sub +local _STRING_GSUB = string.gsub +local _STRING_MATCH = string.match +local _STRING_FORMAT = string.format +local _STRING_UPPER = string.upper +local _CONKY_PARSE = conky_parse +local _SELECT = select +local _SETMETATABLE = setmetatable + +local copy_table = function(t) + local s = {} + for i, v in _PAIRS(t) do s[i] = _TYPE(v) == 'table' and copy_table(v) or v end + return s +end + +local round = function(x, places) + local m = 10 ^ (places or 0) + if x >= 0 then + return _MATH_FLOOR(x * m + 0.5) / m + else + return _MATH_CEIL(x * m - 0.5) / m + end +end + +local get_bytes_power = function(unit) + if unit == 'KiB' then return 10 + elseif unit == 'MiB' then return 20 + elseif unit == 'GiB' then return 30 + elseif unit == 'TiB' then return 40 + else return 0 + end +end + +local convert_bytes = function(x, old_unit, new_unit) + if old_unit == new_unit then + return _TONUMBER(x) + else + return x * 2 ^ (get_bytes_power(old_unit) - get_bytes_power(new_unit)) + end +end + +local round_to_string = function(x, places) + places = places or 0 + local y = round(x, places) + if places > 0 then return _STRING_FORMAT('%.'..places..'f', y) else return _TOSTRING(y) end +end + +local precision_round_to_string = function(x, sig_fig) + sig_fig = sig_fig or 4 + if x < 10 then return round_to_string(x, sig_fig - 1) + elseif x < 100 then return round_to_string(x, sig_fig - 2) + elseif x < 1000 then return round_to_string(x, sig_fig - 3) + else return round_to_string(x, sig_fig - 4) + end +end + +local read_entire_file = function(file, regex, mode) + if not file then return '' end + local str = file:read(mode or '*a') + file:close() + if not str then return '' end + if regex then return _STRING_MATCH(str, regex) or '' else return str end +end + +local conky = function(expr, regex) + local ans = _CONKY_PARSE(expr) + if regex then return _STRING_MATCH(ans, regex) or '' else return ans end +end + +local precision_convert_bytes = function(val, old_unit, new_unit, sig_fig) + return precision_round_to_string(convert_bytes(val, old_unit, new_unit), sig_fig) +end + +local get_unit = function(bytes) + if bytes < 1024 then return 'B' + elseif bytes < 1048576 then return 'KiB' + elseif bytes < 1073741824 then return 'MiB' + else return 'GiB' + end +end + +local get_unit_base_K = function(kilobytes) + if kilobytes < 1024 then return 'KiB' + elseif kilobytes < 1048576 then return 'MiB' + elseif kilobytes < 1073741824 then return 'GiB' + else return 'TiB' + end +end + +local parse_unit = function(str) + return _STRING_MATCH(str, '^([%d%p]-)(%a+)') +end + +local char_count = function(str, char) + return _SELECT(2, _STRING_GSUB(str, char, char)) +end + +local line_count = function(str) + return char_count(str, '\n') +end + +local execute_cmd = function(cmd, regex, mode) + return read_entire_file(_IO_POPEN(cmd), regex, mode) +end + +local read_file = function(path, regex, mode) + return read_entire_file(_IO_OPEN(path, 'rb'), regex, mode) +end + +local write_file = function(path, str) + local file = _IO_OPEN(path, 'w+') + if file then + file:write(str) + file:close() + end +end + +local conky_numeric = function(expr, regex) + return _TONUMBER(conky(expr, regex)) or 0 +end + +local memoize = function(f) + local mem = {} -- memoizing table + _SETMETATABLE(mem, {__mode = "kv"}) -- make it weak + return function (x) -- new version of ’f’, with memoizing + local r = mem[x] + if not r then -- no previous result? + r = f(x) -- calls original function + mem[x] = r -- store result for reuse + end + return r + end +end + +local convert_unix_time = function(unix_time, frmt) + local cmd = 'date -d @'..unix_time + if frmt then cmd = cmd..' +\''..frmt..'\'' end + return _STRING_MATCH(execute_cmd(cmd), '(.-)\n') +end + +local capitalize_each_word = function(str) + return _STRING_SUB(_STRING_GSUB(" "..str, "%W%l", _STRING_UPPER), 2) +end + +c.round = round +c.get_bytes_power = get_bytes_power +c.convert_bytes = convert_bytes +c.copy_table = copy_table +c.conky = conky +c.round_to_string = round_to_string +c.precision_round_to_string = precision_round_to_string +c.precision_convert_bytes = precision_convert_bytes +c.get_unit = get_unit +c.get_unit_base_K = get_unit_base_K +c.parse_unit = parse_unit +c.char_count = char_count +c.line_count = line_count +c.execute_cmd = execute_cmd +c.read_file = read_file +c.write_file = write_file +c.conky_numeric = conky_numeric +c.memoize = memoize +c.convert_unix_time = convert_unix_time +c.capitalize_each_word = capitalize_each_word + +return c diff --git a/super/Color.lua b/super/Color.lua new file mode 100644 index 0000000..47b6cf9 --- /dev/null +++ b/super/Color.lua @@ -0,0 +1,55 @@ +local c = {} + +local Gradient = require 'Gradient' + +local _CAIRO_PATTERN_CREATE_RGBA = cairo_pattern_create_rgba + +--Color(hex_rgba, [force_alpha]) +local init = function(arg) + + local hex_rgba = arg.hex_rgba + + local obj = { + r = ((hex_rgba / 0x1000000) % 0x100) / 255., + g = ((hex_rgba / 0x10000) % 0x100) / 255., + b = ((hex_rgba / 0x100) % 0x100) / 255., + a = arg.force_alpha or (hex_rgba % 0x100) / 255. + } + obj.userdata = _CAIRO_PATTERN_CREATE_RGBA(obj.r, obj.g, obj.b, obj.a) + + return obj +end + +--ColorStop(hex_rgba, stop, [force_alpha]) +local initColorStop = function(arg) + + local obj = init{ + hex_rgba = arg.hex_rgba, + force_alpha = arg.force_alpha + } + + obj.stop = arg.stop + + return obj +end + +--Gradient([p1], [p2], [r0], [r1], ... color stops) +local initGradient = function(arg) + + local obj = { + color_stops = {}, + ptype = 'Gradient' + } + + for i = 1, #arg do obj.color_stops[i] = arg[i] end + + Gradient(obj, arg.p1, arg.p2, arg.r1, arg.r2) + + return obj +end + +c.init = init +c.ColorStop = initColorStop +c.Gradient = initGradient + +return c diff --git a/super/Gradient.lua b/super/Gradient.lua new file mode 100644 index 0000000..ac5aa7c --- /dev/null +++ b/super/Gradient.lua @@ -0,0 +1,20 @@ +local _CAIRO_PATTERN_CREATE_RADIAL = cairo_pattern_create_radial +local _CAIRO_PATTERN_CREATE_LINEAR = cairo_pattern_create_linear +local _CAIRO_PATTERN_ADD_COLOR_STOP_RGBA = cairo_pattern_add_color_stop_rgba +local _PAIRS = pairs + +local set_dimensions = function(gradient, p1, p2, r1, r2) + if p1 and p2 then + local pattern = (r1 and r2) and + _CAIRO_PATTERN_CREATE_RADIAL(p1.x, p1.y, r1, p2.x, p2.y, r2) or + _CAIRO_PATTERN_CREATE_LINEAR(p1.x, p1.y, p2.x, p2.y) + + for _, color_stop in _PAIRS(gradient.color_stops) do + _CAIRO_PATTERN_ADD_COLOR_STOP_RGBA(pattern, color_stop.stop, color_stop.r, + color_stop.g, color_stop.b, color_stop.a) + end + gradient.userdata = pattern + end +end + +return set_dimensions diff --git a/super/Super.lua b/super/Super.lua new file mode 100644 index 0000000..8c6bfcd --- /dev/null +++ b/super/Super.lua @@ -0,0 +1,58 @@ +local c = {} + +local Gradient = require 'Gradient' +local schema = require 'default_patterns' + +local _TONUMBER = tonumber +local _STRING_SUB = string.sub + +--Pattern(pattern, [p1], [p2], [r1], [r2], [key]) +local initPattern = function(arg) + + local pattern = arg.pattern + local p1 = arg.p1 + local p2 = arg.p2 + local r1 = arg.r1 + local r2 = arg.r2 + + if p1 and p2 and pattern and pattern.ptype == 'Gradient' then + Gradient(pattern, p1, p2, r1, r2) + end + + return pattern.userdata +end + +--Critical([critical_pattern], [critical_limit], [p1], [p2], [r1], [r2]) + +local CRITICAL_PATTERN = schema.red +local CRITICAL_LIMIT = '>80' + +local CRITICAL_CREATE_FUNCTION = function(limit) + local compare = limit and _STRING_SUB(limit, 1, 1) + local value = limit and _TONUMBER(_STRING_SUB(limit, 2)) + + if compare == '>' then return function(n) return (n > value) end end + if compare == '<' then return function(n) return (n < value) end end + return function(n) return nil end --if no limit then return dummy +end + +local initCritical = function(arg) + + local obj = { + source = initPattern{ + pattern = arg.critical_pattern or CRITICAL_PATTERN, + p1 = arg.p1, + p2 = arg.p2, + r1 = arg.r1, + r2 = arg.r2, + }, + enabled = CRITICAL_CREATE_FUNCTION(arg.critical_limit or CRITICAL_LIMIT) + } + + return obj +end + +c.Pattern = initPattern +c.Critical = initCritical + +return c diff --git a/super/Widget.lua b/super/Widget.lua new file mode 100644 index 0000000..90c0592 --- /dev/null +++ b/super/Widget.lua @@ -0,0 +1,1112 @@ +local c = {} + +local Super = require 'Super' + +local _CR = require 'CR' +local util = require 'util' +local schema = require 'default_patterns' + +local Arc = require 'Arc' +local Dial = require 'Dial' +local Poly = require 'Poly' +local Bar = require 'Bar' +local Text = require 'Text' +local CriticalText = require 'CriticalText' +local TextColumn = require 'TextColumn' +local Table = require 'Table' +local Plot = require 'Plot' +local LabelPlot = require 'LabelPlot' +local Image = require 'Image' +local ScaledImage = require 'ScaledImage' + +local _CAIRO_NEW_PATH = cairo_new_path +local _CAIRO_RECTANGLE = cairo_rectangle +local _CAIRO_COPY_PATH = cairo_copy_path +local _CAIRO_SET_FONT_FACE = cairo_set_font_face +local _CAIRO_SET_FONT_SIZE = cairo_set_font_size +local _CAIRO_FONT_EXTENTS = cairo_font_extents +local _CAIRO_TOY_FONT_FACE_CREATE = cairo_toy_font_face_create + +local _UNPACK = unpack +local _MATH_ATAN2 = math.atan2 +local _MATH_SIN = math.sin +local _MATH_COS = math.cos +local _MATH_CEIL = math.ceil +local _MATH_LOG = math.log +local _MATH_RAD = math.rad + +--Box(x, y, [width], [height]) + +local BOX_WIDTH = 0 +local BOX_HEIGHT = 0 + +local Box = function(arg) + + local width = arg.width or BOX_WIDTH + local height = arg.height or BOX_HEIGHT + + local obj = { + x = arg.x, + y = arg.y, + width = width, + height = height, + right_x = width + arg.x, + bottom_y = height + arg.y + } + + return obj +end + +--Arc(x, y, radius, [thickness], [theta0], [theta1], [arc_pattern], [cap]) + +local ARC_CAP = CAIRO_LINE_CAP_BUTT +local ARC_THICKNESS = 2 +local ARC_THETA0 = 0 +local ARC_THETA1 = 360 +local ARC_PATTERN = schema.dark_grey + +local initArc = function(arg) + + local x = arg.x + local y = arg.y + local radius = arg.radius + local thickness = arg.thickness or ARC_THICKNESS + + local obj = { + x = x, + y = y, + thickness = thickness, + cap = arg.cap or ARC_CAP, + path = Arc.create_path(x, y, radius, _MATH_RAD(arg.theta0 or ARC_THETA0), + _MATH_RAD(arg.theta1 or ARC_THETA1)), + source = Super.Pattern{ + pattern = arg.arc_pattern or ARC_PATTERN, + p1 = {x = x, y = y}, + p2 = {x = x, y = y}, + r1 = radius - thickness * 0.5, + r2 = radius + thickness * 0.5 + } + } + + return obj +end + +--[[ +Dial([x], [y], radius, [thickness], [theta0], [theta1], [arc_pattern], [cap], + [dial_pattern], [critical_pattern], [critical_limit]) +]] + +local DIAL_THICKNESS = 4 +local DIAL_THETA0 = 90 +local DIAL_THETA1 = 360 +local DIAL_ARC_PATTERN = schema.grey_rounded +local DIAL_DIAL_PATTERN = schema.blue_rounded +local DIAL_CRITICAL_PATTERN = schema.red_rounded +local DIAL_CRITICAL_LIMIT = '>80' + +local initDial = function(arg) + + local x = arg.x + local y = arg.y + local radius = arg.radius + local thickness = arg.thickness or DIAL_THICKNESS + local theta0 = arg.theta0 or DIAL_THETA0 + local theta1 = arg.theta1 or DIAL_THETA1 + + local obj = initArc{ + x = x, + y = y, + radius = radius, + thickness = thickness, + theta0 = theta0, + theta1 = theta1, + arc_pattern = arg.arc_pattern or DIAL_ARC_PATTERN, + cap = arg.cap + } + + local inner_radius = radius - thickness * 0.5 + local outer_radius = radius + thickness * 0.5 + + obj.indicator_source = Super.Pattern{ + pattern = arg.dial_pattern or DIAL_DIAL_PATTERN, + p1 = {x = x, y = y}, + p2 = {x = x, y = y}, + r1 = inner_radius, + r2 = outer_radius + } + + obj.critical = Super.Critical{ + critical_pattern = arg.critical_pattern or DIAL_CRITICAL_PATTERN, + critical_limit = arg.critical_limit or DIAL_CRITICAL_LIMIT, + p1 = {x = x, y = y}, + p2 = {x = x, y = y}, + r1 = inner_radius, + r2 = outer_radius + } + + theta0 = _MATH_RAD(theta0) + theta1 = _MATH_RAD(theta1) + + obj._make_dial_path = util.memoize( + function(percent) + obj.dial_angle = (1 - percent) * theta0 + percent * theta1 + return Arc.create_path(x, y, radius, theta0, obj.dial_angle) + end + ) + + Dial.set(obj, 0) + + return obj +end + +--[[ +Dial([x], [y], inner_radius, outer_radius, spacing, num_dials, [theta0], [theta1], + [arc_pattern], [cap], [dial_pattern], [critical_pattern], [critical_limit]) +]] +local initCompoundDial = function(arg) + + local inner_radius = arg.inner_radius + local outer_radius = arg.outer_radius + local spacing = arg.spacing + local num_dials = arg.num_dials + + local side_length = outer_radius * 2 + + local obj = { + width = side_length, + height = side_length, + num_dials = num_dials, + dials = {} + } + + local thickness = ((outer_radius - inner_radius) - (num_dials - 1) * spacing) / num_dials + + for i = 1, obj.num_dials do + local r = inner_radius + thickness * 0.5 + (i - 1) * (spacing + thickness) + obj.dials[i] = initDial{ + x = arg.x, + y = arg.y, + radius = r, + thickness = thickness, + theta0 = arg.theta0, + theta1 = arg.theta1, + arc_pattern = arg.arc_pattern, + dial_pattern = arg.dial_pattern, + critical_pattern = arg.critical_pattern, + critical_limit = arg.critical_limit, + cap = arg.cap + } + end + + return obj +end + +--Poly([thickness], [line_pattern], [cap], [join], [closed], ... points) + +local POLY_THICKNESS = 1 +local POLY_CAP = CAIRO_LINE_CAP_BUTT +local POLY_JOIN = CAIRO_LINE_JOIN_MITER +local POLY_LINE_PATTERN = schema.mid_grey + +local initPoly = function(arg) + + local obj = { + thickness = arg.thickness or POLY_THICKNESS, + cap = arg.cap or POLY_CAP, + join = arg.join or POLY_JOIN, + points = {} + } + + local points = {} + + for i = 1, #arg do points[i] = arg[i] end + + obj.path = Poly.create_path(arg.closed, _UNPACK(points)) + + obj.source = Super.Pattern{ + pattern = line_pattern or POLY_LINE_PATTERN, + p1 = points[1], + p2 = points[#points] + } + + return obj +end + +--Line(p1, p2, [thickness], [line_pattern], [cap]) + +local LINE_THICKNESS = 1 +local LINE_PATTERN = schema.dark_grey +local LINE_CAP = CAIRO_LINE_CAP_BUTT + +local initLine = function(arg) + + local p1 = arg.p1 + local p2 = arg.p2 + local thickness = arg.thickness or LINE_THICKNESS + local line_pattern = arg.line_pattern or LINE_PATTERN + local cap = arg.cap or LINE_CAP + + local obj = initPoly{ + thickness = thickness, + line_pattern = line_pattern, + cap = cap, + p1, + p2 + } + + return obj +end + +--[[ +Bar(p1, p2, [thickness], [line_pattern], [indicator_pattern], [critical_pattern], + [critical_limit], [cap]) +]] + +local BAR_THICKNESS = 10 +local BAR_CRITICAL_LIMIT = '>80' +local BAR_CAP = CAIRO_LINE_CAP_BUTT +local BAR_LINE_PATTERN = schema.grey_rounded +local BAR_INDICATOR_PATTERN = schema.blue_rounded +local BAR_CRITICAL_PATTERN = schema.red_rounded + +local initBar = function(arg) + + local p1 = arg.p1 + local p2 = arg.p2 + local thickness = arg.thickness or BAR_THICKNESS + + local obj = initLine{ + p1 = p1, + p2 = p2, + thickness = thickness, + line_pattern = line_pattern, + cap = arg.cap or BAR_CAP + } + + local p1_x = p1.x + local p1_y = p1.y + local p2_x = p2.x + local p2_y = p2.y + + local theta = _MATH_ATAN2(p2_y - p1_y, p2_x - p1_x) + local delta_x = 0.5 * thickness * _MATH_SIN(theta) --and yes, these are actually flipped + local delta_y = 0.5 * thickness * _MATH_COS(theta) + local p1_pattern = {x = p1_x + delta_x, y = p1_y + delta_y} + local p2_pattern = {x = p1_x - delta_x, y = p1_y - delta_y} + + --override pattern from superclasses + obj.source = Super.Pattern{ + pattern = arg.line_pattern or BAR_LINE_PATTERN, + p1 = p1_pattern, + p2 = p2_pattern + } + + obj.indicator_source = Super.Pattern{ + pattern = arg.indicator_pattern or BAR_INDICATOR_PATTERN, + p1 = p1_pattern, + p2 = p2_pattern + } + + obj.critical = Super.Critical{ + critical_pattern = arg.critical_pattern or BAR_CRITICAL_PATTERN, + critical_limit = arg.critical_limit or BAR_CRITICAL_LIMIT, + p1 = p1_pattern, + p2 = p2_pattern + } + + obj.midpoint = {} + + obj._make_bar_path = util.memoize( + function(percent) + local mp = obj.midpoint + mp.x = (p2_x - p1_x) * percent + p1_x + mp.y = (p2_y - p1_y) * percent + p1_y + return Poly.create_path(nil, p1, mp) + end + ) + + Bar.set(obj, 0) + + return obj +end + +--[[ +CompoundBar([x], [y], width, [length], [spacing], [num_bars], [line_pattern], + [indicator_pattern], [critical_pattern], [critical_limit], [cap], [is_vertical]) +]] +local initCompoundBar = function(arg) + + local x = arg.x + local y = arg.y + local thickness = arg.thickness + local length = arg.length + local spacing = arg.spacing + local num_bars = arg.num_bars + local is_vertical = arg.is_vertical + + local width = is_vertical and spacing * (num_bars -1) or length + local height = is_vertical and length or spacing * (num_bars -1) + + local obj = Box{ + x = x, + y = y, + width = width, + height = height + } + + obj.bars = { + n = num_bars + } + + for i = 1, num_bars do + local p1, p2 + local var_dim = spacing * (i - 1) + + if is_vertical then + var_dim = x + var_dim + p1 = {x = var_dim, y = y} + p2 = {x = var_dim, y = obj.bottom_y} + else + var_dim = y + var_dim + p1 = {x = x, y = var_dim} + p2 = {x = obj.right_x, y = var_dim} + end + + obj.bars[i] = initBar{ + x = x, + y = y, + p1 = p1, + p2 = p2, + thickness = arg.thickness, + line_pattern = arg.line_pattern, + indicator_pattern = arg.indicator_pattern, + critical_pattern = arg.critical_pattern, + critical_limit = arg.critical_limit, + cap = arg.cap + } + end + + return obj +end + +--Rect(x, y, [width], [height], [thickness], [join], [line_pattern]) + +local RECT_LINE_PATTERN = schema.mid_grey +local RECT_LINE_THICKNESS = 1 +local RECT_LINE_JOIN = CAIRO_LINE_JOIN_MITER + +local RECT_CREATE_PATH = function(x, y, w, h) + _CAIRO_NEW_PATH(_CR) + _CAIRO_RECTANGLE(_CR, x, y, w, h) + return _CAIRO_COPY_PATH(_CR) +end + +local initRect = function(arg) + + local x = arg.x + local y = arg.y + local width = arg.width + local height = arg.height + + local obj = Box{ + x = x, + y = y, + width = width, + height = height + } + + obj.path = RECT_CREATE_PATH(x, y, width, height) + + obj.thickness = arg.thickness or RECT_LINE_THICKNESS + obj.join = arg.join or RECT_LINE_JOIN + + obj.source = Super.Pattern{ + pattern = arg.line_pattern or RECT_LINE_PATTERN, + {x = x, y = y}, + {x = x, y = y + height} + } + + return obj +end + +--FillRect(x, y, [width], [height], [thickness], [join], [line_pattern], [fill_pattern]) +local initFillRect = function(arg) + + local x = arg.x + local y = arg.y + local width = arg.width + local height = arg.height + + local obj = initRect{ + x = x, + y = y, + width = width, + height = height, + join = arg.join, + thickness = arg.thickness, + line_pattern = arg.line_pattern + } + obj.fill_source = Super.Pattern{ + pattern = arg.fill_pattern, + p1 = {x = x, y = y}, + p2 = {x = x, y = y + height} + } + + return obj +end + +--Image([path], x, y) + +local initImage = function(arg) + + local path = arg.path + local x = arg.x + local y = arg.y + + local obj = {x = x, y = y} + + local width, height + + if path then Image.update(obj, path) end + + obj.width = width or 0 + obj.height = height or 0 + obj.path = path + + return obj +end + +--ScaledImage([path], x, y, [width], [height]) + +local initScaledImage = function(arg) + + local path = arg.path + local x = arg.x + local y = arg.y + + local obj = Box{x = x, y = y} + + local img_width + local img_height + + if path then ScaledImage.update(obj, path) end + + obj.width = arg.width or 0 + obj.height = arg.height or 0 + + return obj +end + +--[[ +Text(x, y, [text], [font_size], [x_align], [y_align], [text_color], [font], [slant], + [weight], [append_front], [append_end]) +]] + +local TEXT_STRING = '' +local TEXT_FONT_SIZE = 13 +local TEXT_X_ALIGN = 'left' +local TEXT_Y_ALIGN = 'center' +local TEXT_FONT = 'Neuropolitical' +local TEXT_FONT_SLANT = CAIRO_FONT_SLANT_NORMAL +local TEXT_FONT_WEIGHT = CAIRO_FONT_WEIGHT_NORMAL +local TEXT_COLOR = schema.light_grey + +local fe = cairo_font_extents_t:create() +tolua.takeownership(fe) + +local initText = function(arg) + + local font_size = arg.font_size or TEXT_FONT_SIZE + + local font_face = _CAIRO_TOY_FONT_FACE_CREATE( + arg.font or TEXT_FONT, + arg.slant or TEXT_FONT_SLANT, + arg.weight or TEXT_FONT_WEIGHT + ) + local source = Super.Pattern{ + pattern = arg.text_color or TEXT_COLOR + } + + _CAIRO_SET_FONT_SIZE(_CR, font_size) + _CAIRO_SET_FONT_FACE(_CR, font_face) + _CAIRO_FONT_EXTENTS(_CR, fe) + + local delta_y + local y_align = arg.y_align or TEXT_Y_ALIGN + + if y_align == 'bottom' then delta_y = -fe.descent + elseif y_align == 'top' then delta_y = fe.height + elseif y_align == 'center' then delta_y = 0.92 * fe.height * 0.5 - fe.descent + end + + local obj = { + x = arg.x, + y = arg.y + delta_y, + x_ref = arg.x, + y_ref = arg.y + delta_y, + delta_y = delta_y, + height = fe.ascent, + font_size = font_size, + x_align = arg.x_align or TEXT_X_ALIGN, + y_align = y_align, + font_face = font_face, + source = source, + current_source = source, --hack to integrate critical + append_front = arg.append_front, + append_end = arg.append_end + } + + Text.set(obj, _CR, (arg.text or TEXT_STRING)) + + return obj +end + +--[[ +CriticalText(x, y, [text], [font_size], [x_align], [y_align], [text_color], [font], + [slant], [weight], [append_front], [append_end], [critical_color], [critical_limit]) +]] + +local CRITICALTEXT_COLOR = schema.blue +local CRITICALTEXT_CRITICAL_COLOR = schema.red + +local initCriticalText = function(arg) + + local obj = initText{ + x = arg.x, + y = arg.y, + text = arg.text, + font_size = arg.font_size, + x_align = arg.x_align, + y_align = arg.y_align, + text_color = arg.text_color or CRITICALTEXT_COLOR, + font = arg.font, + slant = arg.slant, + weight = arg.weight, + append_front = arg.append_front, + append_end = arg.append_end + } + + obj.critical = Super.Critical{ + critical_color = arg.critical_color or CRITICALTEXT_CRITICAL_COLOR, + critical_limit = arg.critical_limit + } + + CriticalText.set(obj, _CR, '0') + + return obj +end + +--[[ +TextColumn(x, y, [spacing], [max_length], [font_size], [x_align], [y_align], + [text_color], [font], [slant], [weight], [append_front], [append_end], + [num_rows], ... text list) +]] + +local TEXTCOLUMN_SPACING = 20 +--~ local TEXTCOLUMN_MAX_LENGTH = -1 +local TEXTCOLUMN_NUM_ROWS = 1 + +local initTextColumn = function(arg) + + local obj = { + rows = { + n = (#arg == 0) and (arg.num_rows or TEXTCOLUMN_NUM_ROWS) or #arg + }, + spacing = arg.spacing or TEXTCOLUMN_SPACING, + max_length = arg.max_length-- or TEXTCOLUMN_MAX_LENGTH + } + + for i = 1, obj.rows.n do + obj.rows[i] = initText{ + x = arg.x, + y = arg.y + obj.spacing * (i - 1), + font_size = arg.font_size, + x_align = arg.x_align, + y_align = arg.y_align, + text_color = arg.text_color, + font = arg.font, + slant = arg.slant, + weight = arg.weight, + append_front = arg.append_front, + append_end = arg.append_end + } + TextColumn.set(obj, _CR, i, (#arg == 0) and 'row'..i or arg[i]) + end + + return obj +end + +--[[ +Table(x, y, width, height, [num_rows], [max_length], [line_pattern], + [body_color], [header_color], [separator_pattern], ... header list) +]] + +local TABLE_FONT = "Neuropolitical" + +local TABLE_HEADER_FONT_SIZE = 11 +local TABLE_HEADER_FONT_SLANT = CAIRO_FONT_SLANT_NORMAL +local TABLE_HEADER_FONT_WEIGHT = CAIRO_FONT_WEIGHT_NORMAL + +local TABLE_BODY_FONT_SIZE = 11 +local TABLE_BODY_FONT_SLANT = CAIRO_FONT_SLANT_NORMAL +local TABLE_BODY_FONT_WEIGHT = CAIRO_FONT_WEIGHT_NORMAL + +local TABLE_X_ALIGN = 'center' +local TABLE_Y_ALIGN = 'center' + +local TABLE_JOIN = CAIRO_LINE_JOIN_MITER + +local TABLE_TOP_PAD = 15 +local TABLE_BOTTOM_PAD = 15 +local TABLE_LEFT_PAD = 5 +local TABLE_RIGHT_PAD = 5 +local TABLE_HEADER_PAD_FACTOR = 1.25 + +local TABLE_SEPARATOR_THICKNESS = 1 +local TABLE_SEPARATOR_CAP = CAIRO_LINE_CAP_BUTT + +local TABLE_NUM_ROWS = 5 +local TABLE_MAX_LENGTH = 8 +local TABLE_HEADER_COLOR = schema.blue +local TABLE_BODY_COLOR = schema.light_grey +local TABLE_LINE_PATTERN = schema.dark_grey +local TABLE_SEPARATOR_PATTERN = schema.dark_grey + +local initTable = function(arg) + + local x = arg.x + local y = arg.y + local width = arg.width + local height = arg.height + local num_rows = arg.num_rows or TABLE_NUM_ROWS + + local obj = initRect{ + x = x + 0.5, + y = y + 0.5, + width = width, + height = height, + join = TABLE_JOIN, + line_pattern = arg.line_pattern or TABLE_LINE_PATTERN + } + + obj.table = Box{ + x = x + TABLE_LEFT_PAD, + y = y + TABLE_TOP_PAD, + width = width - TABLE_LEFT_PAD - TABLE_RIGHT_PAD, + height = height - TABLE_TOP_PAD - TABLE_BOTTOM_PAD + } + + local tbl = obj.table + tbl.num_rows = num_rows + tbl.num_columns = #arg + + tbl.columns = {} + + local column_width = tbl.width / tbl.num_columns + local spacing = tbl.height / (TABLE_HEADER_PAD_FACTOR + tbl.num_rows - 1) + + for i = 1, tbl.num_columns do + local column_x = tbl.x + column_width * (i - 0.5) + + tbl.columns[i] = initTextColumn{ + x = column_x, + y = tbl.y + spacing * TABLE_HEADER_PAD_FACTOR, + spacing = spacing, + max_length = arg.max_length or TABLE_MAX_LENGTH, + font_size = TABLE_BODY_FONT_SIZE, + x_align = TABLE_X_ALIGN, + y_align = TABLE_Y_ALIGN, + text_color = arg.body_color or TABLE_BODY_COLOR, + font = TABLE_FONT, + slant = TABLE_BODY_FONT_SLANT, + slant = TABLE_BODY_FONT_WEIGHT, + num_rows = num_rows + } + tbl.columns[i].header = initText{ + x = column_x, + y = tbl.y, + text = arg[i], + font_size = TABLE_HEADER_FONT_SIZE, + x_align = TABLE_X_ALIGN, + y_align = TABLE_Y_ALIGN, + text_color = arg.header_color or TABLE_HEADER_COLOR, + font = TABLE_FONT, + slant = TABLE_HEADER_FONT_SLANT, + weight = TABLE_HEADER_FONT_WEIGHT + } + end + + tbl.separators = { + n = tbl.num_columns - 1 + } + + for i = 1, tbl.separators.n do + local sep_x = tbl.x + column_width * i + tbl.separators[i] = initLine{ + thickness = TABLE_SEPARATOR_THICKNESS, + line_pattern = arg.separator_pattern or TABLE_SEPARATOR_PATTERN, + cap = TABLE_SEPARATOR_CAP, + p1 = {x = sep_x, y = tbl.y}, + p2 = {x = sep_x, y = tbl.bottom_y} + } + end + + return obj +end + + +--[[ +Plot([x], [y], width, height, [seconds], [num_x_intrvl], [num_y_intrvl], + [outline_pattern], [intrvl_pattern], [data_line_pattern], [data_fill_pattern]) +]] + +local PLOT_SECONDS = 60 +local PLOT_NUM_X_INTERVAL = 6 +local PLOT_NUM_Y_INTERVAL = 4 +local PLOT_OUTLINE_PATTERN = schema.dark_grey +local PLOT_INTRVL_PATTERN = schema.dark_grey +local PLOT_DATA_LINE_PATTERN = schema.transparent_blue +local PLOT_DATA_FILL_PATTERN = schema.transparent_blue + +local initPlot = function(arg) + + local x = arg.x + local y = arg.y + local width = arg.width + local height = arg.height + + local obj = Box{ + x = x, + y = y, + width = width, + height = height + } + + local p1 = {x = x, y = y} + local p2 = {x = x + width, y = y} + + --allocate outline objects + obj.outline = { + source = Super.Pattern{ + pattern = arg.outline_pattern or PLOT_OUTLINE_PATTERN, + p1 = p1, + p2 = p2 + }, + } + + obj.intrvls = { + source = Super.Pattern{ + pattern = arg.intrvl_pattern or PLOT_INTRVL_PATTERN, + p1 = p1, + p2 = p2 + }, + x = { + n = arg.num_x_intrvl or PLOT_NUM_X_INTERVAL + }, + y = { + n = arg.num_y_intrvl or PLOT_NUM_Y_INTERVAL + }, + + } + + local data_fill_pattern = arg.data_fill_pattern or PLOT_DATA_FILL_PATTERN + local seconds = arg.seconds or PLOT_SECONDS + + obj.data = { + line_source = Super.Pattern{ + pattern = arg.data_line_pattern or PLOT_DATA_LINE_PATTERN, + p1 = p1, + p2 = p2 + }, + seconds = seconds, + n = seconds * CONSTRUCTION_GLOBAL.UPDATE_INTERVAL, + fill_source = data_fill_pattern and Super.Pattern{ + pattern = data_fill_pattern, + p1 = p1, + p2 = p2 + } + } + + Plot.position_x_intrvls(obj) + Plot.position_y_intrvls(obj) + Plot.position_graph_outline(obj) + + return obj +end + +--[[ +LabelPlot(x, y, width, height, [seconds], [x_label_func], [y_label_func], + [num_x_intrvl], [num_y_intrvl], [outline_pattern], [intrvl_pattern], + [data_line_pattern], [data_fill_pattern], [label_color]) +]] + +local LABELPLOT_LABEL_SIZE = 8 +local LABELPLOT_LABEL_FONT = "Neuropolitical" +local LABELPLOT_LABEL_SLANT = CAIRO_FONT_SLANT_NORMAL +local LABELPLOT_LABEL_WEIGHT = CAIRO_FONT_WEIGHT_NORMAL +local LABELPLOT_LABEL_COLOR = schema.mid_grey +local LABELPLOT_SECONDS = 60 +local LABELPLOT_NUM_X_INTERVAL = 6 +local LABELPLOT_NUM_Y_INTERVAL = 4 + +local initLabelPlot = function(arg) + + local x = arg.x + local y = arg.y + local width = arg.width + local height = arg.height + local seconds = arg.seconds or LABELPLOT_SECONDS + local x_label_func = arg.x_label_func + local y_label_func = arg.y_label_func + local num_x_intrvl = arg.num_x_intrvl or LABELPLOT_NUM_X_INTERVAL + local num_y_intrvl = arg.num_y_intrvl or LABELPLOT_NUM_Y_INTERVAL + local label_color = arg.label_color or LABELPLOT_LABEL_COLOR + local x_input_factor = arg.x_input_factor + local y_input_factor = arg.y_input_factor + + local obj = Box{ + x = x, + y = y, + width = width, + height = height + } + + obj.plot = initPlot{ + x = x, + y = y, + width = width, + height = height, + seconds = seconds, + num_x_intrvl = num_x_intrvl, + num_y_intrvl = num_y_intrvl, + outline_pattern = arg.outline_pattern, + intrvl_pattern = arg.intrvl_pattern, + data_line_pattern = arg.data_line_pattern, + data_fill_pattern = arg.data_fill_pattern + } + + obj.labels = { + x = { + n = num_x_intrvl + 1 + }, + y = { + n = num_y_intrvl + 1 + } + } + + --x labels + local labels_x = obj.labels.x + + labels_x._func = x_label_func or function(fraction) + return util.round((1-fraction) * seconds)..'s' + end + + for i = 1, labels_x.n do + labels_x[i] = initText{ + x = 0, + y = obj.bottom_y, + font_size = LABELPLOT_LABEL_SIZE, + x_align = 'center', + y_align = 'bottom', + text_color = label_color + } + end + + LabelPlot.populate_x_labels(obj, _CR, x_input_factor) + + --y labels + local labels_y = obj.labels.y + + labels_y._func = y_label_func or function(fraction) + return (fraction * 100)..'%' + end + + for i = 1, labels_y.n do + labels_y[i] = initText{ + x = x, + y = 0, + font_size = LABELPLOT_LABEL_SIZE, + x_align = 'left', + y_align = 'center', + text_color = label_color, + } + end + + LabelPlot.populate_y_labels(obj, _CR, y_input_factor) + + LabelPlot.position_x_intrvls(obj.plot) + LabelPlot.position_y_intrvls(obj.plot) + + LabelPlot.position_graph_outline(obj.plot) + + LabelPlot.position_x_labels(obj) + LabelPlot.position_y_labels(obj) + + return obj +end + +--[[ +ScalePlot(x, y, width, height, [seconds], [x_label_func], [y_label_func], + [scale_function], [num_x_intrvl], [num_y_intrvl], [outline_pattern], [intrvl_pattern], + [data_line_pattern], [data_fill_pattern], [label_color]) +]] + +local SCALEPLOT_THRESHOLD = 0.9 --trip point to go to next scale domain +local SCALEPLOT_BASE = 2 --base for log scale +local SCALEPLOT_INITIAL = 1 --initial scale domain value + +--~ local SCALEPLOT_SCALE_FUNCTION = function(x) + --~ local domain = (x > 0) and _MATH_CEIL(_MATH_LOG(x / SCALEPLOT_THRESHOLD) / + --~ _MATH_LOG(SCALEPLOT_BASE) - SCALEPLOT_INITIAL + 1) or 1 + --~ domain = (domain < 1) and 1 or domain + --~ local factor = SCALEPLOT_BASE ^ -(SCALEPLOT_INITIAL + domain - 1) + --~ return domain, factor +--~ end + +local SCALEPLOT_Y_LABEL_FUNCTION = function(kilobytes) + local new_unit = util.get_unit_base_K(kilobytes) + local converted_bytes = util.convert_bytes(kilobytes, 'KiB', new_unit) + local precision = 0 + if converted_bytes < 10 then precision = 1 end + + return util.round_to_string(converted_bytes, precision)..' '..new_unit..'/s' +end + +local initScalePlot = function(arg) + + local base = arg.scaleplot_base or SCALEPLOT_BASE + local initial = arg.scaleplot_initial or SCALEPLOT_INITIAL + + local obj = initLabelPlot{ + x = arg.x, + y = arg.y, + width = arg.width, + height = arg.height, + seconds = arg.seconds, + x_label_func = arg.x_label_func, + y_label_func = arg.y_label_func or SCALEPLOT_Y_LABEL_FUNCTION, + num_x_intrvl = arg.num_x_intrvl, + num_y_intrvl = arg.num_y_intrvl, + outline_pattern = arg.outline_pattern, + intrvl_pattern = arg.intrvl_pattern, + data_line_pattern = arg.data_line_pattern, + data_fill_pattern = arg.data_fill_pattern, + label_color = arg.label_color, + y_input_factor = base ^ initial + } + + obj.scale = { + --~ _func = scale_function or SCALEPLOT_SCALE_FUNCTION, + _func = function(x) + local threshold = arg.scaleplot_threshold or SCALEPLOT_THRESHOLD + + local domain = 1 + if x > 0 then + domain = _MATH_CEIL(_MATH_LOG(x / threshold) / _MATH_LOG(base) - initial + 1) + end + + if domain < 1 then domain = 1 end + return domain, base ^ -(initial + domain - 1) + end, + factor = 0.5, + domain = 1, + timers = {} + } + + obj.scale.previous_domain, obj.scale.previous_factor = obj.scale._func(0) + + return obj +end + +--Header(x, y, width, header) + +local HEADER_HEIGHT = 45 +local HEADER_FONT_SIZE = 15 +local HEADER_FONT_SLANT = CAIRO_FONT_SLANT_NORMAL +local HEADER_FONT_WEIGHT = CAIRO_FONT_WEIGHT_BOLD +local HEADER_X_ALIGN = 'left' +local HEADER_Y_ALIGN = 'top' +local HEADER_COLOR = schema.white +local HEADER_UNDERLINE_OFFSET = -20 +local HEADER_UNDERLINE_THICKNESS = 3 +local HEADER_UNDERLINE_COLOR = schema.white +local HEADER_UNDERLINE_CAP = CAIRO_LINE_CAP_ROUND + +local initHeader = function(arg) + + local x = arg.x + local y = arg.y + local width = arg.width + + local bottom_y = y + HEADER_HEIGHT + local underline_y = bottom_y + HEADER_UNDERLINE_OFFSET + + local obj = { + text = initText{ + x = x, + y = y, + text = arg.header, + font_size = HEADER_FONT_SIZE, + x_align = HEADER_X_ALIGN, + y_align = HEADER_Y_ALIGN, + text_color = HEADER_COLOR, + slant = HEADER_FONT_SLANT, + weight = HEADER_FONT_WEIGHT + }, + bottom_y = bottom_y, + underline = initLine{ + p1 = {x = x, y = underline_y}, + p2 = {x = x + width, y = underline_y}, + thickness = HEADER_UNDERLINE_THICKNESS, + line_pattern = HEADER_UNDERLINE_COLOR, + cap = HEADER_UNDERLINE_CAP + } + } + + return obj +end + +--Panel{x, y, width, height} + +local PANEL_LINE_PATTERN = schema.dark_grey +local PANEL_FILL_PATTERN = schema.transparent_black +--~ local PANEL_GLOSS_HEIGHT = 20 + +local initPanel = function(arg) + + --~ PANEL_FILL_PATTERN.color_stops[2].stop = PANEL_GLOSS_HEIGHT / arg.height + + local obj = initFillRect{ + x = arg.x + 0.5, + y = arg.y + 0.5, + width = arg.width, + height = arg.height, + line_pattern = PANEL_LINE_PATTERN, + fill_pattern = PANEL_FILL_PATTERN, + } + + return obj +end + +c.Arc = initArc +c.Dial = initDial +c.CompoundDial = initCompoundDial +c.Poly = initPoly +c.Line = initLine +c.Bar = initBar +c.CompoundBar = initCompoundBar +c.Rect = initRect +c.FillRect = initFillRect +c.Image = initImage +c.ScaledImage = initScaledImage +c.Text = initText +c.CriticalText = initCriticalText +c.TextColumn = initTextColumn +c.Table = initTable +c.Plot = initPlot +c.LabelPlot = initLabelPlot +c.ScalePlot = initScalePlot +c.Header = initHeader +c.Panel = initPanel + +return c diff --git a/widget/CR.lua b/widget/CR.lua new file mode 100644 index 0000000..675452f --- /dev/null +++ b/widget/CR.lua @@ -0,0 +1,5 @@ +local cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1366, 768) +local CR = cairo_create(cs) +cairo_surface_destroy(cs) + +return CR diff --git a/widget/arc/Arc.lua b/widget/arc/Arc.lua new file mode 100644 index 0000000..0437163 --- /dev/null +++ b/widget/arc/Arc.lua @@ -0,0 +1,31 @@ +local c = {} + +local _CR = require 'CR' + +local _CAIRO_NEW_PATH = cairo_new_path +local _CAIRO_ARC = cairo_arc +local _CAIRO_COPY_PATH = cairo_copy_path +local _CAIRO_APPEND_PATH = cairo_append_path +local _CAIRO_SET_LINE_WIDTH = cairo_set_line_width +local _CAIRO_SET_LINE_CAP = cairo_set_line_cap +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_STROKE = cairo_stroke + +local draw = function(obj, cr) + _CAIRO_APPEND_PATH(cr, obj.path) + _CAIRO_SET_LINE_WIDTH(cr, obj.thickness) + _CAIRO_SET_LINE_CAP(cr, obj.cap) + _CAIRO_SET_SOURCE(cr, obj.source) + _CAIRO_STROKE(cr) +end + +local create_path = function(x, y, radius, theta0, theta1) + _CAIRO_NEW_PATH(_CR) + _CAIRO_ARC(_CR, x, y, radius, theta0, theta1) + return _CAIRO_COPY_PATH(_CR) +end + +c.draw = draw +c.create_path = create_path + +return c diff --git a/widget/arc/CompoundDial.lua b/widget/arc/CompoundDial.lua new file mode 100644 index 0000000..b0ab71c --- /dev/null +++ b/widget/arc/CompoundDial.lua @@ -0,0 +1,34 @@ +local c = {} + +local Dial = require 'Dial' + +local _CAIRO_SET_LINE_WIDTH = cairo_set_line_width +local _CAIRO_SET_LINE_CAP = cairo_set_line_cap +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_STROKE = cairo_stroke +local _CAIRO_APPEND_PATH = cairo_append_path + +local set = function(obj, index, percent) + Dial.set(obj.dials[index], percent) +end + +local draw = function(obj, cr) + local dials = obj.dials + _CAIRO_SET_LINE_WIDTH(cr, dials[1].thickness) + _CAIRO_SET_LINE_CAP(cr, dials[1].cap) + + for i = 1, #dials do + local current_dial = dials[i] + _CAIRO_SET_SOURCE(cr, current_dial.source) + _CAIRO_APPEND_PATH(cr, current_dial.path) + _CAIRO_STROKE(cr) + _CAIRO_SET_SOURCE(cr, current_dial.current_source) + _CAIRO_APPEND_PATH(cr, current_dial.dial_path) + _CAIRO_STROKE(cr) + end +end + +c.set = set +c.draw = draw + +return c diff --git a/widget/arc/Dial.lua b/widget/arc/Dial.lua new file mode 100644 index 0000000..f49a6ea --- /dev/null +++ b/widget/arc/Dial.lua @@ -0,0 +1,30 @@ +local c = {} + +local Arc = require 'Arc' + +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_STROKE = cairo_stroke +local _CAIRO_APPEND_PATH = cairo_append_path + +local set = function(obj, percent) + obj.percent = percent + obj.dial_path = obj._make_dial_path(percent) + + if obj.critical.enabled(obj.percent) then + obj.current_source = obj.critical.source + else + obj.current_source = obj.indicator_source + end +end + +local draw = function(obj, cr) + Arc.draw(obj, cr) + _CAIRO_SET_SOURCE(cr, obj.current_source) + _CAIRO_APPEND_PATH(cr, obj.dial_path) + _CAIRO_STROKE(cr) +end + +c.set = set +c.draw = draw + +return c diff --git a/widget/image/Image.lua b/widget/image/Image.lua new file mode 100644 index 0000000..e47781e --- /dev/null +++ b/widget/image/Image.lua @@ -0,0 +1,31 @@ +local c = {} + +local _IMLIB_LOAD_IMAGE = imlib_load_image +local _IMLIB_CONTEXT_SET_IMAGE = imlib_context_set_image +local _IMLIB_RENDER_IMAGE_ON_DRAWABLE = imlib_render_image_on_drawable +local _IMLIB_FREE_IMAGE = imlib_free_image +local _IMLIB_IMAGE_GET_WIDTH = imlib_image_get_width +local _IMLIB_IMAGE_GET_HEIGHT = imlib_image_get_height + +local set = function(obj, path) + local img = _IMLIB_LOAD_IMAGE(path) + _IMLIB_CONTEXT_SET_IMAGE(img) + + obj.width = _IMLIB_IMAGE_GET_WIDTH() + obj.height = _IMLIB_IMAGE_GET_HEIGHT() + obj.path = path + + _IMLIB_FREE_IMAGE() +end + +local draw = function(obj) + local img = _IMLIB_LOAD_IMAGE(obj.path) + _IMLIB_CONTEXT_SET_IMAGE(img) + _IMLIB_RENDER_IMAGE_ON_DRAWABLE(obj.x, obj.y) + _IMLIB_FREE_IMAGE() +end + +c.set = set +c.draw = draw + +return c diff --git a/widget/image/ScaledImage.lua b/widget/image/ScaledImage.lua new file mode 100644 index 0000000..e1b01b5 --- /dev/null +++ b/widget/image/ScaledImage.lua @@ -0,0 +1,31 @@ +local c = {} + +local _IMLIB_LOAD_IMAGE = imlib_load_image +local _IMLIB_CONTEXT_SET_IMAGE = imlib_context_set_image +local _IMLIB_RENDER_IMAGE_ON_DRAWABLE_AT_SIZE = imlib_render_image_on_drawable_at_size +local _IMLIB_FREE_IMAGE = imlib_free_image +local _IMLIB_IMAGE_GET_WIDTH = imlib_image_get_width +local _IMLIB_IMAGE_GET_HEIGHT = imlib_image_get_height + +local set = function(obj, path) + local img = _IMLIB_LOAD_IMAGE(path) + _IMLIB_CONTEXT_SET_IMAGE(img) + + obj.img_width = _IMLIB_IMAGE_GET_WIDTH() + obj.img_height = _IMLIB_IMAGE_GET_HEIGHT() + obj.path = path + + _IMLIB_FREE_IMAGE() +end + +local draw = function(obj) + local img = _IMLIB_LOAD_IMAGE(obj.path) + _IMLIB_CONTEXT_SET_IMAGE(img) + _IMLIB_RENDER_IMAGE_ON_DRAWABLE_AT_SIZE(obj.x, obj.y, obj.width, obj.height) + _IMLIB_FREE_IMAGE() +end + +c.set = set +c.draw = draw + +return c diff --git a/widget/plot/LabelPlot.lua b/widget/plot/LabelPlot.lua new file mode 100644 index 0000000..fcedc4b --- /dev/null +++ b/widget/plot/LabelPlot.lua @@ -0,0 +1,110 @@ +local c = {} + +local Plot = require 'Plot' +local Text = require 'Text' + +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_SET_FONT_FACE = cairo_set_font_face +local _CAIRO_SET_FONT_SIZE = cairo_set_font_size +local _CAIRO_MOVE_TO = cairo_move_to +local _CAIRO_SHOW_TEXT = cairo_show_text +local _CAIRO_STROKE = cairo_stroke + +local X_LABEL_PAD = 8 +local Y_LABEL_PAD = 5 + +local draw = function(obj, cr) + local labels_x = obj.labels.x + local labels_y = obj.labels.y + local labels_x_1 = labels_x[1] + + _CAIRO_SET_FONT_FACE(cr, labels_x_1.font_face) + _CAIRO_SET_FONT_SIZE(cr, labels_x_1.font_size) + _CAIRO_SET_SOURCE(cr, labels_x_1.source) + + for i = 1, #labels_x do + local current_label = labels_x[i] + _CAIRO_MOVE_TO(cr, current_label.x, current_label.y) + _CAIRO_SHOW_TEXT(cr, current_label.text) + end + + for i = 1, #labels_y do + local current_label = labels_y[i] + _CAIRO_MOVE_TO(cr, current_label.x, current_label.y) + _CAIRO_SHOW_TEXT(cr, current_label.text) + end + + Plot.draw(obj.plot, cr) +end + +local populate_x_labels = function(obj, cr, input_factor) + local labels_x = obj.labels.x + local n = #labels_x - 1 + input_factor = input_factor or 1 + for i = 0, n do + Text.set(labels_x[i + 1], cr, labels_x._func(input_factor * i / n)) + end + labels_x.height = labels_x[1].height + X_LABEL_PAD + local plot = obj.plot + plot.height = obj.height - labels_x.height + plot.bottom_y = plot.height - plot.y +end + +local __get_y_axis_width = function(obj) + local labels_y = obj.labels.y + local width = labels_y[1].width + for i = 2, #labels_y do + local current_width = labels_y[i].width + if current_width > width then + width = current_width + end + end + return width + Y_LABEL_PAD +end + +local populate_y_labels = function(obj, cr, input_factor) + local labels_y = obj.labels.y + local n = #labels_y - 1 + input_factor = input_factor or 1 + for i = 0, n do + Text.set(labels_y[i + 1], cr, labels_y._func(input_factor * (n - i) / n)) + end + labels_y.width = __get_y_axis_width(obj) + + local plot = obj.plot + plot.x = obj.x + labels_y.width + plot.width = obj.width - labels_y.width +end + +local position_x_labels = function(obj) + local start_x = obj.plot.x + local labels_x = obj.labels.x + local x_intrvl_width = obj.plot.width / (#labels_x - 1) + for i = 1, #labels_x do + Text.move_to_x(labels_x[i], start_x + x_intrvl_width * (i - 1)) + end +end + +local position_y_labels = function(obj) + local labels_y = obj.labels.y + local y_intrvl_height = obj.plot.height / (#labels_y - 1) + for i = 1, #labels_y do + Text.move_to_y(labels_y[i], obj.y + y_intrvl_height * (i - 1)) + end +end + +local update = function(obj, value) + Plot.update(obj.plot, value) +end + +c.update = update +c.draw = draw +c.position_x_intrvls = Plot.position_x_intrvls +c.position_y_intrvls = Plot.position_y_intrvls +c.position_graph_outline = Plot.position_graph_outline +c.populate_x_labels = populate_x_labels +c.populate_y_labels = populate_y_labels +c.position_x_labels = position_x_labels +c.position_y_labels = position_y_labels + +return c diff --git a/widget/plot/Plot.lua b/widget/plot/Plot.lua new file mode 100644 index 0000000..0106e77 --- /dev/null +++ b/widget/plot/Plot.lua @@ -0,0 +1,147 @@ +local c = {} + +local Poly = require 'Poly' + +local _CAIRO_APPEND_PATH = cairo_append_path +local _CAIRO_MOVE_TO = cairo_move_to +local _CAIRO_LINE_TO = cairo_line_to +local _CAIRO_SET_LINE_WIDTH = cairo_set_line_width +local _CAIRO_SET_LINE_CAP = cairo_set_line_cap +local _CAIRO_SET_LINE_JOIN = cairo_set_line_join +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_FILL_PRESERVE = cairo_fill_preserve +local _CAIRO_STROKE = cairo_stroke +local _CAIRO_PATH_DESTROY = cairo_path_destroy +local _TABLE_INSERT = table.insert + +local DATA_THICKNESS = 1 +local DATA_CAP = CAIRO_LINE_CAP_BUTT +local DATA_JOIN = CAIRO_LINE_JOIN_MITER +local INTRVL_THICKNESS = 1 +local INTRVL_CAP = CAIRO_LINE_CAP_BUTT +local OUTLINE_THICKNESS = 2 +local OUTLINE_CAP = CAIRO_LINE_CAP_BUTT +local OUTLINE_JOIN = CAIRO_LINE_JOIN_MITER + +local update = function(obj, value) + local data = obj.data + _TABLE_INSERT(data, 1, obj.y + obj.height * (1 - value)) + if #data == data.n + 2 then data[#data] = nil end +end + +local draw = function(obj, cr) + + --draw intervals + local intrvls = obj.intrvls + local x_intrvls = intrvls.x + local y_intrvls = intrvls.y + + _CAIRO_SET_LINE_WIDTH(cr, INTRVL_THICKNESS) + _CAIRO_SET_LINE_CAP(cr, INTRVL_CAP) + _CAIRO_SET_SOURCE(cr, intrvls.source) + for i = 1, #x_intrvls do + _CAIRO_APPEND_PATH(cr, x_intrvls[i]) + end + for i = 1, #y_intrvls do + _CAIRO_APPEND_PATH(cr, y_intrvls[i]) + end + _CAIRO_STROKE(cr) + + --draw data on graph + local data = obj.data + local n = #data - 1 + local spacing = obj.width / data.n + local right = obj.x + obj.width + + _CAIRO_MOVE_TO(cr, right, data[1]) + + for i = 1, n do + _CAIRO_LINE_TO(cr, right - i * spacing, data[i+1]) + end + + if data.fill_source then + local bottom = obj.y + obj.height + _CAIRO_LINE_TO(cr, right - n * spacing, bottom) + _CAIRO_LINE_TO(cr, right, bottom) + _CAIRO_SET_SOURCE(cr, data.fill_source) + _CAIRO_FILL_PRESERVE(cr) + end + + _CAIRO_SET_LINE_WIDTH (cr, DATA_THICKNESS) + _CAIRO_SET_LINE_CAP(cr, DATA_CAP) + _CAIRO_SET_LINE_JOIN(cr, DATA_JOIN) + _CAIRO_SET_SOURCE(cr, data.line_source) + _CAIRO_STROKE(cr) + + --draw graph outline (goes on top of everything) + local outline = obj.outline + + _CAIRO_APPEND_PATH(cr, outline.path) + _CAIRO_SET_LINE_WIDTH(cr, OUTLINE_THICKNESS) + _CAIRO_SET_LINE_JOIN(cr, OUTLINE_JOIN) + _CAIRO_SET_LINE_CAP(cr, OUTLINE_CAP) + _CAIRO_SET_SOURCE(cr, outline.source) + _CAIRO_STROKE(cr) +end + +local position_x_intrvls = function(obj) + local y1 = obj.y - 0.5 + local y2 = y1 + obj.height-- + 0.5 + local x_intrvls = obj.intrvls.x + local intrvl_width = obj.width / x_intrvls.n + local p1 = {x = 0, y = 0} + local p2 = {x = 0, y = 0} + + local obj_x = obj.x + + for i = 1, x_intrvls.n do + local x1 = obj_x + intrvl_width * i-- + 0.5 + p1.x = x1 + p1.y = y1 + p2.x = x1 + p2.y = y2 + _CAIRO_PATH_DESTROY(x_intrvls[i]) + x_intrvls[i] = Poly.create_path(nil, p1, p2) + end +end + +local position_y_intrvls = function(obj) + local x1 = obj.x-- + 0.5 + local x2 = obj.x + obj.width-- + 0.5 + local y_intrvls = obj.intrvls.y + local y_intrvl_height = obj.height / y_intrvls.n + local p1 = {x = 0, y = 0} + local p2 = {x = 0, y = 0} + + for i = 1, y_intrvls.n do + local y1 = obj.y + (i - 1) * y_intrvl_height - 0.5 + p1.x = x1 + p1.y = y1 + p2.x = x2 + p2.y = y1 + _CAIRO_PATH_DESTROY(y_intrvls[i]) + y_intrvls[i] = Poly.create_path(nil, p1, p2) + end +end + +local position_graph_outline = function(obj) + local x1 = obj.x + local y1 = obj.y - 0.5 + local x2 = obj.x + obj.width + 0.5 + local y2 = obj.y + obj.height + 1.0 + local p1 = {x = x1, y = y1} + local p2 = {x = x1, y = y2} + local p3 = {x = x2, y = y2} + + _CAIRO_PATH_DESTROY(obj.outline.path) + + obj.outline.path = Poly.create_path(nil, p1, p2, p3) +end + +c.draw = draw +c.update = update +c.position_x_intrvls = position_x_intrvls +c.position_y_intrvls = position_y_intrvls +c.position_graph_outline = position_graph_outline + +return c diff --git a/widget/plot/ScalePlot.lua b/widget/plot/ScalePlot.lua new file mode 100644 index 0000000..88c7d96 --- /dev/null +++ b/widget/plot/ScalePlot.lua @@ -0,0 +1,89 @@ +local c = {} + +local LabelPlot = require 'LabelPlot' + +local _TABLE_INSERT = table.insert +local _TABLE_REMOVE = table.remove + +local __scale_data = function(obj, cr, new_domain, new_factor) + local y = obj.y + local current_factor = obj.scale.factor + local data = obj.plot.data + local h = obj.plot.height + for i = 1, #data do + data[i] = y + h * (1 - (1 - (data[i] - y) / h) * (new_factor / current_factor)) + end + obj.scale.domain = new_domain + obj.scale.factor = new_factor + LabelPlot.populate_y_labels(obj, cr, 1 / new_factor) + LabelPlot.position_x_labels(obj) + LabelPlot.position_x_intrvls(obj.plot) + LabelPlot.position_y_intrvls(obj.plot) + LabelPlot.position_graph_outline(obj.plot) +end + +local update = function(obj, cr, value) + local scale = obj.scale + local new_domain, new_factor = obj.scale._func(value) + + --###tick/tock timers + + local timers = scale.timers + local n = #timers + for i = n, 1, -1 do + local current_timer = timers[i] + current_timer.remaining = current_timer.remaining - 1 + if current_timer.remaining == 0 then + _TABLE_REMOVE(timers, i) + n = n - 1 + end + end + + --###create/destroy timers + if new_domain > scale.previous_domain then --zap all timers less than/equal to s + for i = n, 1, -1 do + if timers[i].domain <= new_domain then + _TABLE_REMOVE(timers, i) + n = n - 1 + end + end + elseif new_domain < scale.previous_domain then --create new timer for prev_s + timers[n + 1] = { + domain = scale.previous_domain, + factor = scale.previous_factor, + remaining = obj.plot.data.n + } + n = n + 1 + end + + --###scale data + + if new_domain > scale.domain then --scale up + __scale_data(obj, cr, new_domain, new_factor) + elseif new_domain < scale.domain then --check timers + if n == 0 then --scale down bc no timers to block + __scale_data(obj, cr, new_domain, new_factor) + elseif scale.timers[1].domain < scale.domain then --scale down to active timer + __scale_data(obj, cr, scale.timers[1].domain, scale.timers[1].factor) + end + end + + scale.previous_domain = new_domain + scale.previous_factor = new_factor + + local data = obj.plot.data + + _TABLE_INSERT(data, 1, obj.y + obj.plot.height * (1 - value * scale.factor)) + if #data == data.n + 2 then data[#data] = nil end + --~ print('----------------------------------------------------------------------') + --~ print('value', value, 'f', scale.factor, 's', scale.domain, 'curr_s', scale.previous_domain) + --~ for i, v in pairs(timers) do + --~ print('timers', 'i', i, 's', v.domain, 't', v.remaining, 'f', v.factor) + --~ end + --~ print('length', #timers) +end + +c.draw = LabelPlot.draw +c.update = update + +return c diff --git a/widget/poly/Bar.lua b/widget/poly/Bar.lua new file mode 100644 index 0000000..887bd82 --- /dev/null +++ b/widget/poly/Bar.lua @@ -0,0 +1,36 @@ +local c = {} + +local _CAIRO_APPEND_PATH = cairo_append_path +local _CAIRO_SET_LINE_WIDTH = cairo_set_line_width +local _CAIRO_SET_LINE_CAP = cairo_set_line_cap +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_STROKE = cairo_stroke + +local set = function(obj, percent) + obj.percent = percent + obj.bar_path = obj._make_bar_path(percent) + + if obj.critical.enabled(percent) then + obj.current_source = obj.critical.source + else + obj.current_source = obj.indicator_source + end +end + +local draw = function(obj, cr) + _CAIRO_SET_LINE_WIDTH(cr, obj.thickness) + _CAIRO_SET_LINE_CAP(cr, obj.cap) + + _CAIRO_APPEND_PATH(cr, obj.path) + _CAIRO_SET_SOURCE(cr, obj.source) + _CAIRO_STROKE(cr) + + _CAIRO_APPEND_PATH(cr, obj.bar_path) + _CAIRO_SET_SOURCE(cr, obj.current_source) + _CAIRO_STROKE(cr) +end + +c.set = set +c.draw = draw + +return c diff --git a/widget/poly/CompoundBar.lua b/widget/poly/CompoundBar.lua new file mode 100644 index 0000000..2cd4a6b --- /dev/null +++ b/widget/poly/CompoundBar.lua @@ -0,0 +1,34 @@ +local c = {} + +local Bar = require 'Bar' + +local _CAIRO_SET_LINE_WIDTH = cairo_set_line_width +local _CAIRO_SET_LINE_CAP = cairo_set_line_cap +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_STROKE = cairo_stroke +local _CAIRO_APPEND_PATH = cairo_append_path + +local set = function(obj, index, percent) + Bar.set(obj.bars[index], percent) +end + +local draw = function(obj, cr) + local first_bar = obj.bars[1] + _CAIRO_SET_LINE_WIDTH(cr, first_bar.thickness) + _CAIRO_SET_LINE_CAP(cr, first_bar.cap) + + for i = 1, obj.bars.n do + local bar = obj.bars[i] + _CAIRO_SET_SOURCE(cr, bar.source) + _CAIRO_APPEND_PATH(cr, bar.path) + _CAIRO_STROKE(cr) + _CAIRO_SET_SOURCE(cr, bar.current_source) + _CAIRO_APPEND_PATH(cr, bar.bar_path) + _CAIRO_STROKE(cr) + end +end + +c.set = set +c.draw = draw + +return c diff --git a/widget/poly/Line.lua b/widget/poly/Line.lua new file mode 100644 index 0000000..c5a6f32 --- /dev/null +++ b/widget/poly/Line.lua @@ -0,0 +1,19 @@ +local c = {} + +local _CAIRO_APPEND_PATH = cairo_append_path +local _CAIRO_SET_LINE_WIDTH = cairo_set_line_width +local _CAIRO_SET_LINE_CAP = cairo_set_line_cap +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_STROKE = cairo_stroke + +local draw = function(obj, cr) + _CAIRO_APPEND_PATH(cr, obj.path) + _CAIRO_SET_LINE_WIDTH(cr, obj.thickness) + _CAIRO_SET_LINE_CAP(cr, obj.cap) + _CAIRO_SET_SOURCE(cr, obj.source) + _CAIRO_STROKE(cr) +end + +c.draw = draw + +return c diff --git a/widget/poly/Poly.lua b/widget/poly/Poly.lua new file mode 100644 index 0000000..b647fba --- /dev/null +++ b/widget/poly/Poly.lua @@ -0,0 +1,39 @@ +local c = {} + +local _CR = require 'CR' + +local _CAIRO_NEW_PATH = cairo_new_path +local _CAIRO_MOVE_TO = cairo_move_to +local _CAIRO_LINE_TO = cairo_line_to +local _CAIRO_CLOSE_PATH = cairo_close_path +local _CAIRO_APPEND_PATH = cairo_append_path +local _CAIRO_COPY_PATH = cairo_copy_path +local _CAIRO_SET_LINE_WIDTH = cairo_set_line_width +local _CAIRO_SET_LINE_CAP = cairo_set_line_cap +local _CAIRO_SET_LINE_JOIN = cairo_set_line_join +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_STROKE = cairo_stroke + +local create_path = function(closed, ...) + _CAIRO_NEW_PATH(_CR) + _CAIRO_MOVE_TO(_CR, arg[1].x, arg[1].y) + for i = 2, #arg do + _CAIRO_LINE_TO(_CR, arg[i].x, arg[i].y) + end + if closed then _CAIRO_CLOSE_PATH(_CR) end + return _CAIRO_COPY_PATH(_CR) +end + +local draw = function(obj, cr) + _CAIRO_APPEND_PATH(cr, obj.path) + _CAIRO_SET_LINE_WIDTH(cr, obj.thickness) + _CAIRO_SET_LINE_JOIN(cr, obj.join) + _CAIRO_SET_LINE_CAP(cr, obj.cap) + _CAIRO_SET_SOURCE(cr, obj.source) + _CAIRO_STROKE(cr) +end + +c.create_path = create_path +c.draw = draw + +return c diff --git a/widget/rect/FillRect.lua b/widget/rect/FillRect.lua new file mode 100644 index 0000000..d7a48bf --- /dev/null +++ b/widget/rect/FillRect.lua @@ -0,0 +1,24 @@ +local c = {} + +local _CAIRO_APPEND_PATH = cairo_append_path +local _CAIRO_SET_LINE_WIDTH = cairo_set_line_width +local _CAIRO_SET_LINE_JOIN = cairo_set_line_join +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_FILL_PRESERVE = cairo_fill_preserve +local _CAIRO_STROKE = cairo_stroke + +local draw = function(obj, cr) + _CAIRO_APPEND_PATH(cr, obj.path) + _CAIRO_SET_LINE_WIDTH(cr, obj.thickness) + _CAIRO_SET_LINE_JOIN(cr, obj.join) + + _CAIRO_SET_SOURCE(cr, obj.fill_source) + _CAIRO_FILL_PRESERVE(cr) + + _CAIRO_SET_SOURCE(cr, obj.source) + _CAIRO_STROKE(cr) +end + +c.draw = draw + +return c diff --git a/widget/rect/Rect.lua b/widget/rect/Rect.lua new file mode 100644 index 0000000..488576e --- /dev/null +++ b/widget/rect/Rect.lua @@ -0,0 +1,19 @@ +local c = {} + +local _CAIRO_APPEND_PATH = cairo_append_path +local _CAIRO_SET_LINE_WIDTH = cairo_set_line_width +local _CAIRO_SET_LINE_JOIN = cairo_set_line_join +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_STROKE = cairo_stroke + +local draw = function(obj, cr) + _CAIRO_APPEND_PATH(cr, obj.path) + _CAIRO_SET_LINE_WIDTH(cr, obj.thickness) + _CAIRO_SET_LINE_JOIN(cr, obj.join) + _CAIRO_SET_SOURCE(cr, obj.source) + _CAIRO_STROKE(cr) +end + +c.draw = draw + +return c diff --git a/widget/text/CriticalText.lua b/widget/text/CriticalText.lua new file mode 100644 index 0000000..415abfd --- /dev/null +++ b/widget/text/CriticalText.lua @@ -0,0 +1,29 @@ +local c = {} + +local Text = require 'Text' + +local _TONUMBER = tonumber + +local set = function(obj, cr, text, force) + if text and text ~= obj.pretext then + obj.value = _TONUMBER(text) or 0 + + if force == 0 then + obj.current_source = obj.critical.source + elseif force == 1 then + obj.current_source = obj.source + else + if obj.critical.enabled(obj.value) then + obj.current_source = obj.critical.source + else + obj.current_source = obj.source + end + end + Text.set(obj, cr, text) + end +end + +c.draw = Text.draw +c.set = set + +return c diff --git a/widget/text/Table.lua b/widget/text/Table.lua new file mode 100644 index 0000000..31efc83 --- /dev/null +++ b/widget/text/Table.lua @@ -0,0 +1,73 @@ +local c = {} + +local TextColumn = require 'TextColumn' +local Rect = require 'Rect' + +local _CAIRO_SET_LINE_WIDTH = cairo_set_line_width +local _CAIRO_SET_LINE_CAP = cairo_set_line_cap +local _CAIRO_APPEND_PATH = cairo_append_path +local _CAIRO_STROKE = cairo_stroke +local _CAIRO_SET_FONT_FACE = cairo_set_font_face +local _CAIRO_SET_FONT_SIZE = cairo_set_font_size +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_MOVE_TO = cairo_move_to +local _CAIRO_SHOW_TEXT = cairo_show_text + +local set = function(obj, cr, col_num, row_num, text) + local column = obj.table.columns[col_num] + TextColumn.set(column, cr, row_num, text) +end + +local draw = function(obj, cr) + --draw rectangle + Rect.draw(obj, cr) + + --draw headers + local tbl = obj.table + local columns = tbl.columns + + local first_header = columns[1].header + _CAIRO_SET_SOURCE(cr, first_header.source) + _CAIRO_SET_FONT_FACE(cr, first_header.font_face) + _CAIRO_SET_FONT_SIZE(cr, first_header.font_size) + + for c = 1, tbl.num_columns do + local header = columns[c].header + _CAIRO_MOVE_TO(cr, header.x, header.y) + _CAIRO_SHOW_TEXT(cr, header.text) + end + + --draw rows + local first_cell = columns[1].rows[1] + _CAIRO_SET_SOURCE(cr, first_cell.source) + _CAIRO_SET_FONT_FACE(cr, first_cell.font_face) + _CAIRO_SET_FONT_SIZE(cr, first_cell.font_size) + + for c = 1, tbl.num_columns do + local rows = columns[c].rows + for r = 1, rows.n do + local row = rows[r] + _CAIRO_MOVE_TO(cr, row.x, row.y) + _CAIRO_SHOW_TEXT(cr, row.text) + end + end + + --draw separators + local separators = tbl.separators + + local first_separator = separators[1] + _CAIRO_SET_SOURCE(cr, first_separator.source) + _CAIRO_SET_LINE_WIDTH(cr, first_separator.thickness) + _CAIRO_SET_LINE_CAP(cr, first_separator.cap) + + for i = 1, separators.n do + local line = separators[i] + _CAIRO_APPEND_PATH(cr, line.path) + _CAIRO_STROKE(cr) + end +end + +c.set = set +c.draw = draw + +return c diff --git a/widget/text/Text.lua b/widget/text/Text.lua new file mode 100644 index 0000000..ac2812a --- /dev/null +++ b/widget/text/Text.lua @@ -0,0 +1,84 @@ +local c = {} + +local _STRING_SUB = string.sub +local _CAIRO_SET_FONT_FACE = cairo_set_font_face +local _CAIRO_SET_FONT_SIZE = cairo_set_font_size +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_MOVE_TO = cairo_move_to +local _CAIRO_SHOW_TEXT = cairo_show_text +local _CAIRO_TEXT_EXTENTS = cairo_text_extents + +local te = cairo_text_extents_t:create() +tolua.takeownership(te) + +local trim_to_length = function(text, len) + if #text > len then + return _STRING_SUB(text, 1, len)..'...' + else + return text + end +end + +local draw = function(obj, cr) + _CAIRO_SET_FONT_FACE(cr, obj.font_face) + _CAIRO_SET_FONT_SIZE(cr, obj.font_size) + _CAIRO_SET_SOURCE(cr, obj.current_source) + _CAIRO_MOVE_TO(cr, obj.x, obj.y) + _CAIRO_SHOW_TEXT(cr, obj.text) +end + +local set = function(obj, cr, text) + if text and text ~= obj.pretext then + obj.pretext = text + + if obj.append_front then text = obj.append_front..text end + if obj.append_end then text = text..obj.append_end end + + if text ~= obj.text then + local x_align = obj.x_align + local te = te + + _CAIRO_SET_FONT_SIZE(cr, obj.font_size) + _CAIRO_SET_FONT_FACE(cr, obj.font_face) + _CAIRO_TEXT_EXTENTS(cr, text, te) + + obj.width = te.width + + if x_align == 'left' then obj.delta_x = -te.x_bearing + elseif x_align == 'center' then obj.delta_x = -(te.x_bearing + obj.width * 0.5) + elseif x_align == 'right' then obj.delta_x = -(te.x_bearing + obj.width) + end + + obj.x = obj.x_ref + obj.delta_x + end + obj.text = text + end +end + +local move_to_x = function(obj, x) + if x ~= obj.x then + obj.x_ref = x + obj.x = x + obj.delta_x + end +end + +local move_to_y = function(obj, y) + if y ~= obj.y then + obj.y_ref = y + obj.y = y + obj.delta_y + end +end + +local move_to = function(obj, x, y) + move_to_X(obj, x) + move_to_Y(obj, y) +end + +c.trim_to_length = trim_to_length +c.set = set +c.draw = draw +c.move_to = move_to +c.move_to_x = move_to_x +c.move_to_y = move_to_y + +return c diff --git a/widget/text/TextColumn.lua b/widget/text/TextColumn.lua new file mode 100644 index 0000000..bcb0b92 --- /dev/null +++ b/widget/text/TextColumn.lua @@ -0,0 +1,38 @@ +local c = {} + +local Text = require 'Text' + +local _CAIRO_SET_FONT_FACE = cairo_set_font_face +local _CAIRO_SET_FONT_SIZE = cairo_set_font_size +local _CAIRO_SET_SOURCE = cairo_set_source +local _CAIRO_MOVE_TO = cairo_move_to +local _CAIRO_SHOW_TEXT = cairo_show_text +local _STRING_SUB = string.sub + +local set = function(obj, cr, row_num, text) + if obj.max_length then + Text.set(obj.rows[row_num], cr, Text.trim_to_length(text, obj.max_length)) + else + Text.set(obj.rows[row_num], cr, text) + end +end + +local draw = function(obj, cr) + local rep_row = obj.rows[1] + _CAIRO_SET_FONT_FACE(cr, rep_row.font_face) + _CAIRO_SET_FONT_SIZE(cr, rep_row.font_size) + _CAIRO_SET_SOURCE(cr, rep_row.source) + + local rows = obj.rows + + for i = 1, rows.n do + local row = rows[i] + _CAIRO_MOVE_TO(cr, row.x, row.y) + _CAIRO_SHOW_TEXT(cr, row.text) + end +end + +c.set = set +c.draw = draw + +return c