diff --git a/gui/gui.lua b/gui/gui.lua index 082a8c7..d76890d 100644 --- a/gui/gui.lua +++ b/gui/gui.lua @@ -3,12 +3,12 @@ -- this file contains code controlling GUI of test runner -include "gui/tree.lua" include "gui/text_output.lua" include "gui/lights.lua" include "gui/test_summary.lua" include "gui/test_toolbar.lua" include "gui/printed_lines.lua" +include "gui/tree.lua" local width = 280 local height = 200 @@ -65,12 +65,17 @@ local function start_test(item) y = 97, width = width, height = 103, + bg_color = 0, lines_len = function() if selected_test_id == nil then return 0 end return printed_lines:lines_len(selected_test_id) end, get_line = function(line_no) - return printed_lines:line(selected_test_id, line_no) + return { + text = printed_lines:line(selected_test_id, line_no), + bg_color = 0, + fg_color = 7, + } end, is_link = function(line_no) local text = printed_lines:line(selected_test_id, line_no) @@ -100,7 +105,7 @@ local function start_test(item) local function select_test(test_id) selected_test_id = test_id - text_output:scroll_to_the_top() + text_output:scroll_to_line(1) lights:detach() test_summary:detach() @@ -122,11 +127,14 @@ local function start_test(item) test_tree = attach_tree( gui, - { x = 0, y = 16, width = width, height = 80 } + { + x = 0, + y = 16, + width = width, + height = 80, + select = select_test + } ) - function test_tree:select(e) - select_test(e.id) - end attach_toolbar( gui, @@ -328,5 +336,8 @@ function _draw() if gui != nil then cls() gui:draw_all() + -- debug fps and memory usage: + -- print(stat(7), 250, 0, 1) + -- print(stat(0), 220, 10, 1) end end diff --git a/gui/text_output.lua b/gui/text_output.lua index c9d6a7f..ff91880 100644 --- a/gui/text_output.lua +++ b/gui/text_output.lua @@ -1,7 +1,7 @@ -- (c) 2024 Jacek Olszak -- This code is licensed under MIT license (see LICENSE for details) ----@param el {x:number, y:number, width:number, height:number, is_link:function, link_click:function, get_line:function, lines_len:function} +---@param el {x:number, y:number, width:number, height:number, bg_color:integer, is_link:function, link_click:function, get_line:function, lines_len:function} function attach_text_output(parent, el) local line_height = 10 @@ -51,7 +51,10 @@ function attach_text_output(parent, el) for i = line_no, last_line do local line = el.get_line(i + 1) - print(line, 0, i * line_height, 7) + rectfill(0, i * line_height, + text_output.width, (i + 1) * line_height + 1, + line.bg_color) + print(line.text, 1, i * line_height + 1, line.fg_color) end end @@ -59,11 +62,21 @@ function attach_text_output(parent, el) self.y += e.wheel_y * 32 end - function el:scroll_to_the_top() - text_output.y = 0 + function el:scroll_to_line(line_no) + text_output.y = -(line_height * (line_no - 1)) + if text_output.y == 0 then + return + end + text_output.y += el.height / 2 - line_height + if -text_output.y + el.height > text_output.height then + text_output.y = -text_output.height + el.height + end end - function el:draw() end + function el:draw() + -- draw background (needed when tree height is too low) + rectfill(0, 0, el.width, el.height, el.bg_color) + end return el end diff --git a/gui/tree.lua b/gui/tree.lua index 6769755..ba8a233 100644 --- a/gui/tree.lua +++ b/gui/tree.lua @@ -1,120 +1,70 @@ ---[[pod_format="raw",created="2024-11-09 17:24:35",modified="2024-11-09 17:24:35",revision=0]] --- (c) 2024 Jacek Olszak -- This code is licensed under MIT license (see LICENSE for details) ---@param el {x:number,y:number,width:number,height:number,select:function} function attach_tree(parent_el, el) - local bg_color = 7 - local fg_color = 13 - local highlight_bg_color = 1 - local highlight_fg_color = 7 - - local node_height = 10 - - local nodes_by_id = {} - - local selected_child - - el = parent_el:attach(el) - - local root_node = el:attach( - { width = el.width, height = 0, x = 0, y = 0 } - ) - root_node.indent = "" - el:attach_scrollbars { autohide = true } + include "gui/tree_provider.lua" + + local provider = new_tree_provider() + + local selected_line = nil + + local tree = attach_text_output(parent_el, { + x = el.x, + y = el.y, + width = el.width, + height = el.height, + bg_color = 7, + get_line = function(line_no) + local node = provider:get_node(line_no) + local whitespace = " " + + local fg_color = 13 + local bg_color = 7 + if selected_line != nil and selected_line == line_no then + fg_color = 7 + bg_color = 1 + end - function el:add_child(id, text, parent_id) - local parent - if parent_id == nil then - parent = root_node - else - parent = nodes_by_id[parent_id] - end + local prefix = whitespace:rep(node.depth * 2) + if node.has_children then + prefix = prefix .. "[-] " + else + prefix = prefix .. " " + end + local text = prefix .. node.text - local child = parent:attach( - { - width = parent.width, - height = node_height, - x = 0, - y = parent.height + return { + text = text, + bg_color = bg_color, + fg_color = fg_color, } - ) - child.text = text - child.indent = parent.indent .. " " - if parent_id == nil then - child.indent = "" -- root element should not have an indent - end - function child:draw(msg) - if child == selected_child then - pal(bg_color, highlight_bg_color); - pal(fg_color, highlight_fg_color) - end - local prefix = "[-] " - if #child.child == 0 then - prefix = " " - end - rectfill(0, 0, self.width, node_height, bg_color) - print(child.indent .. prefix .. self.text, 0, 1, fg_color) - pal() - end - - function child:click() - selected_child = child - el:select { id = id } + end, + is_link = function(line_no) return true + end, + link_click = function(line_no) + selected_line = line_no + el.select(provider:get_node(line_no).id) + end, + lines_len = function() + return provider:nodes_len() end + }) - local function add_height(parent, h) - parent.height += h - if parent._parent != nil then - add_height(parent._parent, h) - end - end - - add_height(parent, node_height) - - -- use _parent beause parent is already used by Picotron: - child._parent = parent - nodes_by_id[id] = child - child.cursor = "pointer" - end - - function el:draw() - rectfill(0, 0, el.width, el.height, bg_color) - end - - function el:update_child_text(id, text) - nodes_by_id[id].text = text + function tree:add_child(id, text, parent_id) + provider:append_node(parent_id, id, text) end - local function child_y_relative_to_root_node(child) - local y = child.y - while child.parent != root_node do - y += child.parent.y - child = child.parent - end - return y - end - - local function scroll_to_child(child) - local scroll_root_node = - -child_y_relative_to_root_node(child) + (el.height / 2) - if scroll_root_node > 0 then - scroll_root_node = 0 - end - local max_scroll = -root_node.height + el.height - if scroll_root_node <= max_scroll then - scroll_root_node = max_scroll - end - - root_node.y = scroll_root_node + function tree:update_child_text(id, text) + provider:update_node_text(id, text) end - function el:select_child(id) - selected_child = nodes_by_id[id] - scroll_to_child(selected_child) + function tree:select_child(id) + local line = provider:get_line_no(id) + selected_line = line + tree:scroll_to_line(line) end - return el + return tree end diff --git a/gui/tree_provider.lua b/gui/tree_provider.lua new file mode 100644 index 0000000..1e0eecf --- /dev/null +++ b/gui/tree_provider.lua @@ -0,0 +1,48 @@ +-- (c) 2024 Jacek Olszak +-- This code is licensed under MIT license (see LICENSE for details) + +--- returns a new instance of data structure holding tree of nodes. This object +--- is used by tree component. The code was extracted from the tree component +--- because the component was to complex (and will be even more complex +--- when tree will have node collapsing and hiding functionality). +function new_tree_provider() + local p = {} + + local nodes_by_line = {} + local nodes_by_id = {} + + function p:nodes_len() + return #nodes_by_line + end + + function p:get_node(line_no) + return nodes_by_line[line_no] + end + + function p:get_line_no(id) + for line_no, node in ipairs(nodes_by_line) do + if node.id == id then + return line_no + end + end + + return nil + end + + function p:append_node(parent_id, id, text) + local node = { text = text, depth = 0, id = id, has_children = false } + if parent_id != nil then + local parent = nodes_by_id[parent_id] + parent.has_children = true + node.depth = parent.depth + 1 + end + nodes_by_id[node.id] = node + table.insert(nodes_by_line, node) + end + + function p:update_node_text(id, new_text) + nodes_by_id[id].text = new_text + end + + return p +end diff --git a/gui/tree_provider_test.lua b/gui/tree_provider_test.lua new file mode 100644 index 0000000..5a709c0 --- /dev/null +++ b/gui/tree_provider_test.lua @@ -0,0 +1,39 @@ +-- (c) 2024 Jacek Olszak +-- This code is licensed under MIT license (see LICENSE for details) + +include "tree_provider.lua" + +test("new provider has 0 lines", function() + local p = new_tree_provider() + assert_eq(0, p:nodes_len()) +end) + +test("add root", function() + local p = new_tree_provider() + p:append_node(nil, 1, "root") + assert_eq(1, p:nodes_len()) + assert_eq({ text = "root", depth = 0, id = 1, has_children = false }, p:get_node(1)) + assert_eq(1, p:get_line_no(1)) +end) + +test("add child node", function() + local p = new_tree_provider() + p:append_node(nil, 1, "root") + -- when + p:append_node(1, 2, "child") + -- then + assert_eq(2, p:nodes_len()) + assert_eq({ text = "child", depth = 1, id = 2, has_children = false }, p:get_node(2)) + assert_eq(2, p:get_line_no(2)) + -- and + assert(p:get_node(1).has_children) +end) + +test("update node text", function() + local p = new_tree_provider() + p:append_node(nil, 1, "root") + -- when + p:update_node_text(1, "updated") + -- then + assert_eq("updated", p:get_node(1).text) +end)