From 539ef72c8173de4666f174c9ac7a93e04a42332a Mon Sep 17 00:00:00 2001 From: JayK Date: Thu, 4 Jun 2026 23:53:15 -0700 Subject: [PATCH] stuff YAY, xavier. work on the engine --- .vscode/settings.json | 7 +- .vscode/tasks.json | 10 ++ editor/ui.lua | 241 +++++++++++++++++++++++++++--------------- main.lua | 24 +++-- 4 files changed, 190 insertions(+), 92 deletions(-) create mode 100644 .vscode/tasks.json diff --git a/.vscode/settings.json b/.vscode/settings.json index cad84f0..e3e1e81 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,13 @@ { "Lua.workspace.library": [ - "${addons}/love2d/module/library" + ".vscode/love2d" ], "Lua.runtime.version": "LuaJIT", "Lua.runtime.special": { "love.filesystem.load": "loadfile" }, - "Lua.workspace.checkThirdParty": false + "Lua.workspace.checkThirdParty": false, + "Lua.diagnostics.globals": [ + "love" + ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..fceaf92 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,10 @@ +{ + "version": "2.0.0", + "tasks": [{ + "label": "Run Love2D", + "type": "shell", + "command": "love .", + "options": { "cwd": "${workspaceFolder}" }, + "group": { "kind": "build", "isDefault": true } + }] +} \ No newline at end of file diff --git a/editor/ui.lua b/editor/ui.lua index 4e9cb0a..08e679e 100644 --- a/editor/ui.lua +++ b/editor/ui.lua @@ -3,119 +3,194 @@ local ui = {} ui.theme = { bg = {0.13, 0.13, 0.14}, panel = {0.17, 0.17, 0.19}, - border = {0.28, 0.28, 0.30}, -- also known as outline + border = {0.28, 0.28, 0.30}, borderwidth = 3, text = {0.90, 0.90, 0.90}, - text_dim = {0.50, 0.50, 0.55}, -- low-key forgot - accent = {0.27, 0.52, 0.90}, -- low-key also forgot + text_dim = {0.50, 0.50, 0.55}, + accent = {0.27, 0.52, 0.90}, hover = {0.22, 0.25, 0.35}, - active = {0.20, 0.40, 0.75}, -- i low-key forgot again, probably made for when a toggle or something is turned on + active = {0.20, 0.40, 0.75}, } -ui.events = {} -ui._wasDown = false -ui._focused = nil -ui._inputs = {} -ui._claimedFocus = false +-- per-frame state (most/all gets reset if i remember) +ui._wasDown = false +ui._focused = nil +ui._inputs = {} +ui._claimedFocus = false ui._clickConsumed = false +ui._elements = {} -- queue of UI elements, processed at end of frame +ui._hoverConsumed = false -- so it sets hovered to the correct thing -function ui.emit(event) - ui.events[event] = true -- should change to a custom ID soon to prevent conflicts. -end - -function ui.on(event) - return ui.events[event] == true -end - -function ui.flush() -- all issues is because of this btw (joke, kinda. bunch of bugs from it) - local isDown = love.mouse.isDown(1) - if isDown and not ui._wasDown and not ui._claimedFocus then - ui._focused = nil - end - ui._wasDown = isDown -- update AFTER using it - ui._claimedFocus = false - ui.events = {} - ui._clickConsumed = false -end - -function ui.panel(x, y, w, h) -- this thingy magic panel creator makes a ui with outline. (x pos, y pos, width, height) +-- draws a basic bordered panel +function ui._draw_panel(e) love.graphics.setColor(ui.theme.panel) - love.graphics.rectangle("fill", x, y, w, h) + love.graphics.rectangle("fill", e.x, e.y, e.w, e.h) love.graphics.setColor(ui.theme.border) love.graphics.setLineWidth(ui.theme.borderwidth) - love.graphics.rectangle("line", x, y, w, h) + love.graphics.rectangle("line", e.x, e.y, e.w, e.h) end -function ui.button(x, y, w, h, text) - local mousex, mousey = love.mouse.getPosition() - local hovered = mousex > x and mousex < x + w and mousey > y and mousey < y + h -- calculaters if its touching the box - if hovered then - -- makes dimed panel then text ontop - love.graphics.setColor(ui.theme.hover) - love.graphics.rectangle("fill", x, y, w, h) - love.graphics.setColor(ui.theme.border) - love.graphics.setLineWidth(ui.theme.borderwidth) - love.graphics.rectangle("line", x, y, w, h) - ui.text(x, y, w, h, text) - else - ui.textPanel(x, y, w, h, text) -- just normal text with a background - end - - if hovered and love.mouse.isDown(1) and not ui._wasDown and not ui._clickConsumed then - ui.emit(text) - ui._clickConsumed = true - end - - return hovered -- returns hovered, this means hovered is returned. and hovered being returned means it returns what hovered is -end - -function ui.textPanel(x, y, w, h, text) -- expand on this, havent fully read the text documentation so i dont know if i should have more - ui.panel(x, y, w, h) - ui.text(x, y, w, h, text) -end - -function ui.text(x, y, w, h, text) +-- draws centered, wrapped, clipped text inside a box +function ui._draw_text(e) local font = love.graphics.getFont() - local _, lines = font:getWrap(text, w) - local lineCount = #lines - local lineHeight = font:getHeight() - local totalHeight = lineCount * lineHeight - local startY = y + (h - totalHeight) / 2 + local _, lines = font:getWrap(e.text, e.w) + local totalHeight = #lines * font:getHeight() + local startY = e.y + (e.h - totalHeight) / 2 - love.graphics.setScissor(x, y, w, h) + love.graphics.setScissor(e.x, e.y, e.w, e.h) -- clip to box bounds love.graphics.setColor(ui.theme.text) - love.graphics.printf(text, x, startY, w, "center") - love.graphics.setScissor() -- limits drawing space. + love.graphics.printf(e.text, e.x, startY, e.w, "center") + love.graphics.setScissor() -- reset clip end -function ui.textInput(x, y, w, h, placeholder) -- two days for this to work... - local text = ui._inputs[placeholder] or "" - local focused = ui._focused == placeholder - local mousex, mousey = love.mouse.getPosition() - local clickedInside = mousex > x and mousex < x + w and mousey > y and mousey < y + h +-- draws a button, highlighted on hover +function ui._draw_button(e) + local mx, my = love.mouse.getPosition() + local hovered = not ui._hoverConsumed and mx > e.x and mx < e.x + e.w and my > e.y and my < e.y + e.h - if love.mouse.isDown(1) and not ui._wasDown and clickedInside then - ui._focused = placeholder - ui._claimedFocus = true - end + love.graphics.setColor(e._hovered and ui.theme.hover or ui.theme.panel) + love.graphics.rectangle("fill", e.x, e.y, e.w, e.h) + love.graphics.setColor(ui.theme.border) + love.graphics.setLineWidth(ui.theme.borderwidth) + love.graphics.rectangle("line", e.x, e.y, e.w, e.h) + ui._draw_text(e) +end + +-- draws a text input, changes appearance based on focus state +function ui._draw_textInput(e) + local focused = ui._focused == e.placeholder + local text = ui._inputs[e.placeholder] or "" if focused then - ui.panel(x, y, w, h) + love.graphics.setColor(ui.theme.panel) + love.graphics.rectangle("fill", e.x, e.y, e.w, e.h) + love.graphics.setColor(ui.theme.accent) + love.graphics.setLineWidth(ui.theme.borderwidth) + love.graphics.rectangle("line", e.x, e.y, e.w, e.h) + if text == "" then + -- show dimmed placeholder text when empty local font = love.graphics.getFont() - local lineHeight = font:getHeight() - local startY = y + (h - lineHeight) / 2 + local startY = e.y + (e.h - font:getHeight()) / 2 love.graphics.setColor(ui.theme.text_dim) - love.graphics.printf(placeholder, x + 4, startY, w - 8, "left") + love.graphics.printf(e.placeholder, e.x + 4, startY, e.w - 8, "left") else - ui.text(x, y, w, h, text) + ui._draw_text({ x=e.x, y=e.y, w=e.w, h=e.h, text=text }) end else - ui.button(x, y, w, h, text == "" and placeholder or text) + -- unfocused + local display = text == "" and e.placeholder or text + ui._draw_button({ x=e.x, y=e.y, w=e.w, h=e.h, text=display }) end end +-- bordered panel, no interaction +function ui.panel(x, y, w, h) + table.insert(ui._elements, { + type = "panel", + x=x, y=y, w=w, h=h + }) +end +-- centered text inside a box, no background +function ui.text(x, y, w, h, text) + table.insert(ui._elements, { + type = "text", + x=x, y=y, w=w, h=h, text=text + }) +end + +-- panel with centered text inside (justs draws a panel and puts text ontop, dependant on ui.text) +function ui.textPanel(x, y, w, h, text) + table.insert(ui._elements, { + type = "textPanel", + x=x, y=y, w=w, h=h, text=text + }) +end + +-- clickable button. callback is called when clicked (optional) +-- example: ui.button(x, y, w, h, "Click me", function() print("hi") end) +function ui.button(x, y, w, h, text, callback) + table.insert(ui._elements, { + type = "button", + x=x, y=y, w=w, h=h, text=text, + callback = callback + }) +end + +-- text input field. placeholder is also used as its unique ID +-- read typed text with: ui._inputs["placeholder"] +function ui.textInput(x, y, w, h, placeholder) + table.insert(ui._elements, { + type = "textInput", + x=x, y=y, w=w, h=h, + placeholder = placeholder + }) +end + +function ui.process() + local mx, my = love.mouse.getPosition() + local clicked = love.mouse.isDown(1) and not ui._wasDown + for i = #ui._elements, 1, -1 do + local e = ui._elements[i] + local inside = mx > e.x and mx < e.x + e.w and my > e.y and my < e.y + e.h + + -- consume hover for top thing + if inside and not ui._hoverConsumed then + e._hovered = true + ui._hoverConsumed = true + else + e._hovered = false + end + end + + -- reverse hit test so topmost element gets the click, similar to above but just for clicking + for i = #ui._elements, 1, -1 do + local e = ui._elements[i] + if clicked and not ui._clickConsumed then + local inside = mx > e.x and mx < e.x + e.w and my > e.y and my < e.y + e.h + if inside then + if e.type == "button" then + if e.callback then e.callback() end -- fire callback if provided + ui._clickConsumed = true + + elseif e.type == "textInput" then + ui._focused = e.placeholder + ui._claimedFocus = true + ui._clickConsumed = true + end + end + end + end + + -- clicked outside everything, clear focus + if clicked and not ui._claimedFocus then + ui._focused = nil + end + + -- draw in order + for i = 1, #ui._elements do + local e = ui._elements[i] + if e.type == "panel" then ui._draw_panel(e) + elseif e.type == "text" then ui._draw_text(e) + elseif e.type == "textPanel" then ui._draw_panel(e) ui._draw_text(e) + elseif e.type == "button" then ui._draw_button(e) + elseif e.type == "textInput" then ui._draw_textInput(e) + end + end + + -- clear queue/list for next frame (leave at end) + ui._elements = {} +end + +function ui.flush() + ui._wasDown = love.mouse.isDown(1) + ui._claimedFocus = false + ui._clickConsumed = false + ui._hoverConsumed = false +end + +-- text things, both used in the process of rendering text function ui.textinput_char(t) if ui._focused then ui._inputs[ui._focused] = (ui._inputs[ui._focused] or "") .. t diff --git a/main.lua b/main.lua index ad08f40..8a3f7af 100644 --- a/main.lua +++ b/main.lua @@ -1,3 +1,5 @@ +-- ctrl+shift+b for launching game + local ui = require("editor.ui") function love.load() @@ -19,12 +21,20 @@ function love.update(dt) end function love.draw() - ui.panel(50,50,50,50) - ui.button(100,200,100,100,"hello world") - if ui.on("hello world") then -- detect if the button is pressed. - ui.panel(250,250,100,100) - end - ui.textInput(50,400,100,100,"textInput") + ui.panel(50, 50, 50, 50) + + ui.button(100, 200, 100, 100, "hello world", function() + print("clicked!") + end) - ui.flush() -- KEEP AT END + ui.textInput(50, 400, 100, 100, "textInput") + ui.button(200,200,100,100, "test", function () + print("below") + end) + ui.button(200,250,100,100, "test2", function () + print("on top") + end) + + ui.process() -- hit test + draw + ui.flush() -- reset input state end \ No newline at end of file