From da0797a8c254444c4f0a233621fc62f5b2daf506 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Wed, 8 Apr 2020 12:36:26 -0500 Subject: [PATCH] Cleaned up some code; performance improvements --- app.lua | 355 +---------------------------------------- src/ui.lua | 20 ++- src/ui/components.lua | 363 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 382 insertions(+), 356 deletions(-) create mode 100644 src/ui/components.lua diff --git a/app.lua b/app.lua index 81d86ce..f1ea2bc 100644 --- a/app.lua +++ b/app.lua @@ -1,364 +1,15 @@ import { Rectangle = "src.utils:Rectangle"; - draw_arrow = "src.utils:draw_arrow"; - wasm_decompile = "src.wasm.decompile:"; wasm_analyze = "src.wasm.analyze:"; - wasm_text = "src.wasm.text"; ui = "src.ui:"; ui_text = "src.ui.text:"; + ui_components = "src.ui.components"; -- Just imported to add the functionality COLORS = "conf:COLOR_SCHEME"; } --- A node reacts to no events, just helps with the rendering / event processing ordering -ui.register_component "node" {} - -ui.register_component "root" { - resize = function(self, w, h) - self.rect.w = w - self.rect.h = h - end; -} - -ui.register_component "rect" { - mousepressed_trans = function(self, button, x, y) - return button, x - self.rect.x, y - self.rect.y - end; - - mousereleased_trans = function(self, button, x, y) - return button, x - self.rect.x, y - self.rect.y - end; - - mousemoved_trans = function(self, x, y, dx, dy) - return x - self.rect.x, y - self.rect.y, dx, dy - end; - - mousepressed = function(self, button, x, y) - if self.rect:contains_trans(x, y) then - ui.focus(self) - return true - end - end; - - predraw = function(self) - love.graphics.push() - love.graphics.translate(self.rect.x, self.rect.y) - love.graphics.setColor(self.color) - love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h) - end; - - postdraw = function(self) - love.graphics.pop() - end; -} - -local drag_rect = { extends = "rect" } -function drag_rect.init(self) self.mouse_down = false end - -function drag_rect.mousepressed(self, button, x, y) - if not self.rect:contains_trans(x, y) then return false end - - ui.focus(self) - if button == 1 then self.mouse_down = true end - return true -end - -function drag_rect.mousereleased(self, button, x, y) - if button == 1 then self.mouse_down = false end -end - -function drag_rect.unfocus(self) - self.mouse_down = false -end - -function drag_rect.mousemoved(self, x, y, dx, dy) - if ui.focused == self and self.mouse_down then - self.rect.x = self.rect.x + dx - self.rect.y = self.rect.y + dy - end -end - -local function_block = { extends = "drag_rect" } -function function_block.init(self, wasm_function, wasm_mod) - self.func = wasm_function - self.mod = wasm_mod - - local header_text, body_text = wasm_text.generate_text_format(self.func, self.mod, COLORS) - self.header_text = header_text - self.body_text = body_text - - self.scroll = 0 - self.body_visible = true - self.old_height = 400 - self.resize_down = false - - self.canvas = love.graphics.newCanvas(800, 800) - self.redraw = true -end - -function function_block.toggle_body_visible(self) - self.body_visible = not self.body_visible - self.rect.h = self.body_visible and self.old_height or 22 - self.redraw = true -end - -function function_block.mousepressed(self, button, x, y) - local result = false - if drag_rect.mousepressed(self, button, x, y) then - self.redraw = true - result = true - end - if self.rect:contains_trans(x, y) and button == 2 then - self.resize_down = true - result = true - end - - return result -end - -function function_block.mousereleased(self, button, x, y) - drag_rect.mousereleased(self, button, x, y) - if ui.focused == self and button == 2 then self.resize_down = false end - - if ui.focused == self and button == 3 then - ui.insert_child(self, with(ui.make_element "rect") { - rect = Rectangle(50, 40, 80, 60); - color = { 1, 0, 0 }; - }) - end -end - -function function_block.focus(self) - self.layer = 0 -end - -function function_block.unfocus(self) - self.resize_down = false - self.redraw = true - - for _, chld in ipairs(self.parent.children) do - chld.layer = chld.layer + 1 - end -end - -function function_block.mousemoved(self, x, y, dx, dy) - drag_rect.mousemoved(self, x, y, dx, dy) - - if self.resize_down then - self.rect.w = math.min(800, math.max(100, x)) - if self.body_visible then - self.rect.h = math.min(800, math.max(22, y)) - self.old_height = self.rect.h - end - self.redraw = true - end -end - -function function_block.wheelmoved(self, dx, dy) - if ui.focused ~= self then return false end - - self.scroll = self.scroll - dy * 10 - if self.scroll < 0 then self.scroll = 0 end - self.redraw = true - - return true -end - -function function_block.update(self, dt) - if ui.focused ~= self then return end - - if love.keyboard.isDown "down" then - self.scroll = self.scroll + 300 * dt - self.redraw = true - end - if love.keyboard.isDown "up" then - self.scroll = self.scroll - 300 * dt - if self.scroll < 0 then self.scroll = 0 end - self.redraw = true - end -end - -function function_block.predraw(self) - if self.redraw then - love.graphics.setCanvas(self.canvas) - local oldscissor = { love.graphics.getScissor() } - love.graphics.push() - love.graphics.origin() - love.graphics.setScissor(0, 0, self.canvas:getWidth(), self.canvas:getHeight()) - love.graphics.setBackgroundColor(0, 0, 0, 0) - love.graphics.clear() - love.graphics.setScissor(0, 0, self.rect.w, self.rect.h) - - love.graphics.setColor(0, 0, 0) - love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h) - love.graphics.setColor(ui.focused == self and COLORS.background or COLORS.dark_background) - love.graphics.rectangle("fill", 2, 2, self.rect.w - 4, self.rect.h - 4) - love.graphics.setColor(COLORS.dark_background) - love.graphics.rectangle("fill", 2, 2, self.rect.w - 4, 18) - - local x, y = ui_text.render_text(2, 2, self.header_text) - - if self.body_visible and self.body_text then - love.graphics.intersectScissor(2, y - 4, self.rect.w - 4, self.rect.h - 22) - ui_text.render_text(2, y - self.scroll, self.body_text) - end - - love.graphics.pop() - love.graphics.setScissor(unpack(oldscissor)) - love.graphics.setCanvas() - self.redraw = false - end - - love.graphics.setColor(1, 1, 1) - love.graphics.push() - love.graphics.translate(self.rect.x, self.rect.y) - love.graphics.draw(self.canvas, 0, 0) -end - -function function_block.postdraw(self) - love.graphics.pop() -end - -local scrolling = {} -function scrolling.init(self) - self.offset = { x = 0; y = 0 } - self.line_color = { 0, 0, 0 } - self.zoom = 1 - self.mouse_down = false -end - -function scrolling.mouse_trans(self, x, y) - return - ((x - self.rect.x) - self.rect.w / 2) / self.zoom + self.rect.w / 2 - self.offset.x, - ((y - self.rect.y) - self.rect.h / 2) / self.zoom + self.rect.h / 2 - self.offset.y -end - -function scrolling.mousepressed_trans(self, button, x, y) - local mx, my = scrolling.mouse_trans(self, x, y) - return button, mx, my -end - -function scrolling.mousereleased_trans(self, button, x, y) - local mx, my = scrolling.mouse_trans(self, x, y) - return button, mx, my -end - -function scrolling.mousemoved_trans(self, x, y, dx, dy) - local mx, my = scrolling.mouse_trans(self, x, y) - return mx, my, dx / self.zoom, dy / self.zoom -end - -function scrolling.mousepressed(self, button, x, y) - ui.focus(self) - self.mouse_down = true - return true -end - -function scrolling.mousereleased(self, button, x, y) - self.mouse_down = false -end - -function scrolling.unfocus(self) - self.mouse_down = false -end - -function scrolling.mousemoved(self, x, y, dx, dy) - if self.mouse_down then - self.offset.x = self.offset.x + dx - self.offset.y = self.offset.y + dy - - return true - end - - return false -end - -function scrolling.change_zoom(self, multiplier) - self.zoom = self.zoom * multiplier - if self.zoom <= 0.2 then self.zoom = 0.2 end -end - -function scrolling.wheelmoved(self, dx, dy) - if ui.focused == self then - scrolling.change_zoom(self, dy > 0 and 1.05 or (1 / 1.05)) - return true - end -end - -function scrolling.resize(self, w, h) - self.rect.w = w - self.rect.h = h - self.rect.y -end - -function scrolling.update(self, dt) - if ui.focused ~= self then return end - - local speed = 600 / self.zoom - if love.keyboard.isDown "left" then - self.offset.x = self.offset.x + speed * dt - end - if love.keyboard.isDown "right" then - self.offset.x = self.offset.x - speed * dt - end - if love.keyboard.isDown "up" then - self.offset.y = self.offset.y + speed * dt - end - if love.keyboard.isDown "down" then - self.offset.y = self.offset.y - speed * dt - end - - if love.keyboard.isDown "q" then - scrolling.change_zoom(self, 1 + 0.5 * dt) - end - if love.keyboard.isDown "a" then - scrolling.change_zoom(self, 1 - 0.5 * dt) - end -end - -function scrolling.predraw(self) - love.graphics.push() - love.graphics.translate(self.rect.x, self.rect.y) - love.graphics.setScissor(self.rect.x, self.rect.y, self.rect.w, self.rect.h) - - if self.background_color ~= nil then - love.graphics.setColor(self.background_color) - love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h) - end - love.graphics.translate(self.rect.w / 2, self.rect.h / 2) - love.graphics.scale(self.zoom, self.zoom) - love.graphics.translate(-self.rect.w / 2, -self.rect.h / 2) - - local xoff = self.offset.x - local yoff = self.offset.y - local spacing = 64 - - while xoff >= spacing do xoff = xoff - spacing end - while yoff >= spacing do yoff = yoff - spacing end - while xoff <= -spacing do xoff = xoff + spacing end - while yoff <= -spacing do yoff = yoff + spacing end - - love.graphics.setColor(self.line_color) - for x = -60, 100 do - love.graphics.line(x * spacing + xoff, -self.rect.h * 2, x * spacing + xoff, self.rect.h * 4) - end - for y = -60, 100 do - love.graphics.line(-self.rect.w * 2, y * spacing + yoff, self.rect.w * 4, y * spacing + yoff) - end - - love.graphics.translate(self.offset.x, self.offset.y) -end - -function scrolling.postdraw(self) - love.graphics.pop() - love.graphics.setScissor() -end - -ui.register_component "drag_rect" (drag_rect) -ui.register_component "function_block" (function_block) -ui.register_component "scrolling" (scrolling) - -- Responsible for containing and managing all -- global-ish objects for the application class "Application" { @@ -370,7 +21,7 @@ class "Application" { rect = Rectangle(0, 0, 0, 0); } - self.scroller = with(ui.make_element "scrolling") { + self.scroller = with(ui.make_element "scrolling_area") { rect = Rectangle(0, 0, 1200, 900); background_color = COLORS.background; line_color = COLORS.dark_background; @@ -417,7 +68,7 @@ class "Application" { }) i = i + 1 - if i > 20 then break end + if i > 25 then break end end end end; diff --git a/src/ui.lua b/src/ui.lua index ddcfc04..992de4c 100644 --- a/src/ui.lua +++ b/src/ui.lua @@ -37,6 +37,7 @@ function ui.make_element(type_, ...) rect = Rectangle(0, 0, 0, 0); -- Rectangle bounds of the object children = {}; -- List of children, sorted by layer parent = nil; -- The parent node of this child + sort_dirty = false; -- Whether or not the children might be unsorted -- Any other data can be appended to this object } @@ -74,6 +75,15 @@ function ui.sort_children(elem) table.sort(elem.children, function(a, b) return a.layer < b.layer end) + + elem.sort_dirty = false +end + +function ui.change_layer(elem, layer) + elem.layer = layer + if elem.parent then + elem.parent.sort_dirty = true + end end function ui.fire_stoppable_event(elem, eventname, ...) @@ -92,7 +102,9 @@ function ui.fire_stoppable_event(elem, eventname, ...) table.remove(elem.children, i) end - ui.sort_children(elem) + if elem.sort_dirty then + ui.sort_children(elem) + end return true end @@ -121,9 +133,9 @@ function ui.fire_propagating_event(elem, eventname, ...) end end - -- TODO: Maybe make this a flag? - -- ui.resort = true? - ui.sort_children(elem) + if elem.sort_dirty then + ui.sort_children(elem) + end end local event_func = component_lookup(elem.type, eventname) diff --git a/src/ui/components.lua b/src/ui/components.lua new file mode 100644 index 0000000..7b862ad --- /dev/null +++ b/src/ui/components.lua @@ -0,0 +1,363 @@ +import { + ui = "src.ui:"; + ui_text = "src.ui.text:"; + + Rectangle = "src.utils:Rectangle"; + wasm_text = "src.wasm.text"; + + COLORS = "conf:COLOR_SCHEME"; +} + +-- A node reacts to no events, just helps with the rendering / event processing ordering +ui.register_component "node" {} + +ui.register_component "root" { + resize = function(self, w, h) + self.rect.w = w + self.rect.h = h + end; +} + +local rect = {} +function rect:mousepressed_trans(button, x, y) + return button, x - self.rect.x, y - self.rect.y +end + +function rect:mousereleased_trans(button, x, y) + return button, x - self.rect.x, y - self.rect.y +end + +function rect:mousemoved_trans(x, y, dx, dy) + return x - self.rect.x, y - self.rect.y, dx, dy +end + +function rect:mousepressed(button, x, y) + if self.rect:contains_trans(x, y) then + ui.focus(self) + return true + end +end + +function rect:predraw() + love.graphics.push() + love.graphics.translate(self.rect.x, self.rect.y) + love.graphics.setColor(self.color) + love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h) +end + +function rect:postdraw() + love.graphics.pop() +end + +local drag_rect = { extends = "rect" } +function drag_rect:init() self.mouse_down = false end + +function drag_rect:mousepressed(button, x, y) + if not self.rect:contains_trans(x, y) then return false end + + ui.focus(self) + if button == 1 then self.mouse_down = true end + return true +end + +function drag_rect:mousereleased(button, x, y) + if button == 1 then self.mouse_down = false end +end + +function drag_rect:unfocus() + self.mouse_down = false +end + +function drag_rect:mousemoved(x, y, dx, dy) + if ui.focused == self and self.mouse_down then + self.rect.x = self.rect.x + dx + self.rect.y = self.rect.y + dy + end +end + +local function_block = { extends = "drag_rect" } +function function_block:init(wasm_function, wasm_mod) + self.func = wasm_function + self.mod = wasm_mod + + local header_text, body_text = wasm_text.generate_text_format(self.func, self.mod, COLORS) + self.header_text = header_text + self.body_text = body_text + + self.scroll = 0 + self.body_visible = true + self.old_height = 400 + self.resize_down = false + + self.canvas = love.graphics.newCanvas(800, 800) + self.redraw = true +end + +function function_block:toggle_body_visible() + self.body_visible = not self.body_visible + self.rect.h = self.body_visible and self.old_height or 22 + self.redraw = true +end + +function function_block:mousepressed(button, x, y) + local result = false + if drag_rect.mousepressed(self, button, x, y) then + self.redraw = true + result = true + end + if self.rect:contains_trans(x, y) and button == 2 then + self.resize_down = true + result = true + end + + return result +end + +function function_block:mousereleased(button, x, y) + drag_rect.mousereleased(self, button, x, y) + if ui.focused == self and button == 2 then self.resize_down = false end + + if ui.focused == self and button == 3 then + ui.insert_child(self, with(ui.make_element "rect") { + rect = Rectangle(50, 40, 80, 60); + color = { 1, 0, 0 }; + }) + end +end + +function function_block:focus() + ui.change_layer(self, 0) +end + +function function_block:unfocus() + self.resize_down = false + self.redraw = true + + for _, chld in ipairs(self.parent.children) do + ui.change_layer(chld, chld.layer + 1) + end +end + +function function_block:mousemoved(x, y, dx, dy) + drag_rect.mousemoved(self, x, y, dx, dy) + + if self.resize_down then + self.rect.w = math.min(800, math.max(100, x)) + if self.body_visible then + self.rect.h = math.min(800, math.max(22, y)) + self.old_height = self.rect.h + end + self.redraw = true + end +end + +function function_block:wheelmoved(dx, dy) + if ui.focused ~= self then return false end + + self.scroll = self.scroll - dy * 10 + if self.scroll < 0 then self.scroll = 0 end + self.redraw = true + + return true +end + +function function_block:update(dt) + if ui.focused ~= self then return end + + if love.keyboard.isDown "down" then + self.scroll = self.scroll + 300 * dt + self.redraw = true + end + if love.keyboard.isDown "up" then + self.scroll = self.scroll - 300 * dt + if self.scroll < 0 then self.scroll = 0 end + self.redraw = true + end +end + +function function_block:predraw() + if self.redraw then + love.graphics.setCanvas(self.canvas) + local oldscissor = { love.graphics.getScissor() } + love.graphics.push() + love.graphics.origin() + love.graphics.setScissor(0, 0, self.canvas:getWidth(), self.canvas:getHeight()) + love.graphics.setBackgroundColor(0, 0, 0, 0) + love.graphics.clear() + love.graphics.setScissor(0, 0, self.rect.w, self.rect.h) + + love.graphics.setColor(0, 0, 0) + love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h) + love.graphics.setColor(ui.focused == self and COLORS.background or COLORS.dark_background) + love.graphics.rectangle("fill", 2, 2, self.rect.w - 4, self.rect.h - 4) + love.graphics.setColor(COLORS.dark_background) + love.graphics.rectangle("fill", 2, 2, self.rect.w - 4, 18) + + local x, y = ui_text.render_text(2, 2, self.header_text) + + if self.body_visible and self.body_text then + love.graphics.intersectScissor(2, y - 4, self.rect.w - 4, self.rect.h - 22) + ui_text.render_text(2, y - self.scroll, self.body_text) + end + + love.graphics.pop() + love.graphics.setScissor(unpack(oldscissor)) + love.graphics.setCanvas() + self.redraw = false + end + + love.graphics.setColor(1, 1, 1) + love.graphics.push() + love.graphics.translate(self.rect.x, self.rect.y) + love.graphics.draw(self.canvas, 0, 0) +end + +function function_block:postdraw() + love.graphics.pop() +end + +local scrolling_area = {} +function scrolling_area:init() + self.offset = { x = 0; y = 0 } + self.line_color = { 0, 0, 0 } + self.zoom = 1 + self.mouse_down = false +end + +function scrolling_area:mouse_trans(x, y) + return + ((x - self.rect.x) - self.rect.w / 2) / self.zoom + self.rect.w / 2 - self.offset.x, + ((y - self.rect.y) - self.rect.h / 2) / self.zoom + self.rect.h / 2 - self.offset.y +end + +function scrolling_area:mousepressed_trans(button, x, y) + local mx, my = scrolling_area.mouse_trans(self, x, y) + return button, mx, my +end + +function scrolling_area:mousereleased_trans(button, x, y) + local mx, my = scrolling_area.mouse_trans(self, x, y) + return button, mx, my +end + +function scrolling_area:mousemoved_trans(x, y, dx, dy) + local mx, my = scrolling_area.mouse_trans(self, x, y) + return mx, my, dx / self.zoom, dy / self.zoom +end + +function scrolling_area:mousepressed(button, x, y) + ui.focus(self) + self.mouse_down = true + return true +end + +function scrolling_area:mousereleased(button, x, y) + self.mouse_down = false +end + +function scrolling_area:unfocus() + self.mouse_down = false +end + +function scrolling_area:mousemoved(x, y, dx, dy) + if self.mouse_down then + self.offset.x = self.offset.x + dx + self.offset.y = self.offset.y + dy + + return true + end + + return false +end + +function scrolling_area:change_zoom(multiplier) + self.zoom = self.zoom * multiplier + if self.zoom <= 0.2 then self.zoom = 0.2 end +end + +function scrolling_area:wheelmoved(dx, dy) + if ui.focused == self then + scrolling_area.change_zoom(self, dy > 0 and 1.05 or (1 / 1.05)) + return true + end +end + +function scrolling_area:resize(w, h) + self.rect.w = w + self.rect.h = h - self.rect.y +end + +function scrolling_area:update(dt) + if ui.focused ~= self then return end + + local speed = 600 / self.zoom + if love.keyboard.isDown "left" then + self.offset.x = self.offset.x + speed * dt + end + if love.keyboard.isDown "right" then + self.offset.x = self.offset.x - speed * dt + end + if love.keyboard.isDown "up" then + self.offset.y = self.offset.y + speed * dt + end + if love.keyboard.isDown "down" then + self.offset.y = self.offset.y - speed * dt + end + + if love.keyboard.isDown "q" then + scrolling_area.change_zoom(self, 1 + 0.5 * dt) + end + if love.keyboard.isDown "a" then + scrolling_area.change_zoom(self, 1 - 0.5 * dt) + end +end + +function scrolling_area:predraw() + love.graphics.push() + love.graphics.translate(self.rect.x, self.rect.y) + love.graphics.setScissor(self.rect.x, self.rect.y, self.rect.w, self.rect.h) + + if self.background_color ~= nil then + love.graphics.setColor(self.background_color) + love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h) + end + love.graphics.translate(self.rect.w / 2, self.rect.h / 2) + love.graphics.scale(self.zoom, self.zoom) + love.graphics.translate(-self.rect.w / 2, -self.rect.h / 2) + + local xoff = self.offset.x + local yoff = self.offset.y + local spacing = 64 + + while xoff >= spacing do xoff = xoff - spacing end + while yoff >= spacing do yoff = yoff - spacing end + while xoff <= -spacing do xoff = xoff + spacing end + while yoff <= -spacing do yoff = yoff + spacing end + + love.graphics.setColor(self.line_color) + for x = -60, 100 do + love.graphics.line(x * spacing + xoff, -self.rect.h * 2, x * spacing + xoff, self.rect.h * 4) + end + for y = -60, 100 do + love.graphics.line(-self.rect.w * 2, y * spacing + yoff, self.rect.w * 4, y * spacing + yoff) + end + + love.graphics.translate(self.offset.x, self.offset.y) +end + +function scrolling_area:postdraw() + love.graphics.pop() + love.graphics.setScissor() +end + +ui.register_component "rect" (rect) +ui.register_component "drag_rect" (drag_rect) +ui.register_component "function_block" (function_block) +ui.register_component "scrolling_area" (scrolling_area) + +return module { + rect = rect; + drag_rect = drag_rect; + function_block = function_block; + scrolling_area = scrolling_area; +} -- 2.25.1