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
--- /dev/null
+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
+}
return {
COLOR_SCHEMES = COLOR_SCHEMES;
- COLOR_SCHEME = COLOR_SCHEMES.DARK;
+ COLOR_SCHEME = COLOR_SCHEMES.LIGHT;
}
[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
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
--- /dev/null
+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;
+}
--- /dev/null
+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;
+}
--- /dev/null
+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 }
--- /dev/null
+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 }
--- /dev/null
+import {
+}
+
+ui_class = {}
+function ui_class:make()
+end
+
+ui = singleton(ui_class)
+
+return module {
+ ui
+}
+++ /dev/null
-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;
-}
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;
wrap = wrap;
scissor_points = scissor_points;
+ draw_arrow = draw_arrow;
Rectangle = Rectangle;
Stack = Stack;