Continuing to work on this
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 7 Apr 2020 00:24:26 +0000 (19:24 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 7 Apr 2020 00:24:26 +0000 (19:24 -0500)
14 files changed:
Makefile
app.lua [new file with mode: 0644]
conf.lua
docs/todo
main.lua
res/FiraCode-Regular.ttf [deleted file]
res/fonts/FiraCode-Regular.ttf [new file with mode: 0644]
src/old-ui/components.lua [new file with mode: 0644]
src/old-ui/regions.lua [new file with mode: 0644]
src/old-ui/text.lua [new file with mode: 0644]
src/old-ui/ui.lua [new file with mode: 0644]
src/ui.lua [new file with mode: 0644]
src/ui/ui.lua [deleted file]
src/utils.lua

index 42188841964f8c87c314a23494e2c09244e1b642..31dea2a7849e30c7c202b86cfd7adc41c8165382 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 clib/%.o: clib/%.c clib/*.h
-       gcc -DLUA51_TARGET -O2 -o $@ -fPIC -c $< -llua
+       gcc -DLUA51_TARGET  -O2 -o $@ -fPIC -c $< -llua
 
 clib/%.so: clib/%.o
        gcc -shared -o $@ $< -llua
diff --git a/app.lua b/app.lua
new file mode 100644 (file)
index 0000000..494be00
--- /dev/null
+++ b/app.lua
@@ -0,0 +1,281 @@
+import {
+       Rectangle = "src.utils:Rectangle";
+       draw_arrow = "src.utils:draw_arrow";
+
+       wasm_decompile = "src.wasm.decompile:";
+       wasm_analyze = "src.wasm.analyze:";
+
+       COLORS = "conf:COLOR_SCHEME";
+}
+
+class "FunctionBlock" [DraggableRect] {
+       init = function(self, func, x, y)
+               DraggableRect.init(self, x, y, 400, 400)
+
+               self.func = func
+               funcBlocks[func.funcidx] = self
+
+               WasmCodeGenerator:setup(func, mod, COLORS)
+               self.header_text = WasmCodeGenerator:build_header()
+
+               if func.body then
+                       self.body_text = WasmCodeGenerator:build_body()
+               else
+                       self.body_text = {
+                               { text = "imported function", color = COLORS.code }
+                       }
+               end
+
+               self.scroll = 0
+               self.body_visible = true
+               self.old_height = 400
+
+               self.canvas = love.graphics.newCanvas(800, 800)
+               self.redraw = true
+       end;
+
+       toggle_body_visible = function(self)
+               self.body_visible = not self.body_visible
+               self.rect.h = self.body_visible and self.old_height or 22
+               self.redraw = true
+       end;
+
+       onhover = function(self, x, y)
+               self.redraw = not self.selected or self.redraw
+               self.selected = true
+       end;
+
+       onunhover = function(self)
+               self.selected = false
+               self.redraw = true
+       end;
+
+       onclick = function(self, button, x, y, ox, oy)
+               DraggableRect.onclick(self, button, x, y, ox, oy)
+
+               if button == 2 then
+                       local cmr
+                       cmr = ContextMenu(self.ui, ox, oy, {
+                               { text = "Toggle open", action = function()
+                                       cmr.should_delete = true
+                                       self:toggle_body_visible()
+                               end
+                               };
+                               { text = "Resize", action = function() cmr.should_delete = true end };
+                               { text = "Hide", action = function() end; };
+                       })
+                       self.ui:add_region(cmr)
+               end
+               self.redraw = true
+       end;
+
+       onwheel = function(self, _, dy)
+               self.scroll = self.scroll - dy * 10;
+               if self.scroll < 0 then self.scroll = 0 end
+               self.redraw = true
+       end;
+
+       ondrag = function(self, button, x, y, dx, dy)
+               DraggableRect.ondrag(self, button, x, y, dx, dy)
+
+               if y >= 24 and button == 3 then
+                       self.rect.w = math.min(800, math.max(100, self.rect.w + dx))
+                       if self.body_visible then
+                               self.rect.h = math.min(800, math.max(22, self.rect.h + dy))
+                               self.old_height = self.rect.h
+                       end
+                       self.redraw = true
+               end
+       end;
+
+       draw = function(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(self.selected 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 = Text.render_text(2, 2, self.header_text)
+
+                       if self.body_visible and self.body_text then
+                               love.graphics.intersectScissor(2, y - 1, self.rect.w - 4, self.rect.h - 22)
+                               Text.render_text(2, y + 4 - 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.draw(self.canvas, self.rect.x, self.rect.y)
+
+               if self.selected then
+                       if self.func.callees then
+                               love.graphics.setColor(COLORS.primary_light)
+                               for _, callee in ipairs(self.func.callees) do
+                                       local sx, sy = self.rect:center()
+                                       local tx, ty = funcBlocks[callee].rect:center()
+                                       draw_arrow(sx, sy, tx, ty)
+                               end
+                       end
+                       if self.func.callers then
+                               love.graphics.setColor(COLORS.secondary)
+                               for _, caller in ipairs(self.func.callers) do
+                                       local sx, sy = self.rect:center()
+                                       local tx, ty = funcBlocks[caller].rect:center()
+                                       draw_arrow(tx, ty, sx, sy)
+                               end
+                       end
+               end
+       end;
+}
+
+class "ContextMenu" [Region] {
+       init = function(self, ui, cx, cy, options)
+               Region.init(self, ui)
+
+               self.rect.w = 200
+               self.rect.h = 25 * #options
+               self.rect.x = cx - self.rect.w / 2
+               self.rect.y = cy - self.rect.h / 2
+
+               self.background = COLORS.secondary_dark
+
+               for i, opt in ipairs(options) do
+                       self:add_object(with(UiButton()) {
+                               text = opt.text;
+                               background = COLORS.secondary_dark;
+                               hover_background = COLORS.secondary;
+                               click_background = COLORS.secondary_light;
+                               foreground = COLORS.secondary_text;
+                               rect = Rectangle(0, (i - 1) * 25, 200, 25);
+                               click = opt.action
+                       })
+               end
+
+               self.layer = 100
+       end;
+
+       onmouseleave = function(self)
+               self.should_delete = true
+       end;
+}
+
+function make_top_bar(app)
+       local top_bar = Region()
+       with(top_bar) {
+               -- Maybe don't hard code this?
+               rect = Rectangle(0, 0, 1200, 50);
+
+               background_color = COLORS.dark_background;
+               layer = 10;
+
+               onresize = function(region, new_width, _)
+                       -- Fill the width of the screen
+                       region.rect.w = new_width
+               end
+       }
+
+       local test = with(Button()) {
+               text = "Test Button";
+               background = COLORS.primary_dark;
+               hovered_background = COLORS.primary;
+               click_background = COLORS.primary_light;
+               foreground = COLORS.primary_text;
+
+               rect = Rectangle(10, 10, 80, 30);
+       }
+
+       top_bar:add_component(test)
+       return top_bar
+end
+
+-- Responsible for containing and managing all
+-- global-ish objects for the application
+class "Application" {
+       init = function(self)
+               self.ui = Ui()
+               self.wasm = nil
+       end;
+
+       create = function(self)
+               self.scrolling_region = ScrollingRegion(self.ui)
+               with(self.scrolling_region) {
+                       rect = Rectangle(0, 0, 1200, 900);
+                       background_color = COLORS.background;
+                       line_color = COLORS.dark_background;
+                       layer = 0;
+
+                       onresize = function(region, new_width, new_height)
+                               region.rect.w = new_width
+                               region.rect.h = new_height
+                       end;
+               }
+               self.ui:add_region(self.scrolling_region)
+
+               self.ui:add_region(make_top_bar(self))
+       end;
+
+       bootstrap = function(self)
+               local ui = self.ui
+
+               -- Sets all Love callbacks
+               function love.filedropped(file) self:open_file(file:getFilename()) end
+               function love.mousepressed(x, y, button) ui:onmousedown(button, x, y) end
+               function love.mousereleased(x, y, button) ui:onmouseup(button, x, y) end
+               function love.mousemoved(x, y, dx, dy) ui:onmousemove(x, y, dx, dy) end
+               function love.wheelmoved(dx, dy) ui:onmousewheel(dx, dy) end
+               function love.keypressed(key) ui:onkeydown(key) end
+               function love.keyreleased(key) ui:onkeyup(key) end
+               function love.resize(w, h) ui:onresize(w, h) end
+               function love.update(dt) self:update(dt) end
+               function love.draw() self:draw() end
+       end;
+
+       open_file = function(self, path)
+               local wasm_mod = wasm_decompile(path)
+               wasm_mod = wasm_analyze(wasm_mod)
+               self.wasm = wasm_mod
+
+               -- TODO DO SOMETHING HERE
+       end;
+
+       update = function(self, dt)
+               if love.keyboard.isDown "escape" then
+                       love.event.quit()
+               end
+
+               self.ui:update(dt)
+       end;
+
+       draw = function(self)
+               love.graphics.setBackgroundColor(0, 0, 0)
+               love.graphics.clear()
+
+               self.ui:draw()
+
+               love.graphics.setScissor()
+       end;
+}
+
+-- Delay the execution of the init function
+-- until everything has been imported and
+-- resolved
+app = Application()
+
+return module {
+       app
+}
index 8b1368951cab9b60092aba606ac43c48fcb27b25..752af6d28097aa6f3ef0da73c0856e55295982bf 100644 (file)
--- a/conf.lua
+++ b/conf.lua
@@ -50,5 +50,5 @@ end
 return {
        COLOR_SCHEMES = COLOR_SCHEMES;
 
-       COLOR_SCHEME = COLOR_SCHEMES.DARK;
+       COLOR_SCHEME = COLOR_SCHEMES.LIGHT;
 }
index a679a74d3f1c63fd06762f07b10284b41d8f087e..5ca193e74b8b0c3954d4a6504008e5a286b87b18 100644 (file)
--- a/docs/todo
+++ b/docs/todo
@@ -7,3 +7,24 @@
 [X] Implement auto resizing
 [ ] Focus a function
        * Big window with better details
+
+GOALS FOR TODAY:
+[ ] Get the app back to the state that it was in, but with slightly better code
+[ ] Decide the features I want to implement
+[ ] Look into how text input works so textboxes can be a thing
+
+FIRST THING:
+[ ] Refactor to not use classes in the ui
+       * UI object will of the form:
+
+type UIObject a = {
+       type :: string;
+       layer :: string number;
+       data :: a;
+
+       children :: [UiObject b]; // This is sorted by the layer of the object
+}
+
+       * Traversal will be pre-order for everything
+       * Drawing will be split into two parts, pre and post, for whether on the "left" or "right" of the node
+       * Events will be depth first, and return true if they consumed the action, and false if the action should propagate
index c7311bed03ae35647d3eabd2bcda5c170e609976..390bd39afd2f3b6dd110bf338d45ef7d11ce2d48 100644 (file)
--- a/main.lua
+++ b/main.lua
 require "lualib.oop"
 
 import {
-       pprint = "lualib.pprint";
-
-       decompile = "src.wasm.decompile:";
-       analyze = "src.wasm.analyze:";
-       WasmCodeGenerator = "src.wasm.text:";
-
-       Ui = "src.ui.ui:Ui";
-       UiRegion = "src.ui.ui:UiRegion";
-       UiButton = "src.ui.ui:UiButton";
-       ScrollingUiRegion = "src.ui.ui:ScrollingUiRegion";
-       DraggableRect = "src.ui.ui:DraggableRect";
-
-       render_text = "src.ui.ui:render_text";
-
-       scissor_points = "src.utils:scissor_points";
-       Rectangle = "src.utils:Rectangle";
-
-       COLORS = "conf:COLOR_SCHEME";
-}
-
-local funcBlocks = {}
-
-class "FunctionBlock" [DraggableRect] {
-       init = function(self, func, x, y)
-               DraggableRect.init(self, x, y, 400, 400)
-
-               self.func = func
-               funcBlocks[func.funcidx] = self
-
-               WasmCodeGenerator:setup(func, mod, COLORS)
-               self.header_text = WasmCodeGenerator:build_header()
-
-               if func.body then
-                       self.body_text = WasmCodeGenerator:build_body()
-               else
-                       self.body_text = {
-                               { text = "imported function", color = COLORS.code }
-                       }
-               end
-
-               self.scroll = 0
-               self.body_visible = true
-               self.old_height = 400
-
-               self.canvas = love.graphics.newCanvas(800, 800)
-               self.redraw = true
-       end;
-
-       toggle_body_visible = function(self)
-               self.body_visible = not self.body_visible
-               self.rect.h = self.body_visible and self.old_height or 22
-               self.redraw = true
-       end;
-
-       onhover = function(self, x, y)
-               self.redraw = not self.selected or self.redraw
-               self.selected = true
-       end;
-
-       onunhover = function(self)
-               self.selected = false
-               self.redraw = true
-       end;
-
-       onclick = function(self, button, x, y, ox, oy)
-               DraggableRect.onclick(self, button, x, y, ox, oy)
-
-               if button == 2 then
-                       local cmr
-                       cmr = FunctionBlockContextMenu(self.ui, ox, oy, {
-                               { text = "Toggle open", action = function()
-                                       cmr.should_delete = true
-                                       self:toggle_body_visible()
-                               end
-                               };
-                               { text = "Resize", action = function() cmr.should_delete = true end };
-                               { text = "Hide", action = function() end; };
-                       })
-                       self.ui:add_region(cmr)
-               end
-               self.redraw = true
-       end;
-
-       onwheel = function(self, _, dy)
-               self.scroll = self.scroll - dy * 10;
-               if self.scroll < 0 then self.scroll = 0 end
-               self.redraw = true
-       end;
-
-       ondrag = function(self, button, x, y, dx, dy)
-               DraggableRect.ondrag(self, button, x, y, dx, dy)
-
-               if y >= 24 and button == 3 then
-                       self.rect.w = math.min(800, math.max(100, self.rect.w + dx))
-                       if self.body_visible then
-                               self.rect.h = math.min(800, math.max(22, self.rect.h + dy))
-                               self.old_height = self.rect.h
-                       end
-                       self.redraw = true
-               end
-       end;
-
-       draw = function(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(self.selected 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 = render_text(2, 2, self.header_text)
-
-                       if self.body_visible and self.body_text then
-                               love.graphics.intersectScissor(2, y - 1, self.rect.w - 4, self.rect.h - 22)
-                               render_text(2, y + 4 - 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.draw(self.canvas, self.rect.x, self.rect.y)
-
-               if self.func.callees then
-                       love.graphics.setColor(COLORS.primary_light)
-                       for _, callee in ipairs(self.func.callees) do
-                               local sx, sy = self.rect:center()
-                               local tx, ty = funcBlocks[callee].rect:center()
-                               love.graphics.line(sx, sy, tx, ty)
-                       end
-               end
-               if self.func.callers then
-                       love.graphics.setColor(COLORS.secondary_dark)
-                       for _, caller in ipairs(self.func.callers) do
-                               local sx, sy = self.rect:center()
-                               local tx, ty = funcBlocks[caller].rect:center()
-                               love.graphics.line(sx, sy, tx, ty)
-                       end
-               end
-       end;
-}
-
-class "FunctionBlockContextMenu" [UiRegion] {
-       init = function(self, ui, cx, cy, options)
-               UiRegion.init(self, ui)
-
-               self.rect.w = 200
-               self.rect.h = 25 * #options
-               self.rect.x = cx - self.rect.w / 2
-               self.rect.y = cy - self.rect.h / 2
-
-               self.background = COLORS.secondary_dark
-
-               for i, opt in ipairs(options) do
-                       self:add_object(with(UiButton()) {
-                               text = opt.text;
-                               background = COLORS.secondary_dark;
-                               hover_background = COLORS.secondary;
-                               click_background = COLORS.secondary_light;
-                               foreground = COLORS.secondary_text;
-                               rect = Rectangle(0, (i - 1) * 25, 200, 25);
-                               click = opt.action
-                       })
-               end
-
-               self.layer = 100
-       end;
-
-       onmouseleave = function(self)
-               self.should_delete = true
-       end;
+       app = "app:"
 }
 
-mod = nil
-ui = nil
 function love.load()
-       ui = Ui()
-
-       mod = decompile(arg[2])
-       analyze(mod)
-
-       ui_region = ScrollingUiRegion(ui)
-       with(ui_region) {
-               rect = Rectangle(0, 0, 1200, 900);
-               background_color = COLORS.background;
-               line_color = COLORS.dark_background;
-               layer = 0;
-
-               onresize = function(region, new_width, new_height)
-                       region.rect.w = new_width
-                       region.rect.h = new_height
-               end;
-       }
-
-       local x = 0
-       local y = 0
-       for _, func in pairs(mod.funcs) do
-               local block = FunctionBlock(func, math.random() * 10000, math.random() * 10000)
-               block.order = #mod.funcs * 20 + 1 - y
-               ui_region:add_object(block)
-               y = y + 20
-       end
-
-       ui:add_region(ui_region)
-
-       ui:add_region(with(UiRegion()) {
-               rect = Rectangle(0, 0, 1200, 50);
-               background_color = COLORS.dark_background;
-               layer = 10;
-
-               onresize = function(region, new_width, _)
-                       region.rect.w = new_width
-               end;
-       })
-end
+       app:create()
+       app:bootstrap()
 
-function love.update(dt)
-       if love.keyboard.isDown "escape" then
-               love.event.quit()
+       if arg[2] then
+               app:open_file(arg[2])
        end
 
-       ui:update(dt)
-end
-
-function love.mousepressed(x, y, button)
-       ui:onmousedown(button, x, y)
-end
-
-function love.mousereleased(x, y, button)
-       ui:onmouseup(button, x, y)
-end
-
-function love.mousemoved(x, y, dx, dy)
-       ui:onmousemove(x, y, dx, dy)
-end
-
-function love.wheelmoved(dx, dy)
-       ui:onmousewheel(dx, dy)
-end
-
-function love.keypressed(key)
-       ui:onkeydown(key)
-end
-
-function love.keyreleased(key)
-       ui:onkeyup(key)
-end
-
-function love.resize(w, h)
-       ui:onresize(w, h)
-end
-
-function love.draw()
-       love.graphics.setBackgroundColor(0, 0, 0)
-       love.graphics.clear()
-
-       ui:draw()
-
-       love.graphics.setScissor()
+       -- ui = Ui()
+
+       -- mod = decompile(arg[2])
+       -- analyze(mod)
+
+       -- ui_region = ScrollingUiRegion(ui)
+       -- with(ui_region) {
+       --      rect = Rectangle(0, 0, 1200, 900);
+       --      background_color = COLORS.background;
+       --      line_color = COLORS.dark_background;
+       --      layer = 0;
+
+       --      onresize = function(region, new_width, new_height)
+       --              region.rect.w = new_width
+       --              region.rect.h = new_height
+       --      end;
+       -- }
+
+       -- local x = 0
+       -- local y = 0
+       -- for _, func in pairs(mod.funcs) do
+       --      local block = FunctionBlock(func, math.random() * 20000, math.random() * 20000)
+       --      block.order = #mod.funcs * 20 + 1 - y
+       --      ui_region:add_object(block)
+       --      y = y + 20
+       -- end
+
+       -- ui:add_region(ui_region)
+
+       -- ui:add_region(with(UiRegion()) {
+       --      rect = Rectangle(0, 0, 1200, 50);
+       --      background_color = COLORS.dark_background;
+       --      layer = 10;
+
+       --      onresize = function(region, new_width, _)
+       --              region.rect.w = new_width
+       --      end;
+       -- })
 end
diff --git a/res/FiraCode-Regular.ttf b/res/FiraCode-Regular.ttf
deleted file mode 100644 (file)
index 97c1159..0000000
Binary files a/res/FiraCode-Regular.ttf and /dev/null differ
diff --git a/res/fonts/FiraCode-Regular.ttf b/res/fonts/FiraCode-Regular.ttf
new file mode 100644 (file)
index 0000000..97c1159
Binary files /dev/null and b/res/fonts/FiraCode-Regular.ttf differ
diff --git a/src/old-ui/components.lua b/src/old-ui/components.lua
new file mode 100644 (file)
index 0000000..2890572
--- /dev/null
@@ -0,0 +1,99 @@
+import {
+       Rectangle = "src.utils:Rectangle";
+       uuid = "src.utils:uuidv4";
+}
+
+class "Component" {
+       init = function(self)
+               self.uuid = uuid()
+
+               self.rect = Rectangle(0, 0, 0, 0)
+               self.selected = false
+               self.order = math.random(1, 1024)
+               self.ui = {}
+       end;
+
+       onclick = function(self, button, x, y, ox, oy) end;
+       ondrag = function(self, button, x, y, dx, dy) end;
+       onhover = function(self, x, y) end;
+       onunhover = function(self) end;
+       onwheel = function(self, dx, dy) end;
+       onmousedown = function(self, button, x, y, ox, oy)
+               self.selected = true
+       end;
+
+       onselect = function(self)
+               self.selected = true
+       end;
+       onunselect = function(self)
+               self.selected = false
+       end;
+
+       onkey = function(self, button) end;
+
+       update = function(self, dt) end;
+       draw = function(self) end;
+}
+
+class "Button" [Component] {
+       init = function(self)
+               Component.init(self)
+
+               self.text = "Placeholder"
+               self.hovered = false
+               self.background = { 0, 0, 0 }
+               self.hover_background = { 0.2, 0.2, 0.2 }
+               self.click_background = { 0.4, 0.4, 0.4 }
+               self.foreground = { 1, 1, 1 }
+       end;
+
+       onclick = function(self, button, x, y, ox, oy)
+               if button == 1 and self.click then
+                       self.click(self, x, y)
+               end
+               self.selected = false
+       end;
+
+       onhover = function(self, x, y)
+               self.hovered = true
+       end;
+
+       onunhover = function(self)
+               self.hovered = false
+       end;
+
+       draw = function(self)
+               if self.selected then
+                       love.graphics.setColor(self.click_background)
+               elseif self.hovered then
+                       love.graphics.setColor(self.hover_background)
+               else
+                       love.graphics.setColor(self.background)
+               end
+               love.graphics.rectangle("fill", self.rect.x, self.rect.y, self.rect.w, self.rect.h)
+
+               love.graphics.setColor(self.foreground)
+               love.graphics.printf(self.text, self.rect.x + 2, self.rect.y + 2, self.rect.w, "center")
+       end;
+}
+
+class "DraggableRect" [Component] {
+       init = function(self, x, y, w, h)
+               Component.init(self)
+
+               self.rect = Rectangle(x, y, w, h)
+       end;
+
+       ondrag = function(self, button, x, y, dx, dy)
+               if button == 1 then
+                       self.rect.x = self.rect.x + dx
+                       self.rect.y = self.rect.y + dy
+               end
+       end;
+}
+
+return module {
+       Component = Component;
+       Button = Button;
+       DraggableRect = DraggableRect;
+}
diff --git a/src/old-ui/regions.lua b/src/old-ui/regions.lua
new file mode 100644 (file)
index 0000000..8e79c32
--- /dev/null
@@ -0,0 +1,287 @@
+import {
+       revipairs = "src.utils:revipairs";
+       Rectangle = "src.utils:Rectangle";
+       scissor_points = "src.utils:scissor_points";
+}
+
+class "Region" {
+       init = function(self, ui)
+               self.active = false
+               self.ui = ui
+
+               self.selected_comp = nil
+               self.comps = {}
+
+               self.rect = Rectangle(0, 0, 0, 0)
+               self.background_color = { 0, 0, 0 }
+               self.layer = 0
+
+               -- Internals
+               self.is_mouse_down = false
+               self.fire_onclick = false
+               self.should_delete = false
+               self.hovered_comp = nil
+       end;
+
+       add_component = function(self, obj)
+               obj.ui = self.ui
+               table.insert(self.comps, obj)
+       end;
+
+       -- Used for sorting in the Ui
+       __lt = function(self, other)
+               return self.layer < other.layer
+       end;
+
+       _obj_rect = function(self, obj)
+               return obj.rect
+       end;
+
+       set_selected_comp = function(self, new_selected)
+               if self.selected_comp ~= new_selected then
+                       if self.selected_comp ~= nil then
+                               self.selected_comp:onunselect()
+                       end
+
+                       self.selected_comp = new_selected
+
+                       if self.selected_comp ~= nil then
+                               self.selected_comp.order = 0
+
+                               for _, obj in ipairs(self.comps) do
+                                       if obj ~= self.selected_comp then
+                                               obj.order = obj.order + 1
+                                       end
+                               end
+
+                               table.sort(self.comps, function(a, b)
+                                       return a.order > b.order
+                               end)
+
+                               self.selected_comp:onselect()
+                       end
+               end
+       end;
+
+       onmousedown = function(self, button, x, y, ox, oy)
+               self.is_mouse_down = button
+               self.fire_onclick = true
+               local new_selected = nil
+
+               for _, obj in revipairs(self.comps) do
+                       if self:_obj_rect(obj):contains(x, y) then
+                               new_selected = obj
+                               break
+                       end
+               end
+
+               self:set_selected_comp(new_selected)
+
+               if self.selected_comp then
+                       self.selected_comp:onmousedown(button, x, y, ox, oy)
+               end
+       end;
+
+       onmouseup = function(self, button, x, y, ox, oy)
+               self.is_mouse_down = false
+
+               if self.selected_comp ~= nil and self.fire_onclick then
+                       local obj_rect = self:_obj_rect(self.selected_comp)
+                       self.selected_comp:onclick(button, x - obj_rect.x, y - obj_rect.y, ox, oy)
+               end
+       end;
+
+       onmousemove = function(self, x, y, dx, dy)
+               local old_hovered = self.hovered_comp
+
+               self.hovered_comp = nil
+               for _, obj in revipairs(self.comps) do
+                       if self:_obj_rect(obj):contains(x, y) then
+                               obj:onhover(x, y)
+                               self.hovered_comp = obj
+                               break
+                       end
+               end
+
+               if old_hovered and self.hovered_comp ~= old_hovered then
+                       old_hovered:onunhover()
+               end
+
+               self:set_selected_comp(self.hovered_comp)
+
+               if self.is_mouse_down and self.selected_comp ~= nil then
+                       local obj_rect = self:_obj_rect(self.selected_comp)
+                       self.selected_comp:ondrag(self.is_mouse_down, x - obj_rect.x, y - obj_rect.y, dx, dy)
+                       self.fire_onclick = self.fire_onclick and (math.abs(dx) + math.abs(dy)) < 2
+               end
+       end;
+
+       onmouseenter = function(self)
+               self.active = true
+       end;
+
+       onmouseleave = function(self)
+               self.active = false;
+               self.is_mouse_down = false
+               self.fire_onclick = false
+
+               self:set_selected_comp(nil)
+       end;
+
+       onmousewheel = function(self, dx, dy)
+               if self.selected_comp ~= nil then
+                       self.selected_comp:onwheel(dx, dy)
+               end
+       end;
+
+       onresize = function(self, new_width, new_height) end;
+
+       onkeydown = function(self, key) end;
+       onkeyup = function(self, key) end;
+
+       update = function(self, dt)
+               for _, obj in ipairs(self.comps) do
+                       obj:update(dt)
+               end
+       end;
+
+       draw = function(self)
+               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
+
+               for _, comp in ipairs(self.comps) do
+                       love.graphics.push()
+                       -- love.graphics.translate(obj.rect.x, obj.rect.y)
+                       comp:draw()
+                       love.graphics.pop()
+               end
+       end;
+}
+
+class "ScrollingRegion" [Region] {
+       init = function(self, ui)
+               Region.init(self, ui)
+
+               self.offset = { x = 0; y = 0; }
+               self.line_color = { 0, 0, 0 }
+
+               self.zoom = 1
+       end;
+
+       _obj_rect = function(self, obj)
+               return obj.rect
+       end;
+
+       _transform_mouse = function(self, x, y)
+               return
+                       (x - self.rect.w / 2) / self.zoom + self.rect.w / 2 - self.offset.x,
+                       (y - self.rect.h / 2) / self.zoom + self.rect.h / 2 - self.offset.y
+       end;
+
+       onmousedown = function(self, button, x, y, ox, oy)
+               local mx, my = self:_transform_mouse(x, y)
+               Region.onmousedown(self, button, mx, my, ox, oy)
+       end;
+
+       onmouseup = function(self, button, x, y, ox, oy)
+               local mx, my = self:_transform_mouse(x, y)
+               Region.onmouseup(self, button, mx, my, ox, oy)
+       end;
+
+       onmousemove = function(self, x, y, dx, dy)
+               local mx, my = self:_transform_mouse(x, y)
+               Region.onmousemove(self, mx, my, dx / self.zoom, dy / self.zoom)
+
+               if self.is_mouse_down and self.selected_comp == nil then
+                       self.offset.x = self.offset.x + dx / self.zoom
+                       self.offset.y = self.offset.y + dy / self.zoom
+               end
+       end;
+
+       onmousewheel = function(self, dx, dy)
+               Region.onmousewheel(self, dx, dy)
+
+               if self.selected_comp == nil then
+                       self:change_zoom(dy > 0 and 1.05 or (1 / 1.05))
+               end
+       end;
+
+       change_zoom = function(self, multiplier)
+               self.zoom = self.zoom * multiplier
+               -- if self.zoom >= 1 then self.zoom = 1 end
+               if self.zoom <= 0.2 then self.zoom = 0.2 end
+       end;
+
+       update = function(self, dt)
+               Region.update(self, dt)
+
+               if self.active then
+                       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
+                               self:change_zoom(1 + 0.5 * dt)
+                       end
+                       if love.keyboard.isDown "a" then
+                               self:change_zoom(1 - 0.5 * dt)
+                       end
+               end
+       end;
+
+       draw = function(self)
+               love.graphics.push()
+
+               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
+               local tmpbgcolor = self.background_color
+               self.background_color = nil
+
+               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)
+               Region.draw(self)
+               love.graphics.pop()
+
+               self.background_color = tmpbgcolor
+       end;
+}
+
+return module {
+       Region = Region;
+       ScrollingRegion = ScrollingRegion;
+}
diff --git a/src/old-ui/text.lua b/src/old-ui/text.lua
new file mode 100644 (file)
index 0000000..8eee352
--- /dev/null
@@ -0,0 +1,70 @@
+import { }
+
+FONTS = {}
+
+Text = {}
+function Text.render_text(x, y, text)
+       local curr_font = love.graphics.getFont()
+
+       local origx = x
+       local fheight = curr_font:getHeight()
+       local indent_level = 0
+       local indentation = ""
+       local fresh_newline = true
+
+       for _, txt in ipairs(text) do
+               if txt.text then
+                       local printed = txt.text
+                       if fresh_newline then
+                               if txt.post_indent then
+                                       printed = txt.text .. indentation
+                               else
+                                       printed = indentation .. txt.text
+                               end
+                               fresh_newline = false
+                       end
+
+                       if txt.background then
+                               love.graphics.setColor(txt.background)
+                               love.graphics.rectangle("fill", x, y, curr_font:getWidth(txt.text), fheight + 2);
+                       end
+
+                       if txt.color then
+                               love.graphics.setColor(txt.color)
+                       end
+                       love.graphics.print(printed, x, y)
+
+                       x = x + curr_font:getWidth(printed)
+               end
+
+               if txt.newline then
+                       y = y + fheight + 2;
+                       x = origx
+                       fresh_newline = true
+               end
+
+               if txt.change_indent then
+                       indent_level = indent_level + txt.change_indent
+                       indentation = ""
+                       for i=1,indent_level do
+                               indentation = indentation .. "  "
+                       end
+               end
+       end
+
+       return x, y
+end
+
+function Text.register_font(name, size)
+       local fonts = FONTS[name] or {}
+       fonts[size] = love.graphics.newFont(("res/%s.ttf"):format(name), size)
+       FONTS[name] = fonts
+end
+
+function Text.set_font(name, size)
+       if FONTS[name][size] then
+               love.graphics.setFont(FONTS[name][size])
+       end
+end
+
+return module { Text }
diff --git a/src/old-ui/ui.lua b/src/old-ui/ui.lua
new file mode 100644 (file)
index 0000000..23f0c05
--- /dev/null
@@ -0,0 +1,114 @@
+import {
+       revipairs = "src.utils:revipairs";
+       scissor_points = "src.utils:scissor_points";
+}
+
+class "Ui" {
+       init = function(self)
+               self.regions = {}
+               self.active_region = nil
+       end;
+
+       add_region = function(self, region)
+               table.insert(self.regions, region)
+               table.sort(self.regions)
+       end;
+
+       onmousedown = function(self, button, x, y)
+               local region = nil
+               for _, r in revipairs(self.regions) do
+                       if r.rect:contains(x, y) then
+                               region = r
+                               break
+                       end
+               end
+
+               if region ~= nil then
+                       region:onmousedown(button, x - region.rect.x, y - region.rect.y, x, y)
+               end
+       end;
+
+       onmouseup = function(self, button, x, y)
+               local region = nil
+               for _, r in revipairs(self.regions) do
+                       if r.rect:contains(x, y) then
+                               region = r
+                               break
+                       end
+               end
+
+               if region ~= nil then
+                       region:onmouseup(button, x - region.rect.x, y - region.rect.y, x, y)
+               end
+       end;
+
+       onmousemove = function(self, x, y, dx, dy)
+               local region = nil
+               for _, r in revipairs(self.regions) do
+                       if r.rect:contains(x, y) then
+                               region = r
+                               break
+                       end
+               end
+
+               if region ~= self.active_region then
+                       if self.active_region then
+                               self.active_region:onmouseleave()
+                       end
+
+                       if region ~= nil then
+                               region:onmouseenter()
+                       end
+               end
+
+               if region ~= nil then
+                       region:onmousemove(x - region.rect.x, y - region.rect.y, dx, dy)
+               end
+
+               self.active_region = region
+       end;
+
+       onmousewheel = function(self, dx, dy)
+               if not self.active_region then return end
+               self.active_region:onmousewheel(dx, dy)
+       end;
+
+       onkeydown = function(self, key)
+               if not self.active_region then return end
+               self.active_region:onkeydown(key)
+       end;
+
+       onkeyup = function(self, key)
+               if not self.active_region then return end
+               self.active_region:onkeyup(key)
+       end;
+
+       onresize = function(self, new_width, new_height)
+               for _, region in ipairs(self.regions) do
+                       region:onresize(new_width, new_height)
+               end
+       end;
+
+       update = function(self, dt)
+               for i, region in ipairs(self.regions) do
+                       region:update(dt)
+
+                       if region.should_delete then
+                               table.remove(self.regions, i)
+                       end
+               end
+       end;
+
+       draw = function(self)
+               for _, region in ipairs(self.regions) do
+                       love.graphics.push()
+                       love.graphics.setScissor(scissor_points(region.rect.x, region.rect.y, region.rect.w, region.rect.h))
+                       love.graphics.translate(region.rect.x, region.rect.y)
+                       region:draw()
+                       love.graphics.setScissor()
+                       love.graphics.pop()
+               end
+       end;
+}
+
+return module { Ui }
diff --git a/src/ui.lua b/src/ui.lua
new file mode 100644 (file)
index 0000000..99c3c79
--- /dev/null
@@ -0,0 +1,12 @@
+import {
+}
+
+ui_class = {}
+function ui_class:make()
+end
+
+ui = singleton(ui_class)
+
+return module {
+       ui
+}
diff --git a/src/ui/ui.lua b/src/ui/ui.lua
deleted file mode 100644 (file)
index 73d3c43..0000000
+++ /dev/null
@@ -1,546 +0,0 @@
-import {
-       pprint = "lualib.pprint";
-       Rectangle = "src.utils:Rectangle";
-       uuid = "src.utils:uuidv4";
-       revipairs = "src.utils:revipairs";
-       scissor_points = "src.utils:scissor_points";
-}
-
-class "Ui" {
-       init = function(self)
-               self.regions = {}
-               self.active_region = nil
-       end;
-
-       add_region = function(self, region)
-               table.insert(self.regions, region)
-               table.sort(self.regions)
-       end;
-
-       onmousedown = function(self, button, x, y)
-               local region = nil
-               for _, r in revipairs(self.regions) do
-                       if r.rect:contains(x, y) then
-                               region = r
-                               break
-                       end
-               end
-
-               if region ~= nil then
-                       region:onmousedown(button, x - region.rect.x, y - region.rect.y, x, y)
-               end
-       end;
-
-       onmouseup = function(self, button, x, y)
-               local region = nil
-               for _, r in revipairs(self.regions) do
-                       if r.rect:contains(x, y) then
-                               region = r
-                               break
-                       end
-               end
-
-               if region ~= nil then
-                       region:onmouseup(button, x - region.rect.x, y - region.rect.y, x, y)
-               end
-       end;
-
-       onmousemove = function(self, x, y, dx, dy)
-               local region = nil
-               for _, r in revipairs(self.regions) do
-                       if r.rect:contains(x, y) then
-                               region = r
-                               break
-                       end
-               end
-
-               if region ~= self.active_region then
-                       if self.active_region then
-                               self.active_region:onmouseleave()
-                       end
-
-                       if region ~= nil then
-                               region:onmouseenter()
-                       end
-               end
-
-               if region ~= nil then
-                       region:onmousemove(x - region.rect.x, y - region.rect.y, dx, dy)
-               end
-
-               self.active_region = region
-       end;
-
-       onmousewheel = function(self, dx, dy)
-               if not self.active_region then return end
-               self.active_region:onmousewheel(dx, dy)
-       end;
-
-       onkeydown = function(self, key)
-               if not self.active_region then return end
-               self.active_region:onkeydown(key)
-       end;
-
-       onkeyup = function(self, key)
-               if not self.active_region then return end
-               self.active_region:onkeyup(key)
-       end;
-
-       onresize = function(self, new_width, new_height)
-               for _, region in ipairs(self.regions) do
-                       region:onresize(new_width, new_height)
-               end
-       end;
-
-       update = function(self, dt)
-               for i, region in ipairs(self.regions) do
-                       region:update(dt)
-
-                       if region.should_delete then
-                               table.remove(self.regions, i)
-                       end
-               end
-       end;
-
-       draw = function(self)
-               for _, region in ipairs(self.regions) do
-                       love.graphics.push()
-                       love.graphics.setScissor(scissor_points(region.rect.x, region.rect.y, region.rect.w, region.rect.h))
-                       love.graphics.translate(region.rect.x, region.rect.y)
-                       region:draw()
-                       love.graphics.setScissor()
-                       love.graphics.pop()
-               end
-       end;
-}
-
-class "UiRegion" {
-       init = function(self, ui)
-               self.active = false
-               self.ui = ui
-
-               self.selected_object = nil
-               self.objects = {}
-
-               self.rect = Rectangle(0, 0, 0, 0)
-               self.background_color = { 0, 0, 0 }
-               self.layer = 0
-
-               -- Internals
-               self.is_mouse_down = false
-               self.fire_onclick = false
-               self.should_delete = false
-               self.hovered_object = nil
-       end;
-
-       add_object = function(self, obj)
-               obj.ui = self.ui
-               table.insert(self.objects, obj)
-       end;
-
-       -- Used for sorting in the Ui
-       __lt = function(self, other)
-               return self.layer < other.layer
-       end;
-
-       _obj_rect = function(self, obj)
-               return obj.rect
-       end;
-
-       set_selected_object = function(self, new_selected)
-               if self.selected_object ~= new_selected then
-                       if self.selected_object ~= nil then
-                               self.selected_object:onunselect()
-                       end
-
-                       self.selected_object = new_selected
-
-                       if self.selected_object ~= nil then
-                               self.selected_object.order = 0
-
-                               for _, obj in ipairs(self.objects) do
-                                       if obj ~= self.selected_obj then
-                                               obj.order = obj.order + 1
-                                       end
-                               end
-
-                               table.sort(self.objects, function(a, b)
-                                       return a.order > b.order
-                               end)
-
-                               self.selected_object:onselect()
-                       end
-               end
-       end;
-
-       onmousedown = function(self, button, x, y, ox, oy)
-               self.is_mouse_down = button
-               self.fire_onclick = true
-               local new_selected = nil
-
-               for _, obj in revipairs(self.objects) do
-                       if self:_obj_rect(obj):contains(x, y) then
-                               new_selected = obj
-                               break
-                       end
-               end
-
-               self:set_selected_object(new_selected)
-
-               if self.selected_object then
-                       self.selected_object:onmousedown(button, x, y, ox, oy)
-               end
-       end;
-
-       onmouseup = function(self, button, x, y, ox, oy)
-               self.is_mouse_down = false
-
-               if self.selected_object ~= nil and self.fire_onclick then
-                       local obj_rect = self:_obj_rect(self.selected_object)
-                       self.selected_object:onclick(button, x - obj_rect.x, y - obj_rect.y, ox, oy)
-               end
-       end;
-
-       onmousemove = function(self, x, y, dx, dy)
-               local old_hovered = self.hovered_object
-
-               self.hovered_object = nil
-               for _, obj in revipairs(self.objects) do
-                       if self:_obj_rect(obj):contains(x, y) then
-                               obj:onhover(x, y)
-                               self.hovered_object = obj
-                               break
-                       end
-               end
-
-               if old_hovered and self.hovered_object ~= old_hovered then
-                       old_hovered:onunhover()
-               end
-
-               self:set_selected_object(self.hovered_object)
-
-               if self.is_mouse_down and self.selected_object ~= nil then
-                       local obj_rect = self:_obj_rect(self.selected_object)
-                       self.selected_object:ondrag(self.is_mouse_down, x - obj_rect.x, y - obj_rect.y, dx, dy)
-                       self.fire_onclick = self.fire_onclick and (math.abs(dx) + math.abs(dy)) < 2
-               end
-       end;
-       onmouseenter = function(self)
-               self.active = true
-       end;
-
-       onmouseleave = function(self)
-               self.active = false;
-               self.is_mouse_down = false
-               self.fire_onclick = false
-
-               self:set_selected_object(nil)
-       end;
-
-       onmousewheel = function(self, dx, dy)
-               if self.selected_object ~= nil then
-                       self.selected_object:onwheel(dx, dy)
-               end
-       end;
-
-       onresize = function(self, new_width, new_height) end;
-
-       onkeydown = function(self, key) end;
-       onkeyup = function(self, key) end;
-
-       update = function(self, dt)
-               for _, obj in ipairs(self.objects) do
-                       obj:update(dt)
-               end
-       end;
-
-       draw = function(self)
-               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
-
-               for _, obj in ipairs(self.objects) do
-                       love.graphics.push()
-                       -- love.graphics.translate(obj.rect.x, obj.rect.y)
-                       obj:draw()
-                       love.graphics.pop()
-               end
-       end;
-}
-
-class "ScrollingUiRegion" [UiRegion] {
-       init = function(self, ui)
-               UiRegion.init(self, ui)
-
-               self.offset = { x = 0; y = 0; }
-               self.line_color = { 0, 0, 0 }
-
-               self.zoom = 1
-       end;
-
-       _obj_rect = function(self, obj)
-               return obj.rect
-       end;
-
-       _transform_mouse = function(self, x, y)
-               return
-                       (x - self.rect.w / 2) / self.zoom + self.rect.w / 2 - self.offset.x,
-                       (y - self.rect.h / 2) / self.zoom + self.rect.h / 2 - self.offset.y
-       end;
-
-       onmousedown = function(self, button, x, y, ox, oy)
-               local mx, my = self:_transform_mouse(x, y)
-               UiRegion.onmousedown(self, button, mx, my, ox, oy)
-       end;
-
-       onmouseup = function(self, button, x, y, ox, oy)
-               local mx, my = self:_transform_mouse(x, y)
-               UiRegion.onmouseup(self, button, mx, my, ox, oy)
-       end;
-
-       onmousemove = function(self, x, y, dx, dy)
-               local mx, my = self:_transform_mouse(x, y)
-               UiRegion.onmousemove(self, mx, my, dx / self.zoom, dy / self.zoom)
-
-               if self.is_mouse_down and self.selected_object == nil then
-                       self.offset.x = self.offset.x + dx / self.zoom
-                       self.offset.y = self.offset.y + dy / self.zoom
-               end
-       end;
-
-       onmousewheel = function(self, dx, dy)
-               UiRegion.onmousewheel(self, dx, dy)
-
-               if self.selected_object == nil then
-                       self:change_zoom(dy > 0 and 1.05 or (1 / 1.05))
-               end
-       end;
-
-       change_zoom = function(self, multiplier)
-               self.zoom = self.zoom * multiplier
-               -- if self.zoom >= 1 then self.zoom = 1 end
-               if self.zoom <= 0.2 then self.zoom = 0.2 end
-       end;
-
-       update = function(self, dt)
-               UiRegion.update(self, dt)
-
-               if self.active then
-                       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
-                               self:change_zoom(1 + 0.5 * dt)
-                       end
-                       if love.keyboard.isDown "a" then
-                               self:change_zoom(1 - 0.5 * dt)
-                       end
-               end
-       end;
-
-       draw = function(self)
-               love.graphics.push()
-
-               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
-               local tmpbgcolor = self.background_color
-               self.background_color = nil
-
-               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)
-               UiRegion.draw(self)
-               love.graphics.pop()
-
-               self.background_color = tmpbgcolor
-       end;
-}
-
-class "UiObject" {
-       init = function(self)
-               self.rect = Rectangle(0, 0, 0, 0)
-               self.uuid = uuid()
-               self.selected = false
-               self.order = math.random(1, 1024)
-               self.ui = {}
-       end;
-
-       onclick = function(self, button, x, y, ox, oy) end;
-       ondrag = function(self, button, x, y, dx, dy) end;
-       onhover = function(self, x, y) end;
-       onunhover = function(self) end;
-       onwheel = function(self, dx, dy) end;
-       onmousedown = function(self, button, x, y, ox, oy)
-               self.selected = true
-       end;
-
-       onselect = function(self)
-               self.selected = true
-       end;
-       onunselect = function(self)
-               self.selected = false
-       end;
-
-       onkey = function(self, button) end;
-
-       update = function(self, dt) end;
-       draw = function(self) end;
-}
-
-class "UiButton" [UiObject] {
-       init = function(self)
-               UiObject.init(self)
-
-               self.text = "Placeholder"
-               self.hovered = false
-               self.background = { 0, 0, 0 }
-               self.hover_background = { 0.2, 0.2, 0.2 }
-               self.click_background = { 0.4, 0.4, 0.4 }
-               self.foreground = { 1, 1, 1 }
-       end;
-
-       onclick = function(self, button, x, y, ox, oy)
-               if button == 1 and self.click then
-                       self.click(self, x, y)
-               end
-               self.selected = false
-       end;
-
-       onhover = function(self, x, y)
-               self.hovered = true
-       end;
-
-       onunhover = function(self)
-               self.hovered = false
-       end;
-
-       draw = function(self)
-               if self.selected then
-                       love.graphics.setColor(self.click_background)
-               elseif self.hovered then
-                       love.graphics.setColor(self.hover_background)
-               else
-                       love.graphics.setColor(self.background)
-               end
-               love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h)
-
-               love.graphics.setColor(self.foreground)
-               love.graphics.printf(self.text, 2, 2, self.rect.w, "center")
-       end;
-}
-
-class "DraggableRect" [UiObject] {
-       init = function(self, x, y, w, h)
-               UiObject.init(self)
-
-               self.rect = Rectangle(x, y, w, h)
-       end;
-
-       ondrag = function(self, button, x, y, dx, dy)
-               if button == 1 then
-                       self.rect.x = self.rect.x + dx
-                       self.rect.y = self.rect.y + dy
-               end
-       end;
-}
-
-programming_font = false
-function render_text(x, y, text)
-       if not programming_font then
-               programming_font = love.graphics.newFont("res/FiraCode-Regular.ttf", 14)
-               love.graphics.setFont(programming_font)
-       end
-
-       local origx = x
-       local fheight = programming_font:getHeight()
-       local indent_level = 0
-       local indentation = ""
-       local fresh_newline = true
-
-       for _, txt in ipairs(text) do
-               if txt.text then
-                       local printed = txt.text
-                       if fresh_newline then
-                               if txt.post_indent then
-                                       printed = txt.text .. indentation
-                               else
-                                       printed = indentation .. txt.text
-                               end
-                               fresh_newline = false
-                       end
-
-                       if txt.background then
-                               love.graphics.setColor(txt.background)
-                               love.graphics.rectangle("fill", x, y, programming_font:getWidth(txt.text), fheight + 2);
-                       end
-
-                       if txt.color then
-                               love.graphics.setColor(txt.color)
-                       end
-                       love.graphics.print(printed, x, y)
-
-                       x = x + programming_font:getWidth(printed)
-               end
-
-               if txt.newline then
-                       y = y + fheight + 2;
-                       x = origx
-                       fresh_newline = true
-               end
-
-               if txt.change_indent then
-                       indent_level = indent_level + txt.change_indent
-                       indentation = ""
-                       for i=1,indent_level do
-                               indentation = indentation .. "  "
-                       end
-               end
-       end
-
-       return x, y
-end
-
-return module {
-       Ui = Ui;
-       UiRegion = UiRegion;
-       ScrollingUiRegion = ScrollingUiRegion;
-       UiObject = UiObject;
-       UiButton = UiButton;
-       DraggableRect = DraggableRect;
-
-       render_text = render_text;
-}
index 812847a141e35ddda9607df381a57926bb437d22..86167b3311bce8eb96b692526de09d9826351fe7 100644 (file)
@@ -131,6 +131,26 @@ function wrap(f, ...)
        return function() f(unpack(a)) end
 end
 
+function draw_arrow(sx, sy, tx, ty)
+       love.graphics.line(sx, sy, tx, ty)
+
+       local dx = tx - sx
+       local dy = ty - sy
+       local d = math.sqrt(dx * dx + dy * dy)
+       dx = 18 * dx / d
+       dy = 18 * dy / d
+
+       ox = dy
+       oy = -dx
+
+       love.graphics.polygon(
+               "fill",
+               tx, ty,
+               tx - dx + ox, ty - dy + oy,
+               tx - dx - ox, ty - dy - oy
+       )
+end
+
 return module {
        buffered_read = buffered_read;
        build_str = build_str;
@@ -141,6 +161,7 @@ return module {
        wrap = wrap;
 
        scissor_points = scissor_points;
+       draw_arrow = draw_arrow;
 
        Rectangle = Rectangle;
        Stack = Stack;