From 37190684c471be5860d64476111ed8bd204848fc Mon Sep 17 00:00:00 2001 From: Insality Date: Tue, 19 Nov 2024 00:48:15 +0200 Subject: [PATCH] Add more widgets --- druid/color.lua | 222 +++++++++++++++++++++ druid/extended/layout.lua | 12 +- druid/widget/memory_panel/memory_panel.gui | 58 ++++++ druid/widget/memory_panel/memory_panel.lua | 22 ++ druid/widget/mini_graph/mini_graph.gui | 171 ++++++++++++++++ druid/widget/mini_graph/mini_graph.lua | 88 ++++++++ 6 files changed, 570 insertions(+), 3 deletions(-) create mode 100644 druid/color.lua create mode 100644 druid/widget/memory_panel/memory_panel.gui create mode 100644 druid/widget/memory_panel/memory_panel.lua create mode 100644 druid/widget/mini_graph/mini_graph.gui create mode 100644 druid/widget/mini_graph/mini_graph.lua diff --git a/druid/color.lua b/druid/color.lua new file mode 100644 index 0000000..d677233 --- /dev/null +++ b/druid/color.lua @@ -0,0 +1,222 @@ +---@type table> +local PALETTE_DATA +local CURRENT_PALETTE = "default" +local DEFAULT_COLOR = vmath.vector4(1, 1, 1, 1) +local COLOR_X = hash("color.x") +local COLOR_Y = hash("color.y") +local COLOR_Z = hash("color.z") + +local M = {} + + +---Get color color by id +---@param color_id string +---@return vector4 +function M.get(color_id) + return PALETTE_DATA[CURRENT_PALETTE] and PALETTE_DATA[CURRENT_PALETTE][color_id] or DEFAULT_COLOR +end + + +---Add palette to palette data +---@param palette_name string +---@param palette_data table +function M.add_palette(palette_name, palette_data) + PALETTE_DATA[palette_name] = PALETTE_DATA[palette_name] or {} + local palette = PALETTE_DATA[palette_name] + + for color_id, color in pairs(palette_data) do + if type(color) == "string" then + palette[color_id] = M.hex2vector4(color) + else + palette[color_id] = color + end + end +end + + +function M.set_palette(palette_name) + if PALETTE_DATA[palette_name] then + CURRENT_PALETTE = palette_name + end +end + + +function M.get_palette() + return CURRENT_PALETTE +end + + +---Set color of gui node without changing alpha +---@param gui_node node +---@param color vector4|vector3|string Color in vector4, vector3 or color id from palette +function M.set_color(gui_node, color) + if type(color) == "string" then + color = M.get(color) + end + + gui.set(gui_node, COLOR_X, color.x) + gui.set(gui_node, COLOR_Y, color.y) + gui.set(gui_node, COLOR_Z, color.z) +end + + +function M.get_random_color() + return vmath.vector4(math.random(), math.random(), math.random(), 1) +end + + +---Lerp colors via color HSB values +function M.lerp(t, color1, color2) + local h1, s1, v1 = M.rgb2hsb(color1.x, color1.y, color1.z) + local h2, s2, v2 = M.rgb2hsb(color2.x, color2.y, color2.z) + + local h = h1 + (h2 - h1) * t + local s = s1 + (s2 - s1) * t + local v = v1 + (v2 - v1) * t + + local r, g, b, a = M.hsb2rgb(h, s, v) + a = a or 1 + return vmath.vector4(r, g, b, a) +end + + +---@param hex string +---@param alpha number|nil +---@return number, number, number, number +function M.hex2rgb(hex, alpha) + alpha = alpha or 1 + if alpha > 1 then + alpha = alpha / 100 + end + + -- Remove leading # + if string.sub(hex, 1, 1) == "#" then + hex = string.sub(hex, 2) + end + + -- Expand 3-digit hex codes to 6 digits + if #hex == 3 then + hex = string.rep(string.sub(hex, 1, 1), 2) .. + string.rep(string.sub(hex, 2, 2), 2) .. + string.rep(string.sub(hex, 3, 3), 2) + end + + local r = tonumber("0x" .. string.sub(hex, 1, 2)) / 255 + local g = tonumber("0x" .. string.sub(hex, 3, 4)) / 255 + local b = tonumber("0x" .. string.sub(hex, 5, 6)) / 255 + return r, g, b, alpha +end + + +---@param hex string +---@param alpha number|nil +---@return vector4 +function M.hex2vector4(hex, alpha) + local r, g, b, a = M.hex2rgb(hex, alpha) + return vmath.vector4(r, g, b, a) +end + + +---Convert hsb color to rgb color +---@param r number @Red value +---@param g number @Green value +---@param b number @Blue value +---@param alpha number|nil @Alpha value. Default is 1 +function M.rgb2hsb(r, g, b, alpha) + alpha = alpha or 1 + local min, max = math.min(r, g, b), math.max(r, g, b) + local delta = max - min + local h, s, v = 0, max, max + + s = max ~= 0 and delta / max or 0 + + if delta ~= 0 then + if r == max then + h = (g - b) / delta + elseif g == max then + h = 2 + (b - r) / delta + else + h = 4 + (r - g) / delta + end + h = (h / 6) % 1 + end + + alpha = alpha > 1 and alpha / 100 or alpha + + return h, s, v, alpha +end + + +---Convert hsb color to rgb color +---@param h number @Hue +---@param s number @Saturation +---@param v number @Value +---@param alpha number|nil @Alpha value. Default is 1 +function M.hsb2rgb(h, s, v, alpha) + local r, g, b + local i = math.floor(h * 6) + local f = h * 6 - i + local p = v * (1 - s) + local q = v * (1 - f * s) + local t = v * (1 - (1 - f) * s) + + i = i % 6 + + if i == 0 then r, g, b = v, t, p + elseif i == 1 then r, g, b = q, v, p + elseif i == 2 then r, g, b = p, v, t + elseif i == 3 then r, g, b = p, q, v + elseif i == 4 then r, g, b = t, p, v + elseif i == 5 then r, g, b = v, p, q + end + + return r, g, b, alpha +end + + +---Convert rgb color to hex color +---@param red number @Red value +---@param green number @Green value +---@param blue number @Blue value +function M.rgb2hex(red, green, blue) + local r = string.format("%x", math.floor(red * 255)) + local g = string.format("%x", math.floor(green * 255)) + local b = string.format("%x", math.floor(blue * 255)) + return string.upper((#r == 1 and "0" or "") .. r .. (#g == 1 and "0" or "") .. g .. (#b == 1 and "0" or "") .. b) +end + + +function M.load_palette() + local PALETTE_PATH = sys.get_config_string("fluid.palette") + if PALETTE_PATH then + PALETTE_DATA = M.load_json(PALETTE_PATH) --[[@as table>]] + end + PALETTE_DATA = PALETTE_DATA or {} + + for _, palette_data in pairs(PALETTE_DATA) do + for color_id, color in pairs(palette_data) do + if type(color) == "string" then + palette_data[color_id] = M.hex2vector4(color) + end + end + end +end + + +---Load JSON file from game resources folder (by relative path to game.project) +---Return nil if file not found or error +---@param json_path string +---@return table|nil +function M.load_json(json_path) + local resource, is_error = sys.load_resource(json_path) + if is_error or not resource then + return nil + end + + return json.decode(resource) +end + + +M.load_palette() + +return M \ No newline at end of file diff --git a/druid/extended/layout.lua b/druid/extended/layout.lua index 8eb657c..804e518 100644 --- a/druid/extended/layout.lua +++ b/druid/extended/layout.lua @@ -80,10 +80,16 @@ function M:set_margin(margin_x, margin_y) end ----@param padding vector4 The vector4 with padding values, where x - left, y - top, z - right, w - bottom +---@param padding_x number|nil +---@param padding_y number|nil +---@param padding_z number|nil +---@param padding_w number|nil ---@return druid.layout -function M:set_padding(padding) - self.padding = padding +function M:set_padding(padding_x, padding_y, padding_z, padding_w) + self.padding.x = padding_x or self.padding.x + self.padding.y = padding_y or self.padding.y + self.padding.z = padding_z or padding_x or self.padding.z + self.padding.w = padding_w or padding_y or self.padding.w self.is_dirty = true return self diff --git a/druid/widget/memory_panel/memory_panel.gui b/druid/widget/memory_panel/memory_panel.gui new file mode 100644 index 0000000..c1ed80a --- /dev/null +++ b/druid/widget/memory_panel/memory_panel.gui @@ -0,0 +1,58 @@ +nodes { + size { + x: 200.0 + y: 100.0 + } + type: TYPE_BOX + id: "root" + inherit_alpha: true + size_mode: SIZE_MODE_AUTO + visible: false +} +nodes { + type: TYPE_TEMPLATE + id: "mini_graph" + parent: "root" + inherit_alpha: true + template: "/druid/widget/mini_graph/mini_graph.gui" +} +nodes { + type: TYPE_BOX + id: "mini_graph/root" + parent: "mini_graph" + template_node_child: true +} +nodes { + type: TYPE_TEXT + text: "Memory Panel" + id: "mini_graph/text_header" + parent: "mini_graph/root" + overridden_fields: 8 + template_node_child: true +} +nodes { + type: TYPE_BOX + id: "mini_graph/icon_drag" + parent: "mini_graph/root" + template_node_child: true +} +nodes { + type: TYPE_BOX + id: "mini_graph/panel_diagram" + parent: "mini_graph/root" + template_node_child: true +} +nodes { + type: TYPE_BOX + id: "mini_graph/prefab_line" + parent: "mini_graph/panel_diagram" + template_node_child: true +} +nodes { + type: TYPE_TEXT + id: "mini_graph/text_value" + parent: "mini_graph/root" + template_node_child: true +} +material: "/builtins/materials/gui.material" +adjust_reference: ADJUST_REFERENCE_PARENT diff --git a/druid/widget/memory_panel/memory_panel.lua b/druid/widget/memory_panel/memory_panel.lua new file mode 100644 index 0000000..800447a --- /dev/null +++ b/druid/widget/memory_panel/memory_panel.lua @@ -0,0 +1,22 @@ +local mini_graph = require("druid.widget.mini_graph.mini_graph") + +---@class widget.memory_panel: druid.widget +---@field root node +local M = {} + + +function M:init() + self.druid = self:get_druid() + self.mini_graph = self.druid:new_widget(mini_graph, "mini_graph") + + --for index = 1, 32 do + -- self.mini_graph:set_line_value(index, 0) + --end + + timer.delay(0.1, true, function() + self.mini_graph:push_line_value(math.random()) + end) +end + + +return M \ No newline at end of file diff --git a/druid/widget/mini_graph/mini_graph.gui b/druid/widget/mini_graph/mini_graph.gui new file mode 100644 index 0000000..1de9224 --- /dev/null +++ b/druid/widget/mini_graph/mini_graph.gui @@ -0,0 +1,171 @@ +fonts { + name: "text_regular" + font: "/druid/fonts/text_regular.font" +} +fonts { + name: "text_bold" + font: "/druid/fonts/text_bold.font" +} +textures { + name: "druid" + texture: "/druid/druid.atlas" +} +nodes { + size { + x: 200.0 + y: 140.0 + } + color { + x: 0.173 + y: 0.184 + z: 0.204 + } + type: TYPE_BOX + texture: "druid/ui_circle_16" + id: "root" + inherit_alpha: true + slice9 { + x: 8.0 + y: 8.0 + z: 8.0 + w: 8.0 + } +} +nodes { + position { + x: -92.0 + y: 63.0 + } + scale { + x: 0.5 + y: 0.5 + } + size { + x: 260.0 + y: 50.0 + } + color { + x: 0.463 + y: 0.475 + z: 0.49 + } + type: TYPE_TEXT + text: "Mini Graph" + font: "text_regular" + id: "text_header" + pivot: PIVOT_NW + outline { + x: 1.0 + y: 1.0 + z: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + } + parent: "root" + inherit_alpha: true + outline_alpha: 0.0 + shadow_alpha: 0.0 +} +nodes { + position { + x: 92.0 + y: 67.0 + } + color { + x: 0.129 + y: 0.141 + z: 0.157 + } + type: TYPE_BOX + texture: "druid/ui_circle_32" + id: "icon_drag" + pivot: PIVOT_NE + parent: "root" + inherit_alpha: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + y: -70.0 + } + size { + x: 200.0 + y: 100.0 + } + color { + x: 0.129 + y: 0.141 + z: 0.157 + } + type: TYPE_BOX + texture: "druid/ui_circle_16" + id: "panel_diagram" + pivot: PIVOT_S + parent: "root" + inherit_alpha: true + slice9 { + x: 8.0 + y: 8.0 + z: 8.0 + w: 8.0 + } + clipping_mode: CLIPPING_MODE_STENCIL +} +nodes { + size { + x: 8.0 + y: 70.0 + } + color { + x: 0.957 + y: 0.608 + z: 0.608 + } + type: TYPE_BOX + texture: "druid/pixel" + id: "prefab_line" + pivot: PIVOT_S + parent: "panel_diagram" + inherit_alpha: true +} +nodes { + position { + y: 12.0 + } + scale { + x: 0.7 + y: 0.7 + } + size { + x: 260.0 + y: 40.0 + } + color { + x: 0.463 + y: 0.475 + z: 0.49 + } + type: TYPE_TEXT + text: "120.23 KB" + font: "text_bold" + id: "text_value" + outline { + x: 1.0 + y: 1.0 + z: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + } + parent: "root" + inherit_alpha: true + outline_alpha: 0.0 + shadow_alpha: 0.0 +} +material: "/builtins/materials/gui.material" +adjust_reference: ADJUST_REFERENCE_PARENT diff --git a/druid/widget/mini_graph/mini_graph.lua b/druid/widget/mini_graph/mini_graph.lua new file mode 100644 index 0000000..553a1b8 --- /dev/null +++ b/druid/widget/mini_graph/mini_graph.lua @@ -0,0 +1,88 @@ +local color = require("druid.color") + +---@class widget.mini_graph: druid.widget +local M = {} + +local SIZE_Y = hash("size.y") + + +function M:init() + self.druid = self:get_druid() + self.root = self:get_node("root") + self.container = self.druid:new_container(self.root) + self.text_header = self.druid:new_text("text_header") + self.text_value = self.druid:new_text("text_value") + self.drag_corner = self.druid:new_drag("icon_drag", self.on_drag_corner) + self.layout = self.druid:new_layout("panel_diagram", "horizontal") + :set_margin(0, 0) + :set_padding(0, 0, 0, 0) + + self.prefab_line = self:get_node("prefab_line") + gui.set_enabled(self.prefab_line, false) + + self.color_zero = color.hex2vector4("#8ED59E") + self.color_one = color.hex2vector4("#F49B9B") + + self.lines = {} + self.values = {} + self.samples = 64 + local line_width = self.layout:get_size().x / self.samples + for index = 1, self.samples do + local line = gui.clone(self.prefab_line) + gui.set_enabled(line, true) + gui.set(line, "size.x", line_width) + self.layout:add(line) + table.insert(self.lines, line) + end + + for index = 1, self.samples do + local outsine = index/self.samples + self:set_line_value(index, outsine) + end +end + + +---@param index number +---@param value number The normalized value from 0 to 1 +function M:set_line_value(index, value) + local line = self.lines[index] + if not line then + return + end + + local target_color = color.lerp(value * value, self.color_zero, self.color_one) + gui.set(line, SIZE_Y, value * 70) + gui.set_color(line, target_color) + + self.values[index] = value +end + + +---@return number +function M:get_line_value(index) + return self.values[index] +end + + +function M:push_line_value(value) + for index = 1, self.samples - 1 do + self:set_line_value(index, self:get_line_value(index + 1), true) + end + + self:set_line_value(self.samples, value, true) +end + + +---@param text string +function M:set_text(text) + self.text_value:set_to(text) +end + + +function M:on_drag_corner(dx, dy) + local position = self.container:get_position() + self.container:set_position(position.x + dx, position.y + dy) +end + + +return M \ No newline at end of file