working on game
This commit is contained in:
+104
-22
@@ -3,24 +3,34 @@ local theme = require("gui.core.theme")
|
||||
local color = require("gui.core.color")
|
||||
local multi, thread = require("multi"):init()
|
||||
local mediaProc = gui:newProcessor()
|
||||
local miscProc = gui:newProcessor()
|
||||
|
||||
local function noOf(sx,sy,sw,sh)
|
||||
return nil,nil,nil,nil,sx,sy,sw,sh
|
||||
end
|
||||
|
||||
function gui:newVideoPlayer(source, x, y, w, h, sx, sy, sw, sh)
|
||||
local window = gui:newWindow(x, y, w, h, source, true, theme:new({
|
||||
function gui:newVideoPlayer(source, x, y, w, h, sx, sy, sw, sh, draggable, th)
|
||||
local window = self:newWindow(x, y, w, h, sx, sy, sw, sh, "", draggable, th or theme:new({
|
||||
primary = "#000000",
|
||||
primaryDark = "#10465c",
|
||||
primaryText = "#ffffff"
|
||||
}))
|
||||
local video = window:newVideo(source, 0, 0, 0, 0, 0, .05, 1, .75)
|
||||
local play_pause = window:newImageButton("gui/assets/play.png",0,0,0,0,.45,.82,0,.175)
|
||||
local seek = window:newFrame(0,0,0,0,0,.8,1,.015)
|
||||
seek.color = color.new("#3c434c")
|
||||
local seeker = seek:newFrame(0,0,0,0,0,.1,0,.8)
|
||||
seeker.drawBorder = false
|
||||
seeker.color = color.new("#0c278a")
|
||||
local length = video:getDuration()
|
||||
local play_pause = window:newImageButton("gui/assets/play.png",0,0,0,0,.45,.86,0,.14)
|
||||
local seek = window:newProgressBar(0,0,0,0,0,.8,1,.05,length*100,0)
|
||||
seek.OnProgressUpdated(function(self,_,value, drag)
|
||||
if drag then
|
||||
local status = video:isPlaying()
|
||||
video:seek(value/100)
|
||||
if status then
|
||||
video:play()
|
||||
else
|
||||
video:stop()
|
||||
end
|
||||
end
|
||||
end)
|
||||
seek:isClickable(true)
|
||||
play_pause.square = "h"
|
||||
play_pause.isPaused = true
|
||||
play_pause:OnReleased(function(self)
|
||||
@@ -34,16 +44,31 @@ function gui:newVideoPlayer(source, x, y, w, h, sx, sy, sw, sh)
|
||||
self.isPaused = not self.isPaused
|
||||
end)
|
||||
|
||||
local length = video:getDuration()
|
||||
mediaProc:newThread(function()
|
||||
while true do
|
||||
thread.yield()
|
||||
seeker:setDualDim(nil,nil,nil,nil,nil,nil,video:tell()/length)
|
||||
thread.hold(function() return video:isPlaying() end)
|
||||
local p = video:tell()
|
||||
seek:update(p*100)
|
||||
end
|
||||
end)
|
||||
|
||||
-- print()
|
||||
function window:seek(...)
|
||||
video.video:seek(...)
|
||||
end
|
||||
|
||||
function window:play()
|
||||
video:play()
|
||||
end
|
||||
|
||||
function window:stop()
|
||||
video:stop()
|
||||
end
|
||||
|
||||
window.OnClose(function()
|
||||
video:pause()
|
||||
end)
|
||||
|
||||
return window
|
||||
end
|
||||
|
||||
function gui:newCheckbox(label, x, y, size, sx, sy, checked)
|
||||
@@ -117,21 +142,68 @@ end
|
||||
|
||||
function gui:newProgressBar(x, y, w, h, sx, sy, sw, sh, count, value)
|
||||
local value = value or 0
|
||||
local count = count or 100
|
||||
local progressbar = self:newFrame(x,y,w,h,sx,sy,sw,sh)
|
||||
local fillframe = progressbar:newFrame(noOf(.025, .1, .95, .8))
|
||||
local fillframe = progressbar:newFrame(2,2,-4,-4,0,0,1,1)
|
||||
local fill = fillframe:newFrame(noOf(0, 0, 1, 1))
|
||||
|
||||
local percentDisplay = fillframe:newTextLabel("",noOf(0,0,1,1))
|
||||
percentDisplay.align = gui.ALIGN_CENTER
|
||||
percentDisplay.textColor = color.new("#CC5500")
|
||||
fillframe.visibility = 0
|
||||
progressbar.color = color.new("#000000")
|
||||
fill.color = color.new("#ffffff")
|
||||
progressbar.fillframe = fillframe
|
||||
progressbar.fill = fill
|
||||
progressbar.display = percentDisplay
|
||||
progressbar.OnProgressUpdated = multi:newConnection()
|
||||
percentDisplay.visibility = 0
|
||||
local displayPercent = false
|
||||
|
||||
function progressbar:update(value)
|
||||
if value > count then value = count end
|
||||
if value < 0 then value = 0 end
|
||||
function progressbar:showPercent(bool)
|
||||
displayPercent = bool
|
||||
if bool then
|
||||
miscProc:newThread(function()
|
||||
thread.skip(2)
|
||||
_,_,_h = percentDisplay:getAbsolutes()
|
||||
percentDisplay:setFont(math.floor(h/1.3))
|
||||
self:update()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function progressbar:isClickable(bool)
|
||||
fillframe:respectHierarchy(not bool)
|
||||
-- Makes the bar updatable by clicking on it
|
||||
fillframe:enableDragging(bool and gui.MOUSE_PRIMARY)
|
||||
end
|
||||
|
||||
local calcFunc = function(self, dx, dy, x, y, istouch)
|
||||
local sx, sy, sw, sh = self:getAbsolutes()
|
||||
if x >= sx and x <= sx + sw then
|
||||
progressbar:update((((x - sx)/sw) * count), true)
|
||||
end
|
||||
end
|
||||
|
||||
fillframe:OnDragStart(calcFunc)
|
||||
|
||||
fillframe.OnDragging(calcFunc)
|
||||
|
||||
fillframe.OnPressed(function(self, x, y, dx, dy, istouch)
|
||||
calcFunc(self, dx, dy, x, y, istouch)
|
||||
end)
|
||||
|
||||
function progressbar:update(v, drag)
|
||||
v = v or value
|
||||
if v > count then v = count end
|
||||
if v < 0 then v = 0 end
|
||||
local percent = value/count
|
||||
fill:setDualDim(noOf(nil,nil,percent))
|
||||
if displayPercent then
|
||||
percentDisplay.text = math.floor((percent*100)+.5).. "%"
|
||||
percentDisplay:centerFont()
|
||||
end
|
||||
value = v
|
||||
self.OnProgressUpdated:Fire(self, percent, value, drag)
|
||||
end
|
||||
|
||||
function progressbar:add(n)
|
||||
@@ -151,17 +223,27 @@ function gui:newProgressBar(x, y, w, h, sx, sy, sw, sh, count, value)
|
||||
end
|
||||
|
||||
function progressbar:max()
|
||||
value = count
|
||||
self:update(value)
|
||||
self:update(count)
|
||||
end
|
||||
|
||||
function progressbar:min()
|
||||
value = 0
|
||||
self:update(value)
|
||||
self:update(0)
|
||||
end
|
||||
|
||||
function progressbar:half()
|
||||
self:update(math.floor(count/2))
|
||||
end
|
||||
|
||||
function progressbar:getPercent()
|
||||
return math.floor(((value/count)*100)+.5)
|
||||
end
|
||||
|
||||
function progressbar:getValue()
|
||||
return value
|
||||
end
|
||||
|
||||
progressbar:update(value)
|
||||
|
||||
-- to change colors and modify main components
|
||||
return progressbar, fill, fillframe
|
||||
return progressbar, fill, percentDisplay, fillframe
|
||||
end
|
||||
+11
-5
@@ -312,7 +312,7 @@ end
|
||||
|
||||
-- ── window constructor (unchanged from original) ──────────────────────────────
|
||||
local windowCount = 0
|
||||
function gui:newWindow(x, y, w, h, text, draggable, theme)
|
||||
function gui:newWindow(x, y, w, h, sx, sy, sw, sh, text, draggable, theme)
|
||||
local process = gui:newProcessor(text or "window_"..windowCount)
|
||||
windowCount = windowCount + 1
|
||||
local parent = self
|
||||
@@ -323,9 +323,9 @@ function gui:newWindow(x, y, w, h, text, draggable, theme)
|
||||
local sizenwse = love.mouse.getSystemCursor("sizenwse")
|
||||
local theme = theme or default_theme
|
||||
|
||||
local header = self:newFrame(x, y, w, 35)
|
||||
local header = self:newFrame(x, y, w, 35, sx, sy, sw)
|
||||
header:setRoundness(10, 10, nil, "top")
|
||||
local window = header:newFrame(0, 35, 0, h - 35, 0, 0, 1)
|
||||
local window = header:newFrame(0, 35, 0, h, sx, sy, 1, sh)
|
||||
window.clipDescendants = true
|
||||
local left = window:newFrame(0, -4, 4, 0, 0, 0, 0, 1):tag("left")
|
||||
local right = window:newFrame(-4, -4, 4, 0, 1, 0, 0, 1):tag("right")
|
||||
@@ -384,8 +384,11 @@ function gui:newWindow(x, y, w, h, text, draggable, theme)
|
||||
|
||||
local X = header:newTextButton("", -25, -25, 20, 20, 1, 1)
|
||||
X:setRoundness(10, 10)
|
||||
X:respectHierarchy(false)
|
||||
X.align = gui.ALIGN_CENTER
|
||||
X.color = color.red
|
||||
window.XButton = X
|
||||
|
||||
local darkenX = color.darken(color.red, .2)
|
||||
X.OnEnter(function(self) self.color = darkenX end)
|
||||
X.OnExit(function(self) self.color = color.red end)
|
||||
@@ -403,7 +406,10 @@ function gui:newWindow(x, y, w, h, text, draggable, theme)
|
||||
end)
|
||||
end
|
||||
|
||||
window.OnClose = function() return window end % X.OnPressed
|
||||
window.OnClose = multi:newConnection()
|
||||
X.OnPressed(function(self, ...)
|
||||
window.OnClose:Fire(window, ...)
|
||||
end)
|
||||
window.OnClose(function()
|
||||
header:setParent(gui.virtual)
|
||||
love.mouse.setCursor(pointer)
|
||||
@@ -758,7 +764,7 @@ function gui:showTaskManager()
|
||||
local WIN_W = TOTAL_W + 20
|
||||
local WIN_H = 620
|
||||
|
||||
taskManager = gui:newWindow(0, 0, WIN_W, WIN_H, "Task Manager", true, TM_THEME)
|
||||
taskManager = gui:newWindow(0, 0, WIN_W, WIN_H, nil, nil, nil, nil, "Task Manager", true, TM_THEME)
|
||||
taskManager.clipDescendants = true
|
||||
|
||||
-- ── tab bar ──────────────────────────────────────────────────────────────
|
||||
|
||||
+34
-19
@@ -1,6 +1,6 @@
|
||||
local gui = require("gui")
|
||||
local multi, thread = require("multi"):init()
|
||||
local processor = gui:newProcessor("Transistion Processor")
|
||||
local processor = gui:newProcessor("Transition Processor")
|
||||
local transition = {}
|
||||
|
||||
local width, height, flags = love.window.getMode()
|
||||
@@ -13,23 +13,26 @@ end
|
||||
transition.__index = transition
|
||||
transition.__call = function(t, start, stop, time, ...)
|
||||
local args = {...}
|
||||
return function(st, sp, ti) -- allow these values to be overridden
|
||||
if not (st or start) or not (sp or stop) then return multi.error("start and stop must be supplied") end
|
||||
if start == stop then
|
||||
return function(st, sp, ti)
|
||||
local s = st or start
|
||||
local e = sp or stop
|
||||
local dur = ti or time or 1
|
||||
if not s or not e then return multi.error("start and stop must be supplied") end
|
||||
if s == e then
|
||||
local temp = {
|
||||
OnStep = function() end,
|
||||
OnStep = function() end,
|
||||
OnStop = multi:newConnection()
|
||||
}
|
||||
proc:newTask(function()
|
||||
processor:newTask(function()
|
||||
temp.OnStop:Fire()
|
||||
end)
|
||||
return temp
|
||||
end
|
||||
local handle = t.func(t, start, stop, (ti or time) or 1, unpack(args))
|
||||
local handle = t.func(t, s, e, dur, unpack(args))
|
||||
return {
|
||||
OnStep = handle.OnStatus,
|
||||
OnStop = handle.OnReturn + handle.OnError,
|
||||
Kill = t.Kill
|
||||
Kill = function() t:Kill() end
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -43,11 +46,11 @@ function transition:newTransition(func)
|
||||
c.OnStop = multi:newConnection()
|
||||
c.kill = false
|
||||
|
||||
function c:SetFPS(fps)
|
||||
self.fps = fps
|
||||
function c:SetFPS(f)
|
||||
self.fps = f
|
||||
end
|
||||
|
||||
function c:GetFPS(fps)
|
||||
function c:GetFPS()
|
||||
return self.fps
|
||||
end
|
||||
|
||||
@@ -60,17 +63,29 @@ function transition:newTransition(func)
|
||||
return c
|
||||
end
|
||||
|
||||
transition.glide = transition:newTransition(function(t, start, stop, time, ...)
|
||||
local steps = t.fps*time
|
||||
local piece = time/steps
|
||||
local split = stop-start
|
||||
transition.glide = transition:newTransition(function(t, start, stop, time)
|
||||
local split = stop - start
|
||||
local startTime = love.timer.getTime()
|
||||
local endTime = startTime + time
|
||||
local stepTime = 1 / t.fps
|
||||
|
||||
t.running = true
|
||||
for i = 0, steps do
|
||||
if not(t.kill) then
|
||||
thread.sleep(piece)
|
||||
thread.pushStatus(start + i*(split/steps),piece*i)
|
||||
|
||||
while not t.kill do
|
||||
thread.sleep(stepTime)
|
||||
|
||||
local now = love.timer.getTime()
|
||||
local elapsed = now - startTime
|
||||
local clamped = math.min(elapsed, time)
|
||||
local value = start + (clamped / time) * split
|
||||
|
||||
thread.pushStatus(value, clamped)
|
||||
|
||||
if now >= endTime then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
t.running = false
|
||||
t.kill = false
|
||||
end)
|
||||
|
||||
+190
-22
@@ -136,6 +136,7 @@ updater:newTask(function()
|
||||
Hook("joystickadded", gui.Events.OnJoystickAdded.Fire)
|
||||
end)
|
||||
|
||||
|
||||
-- Hotkeys
|
||||
|
||||
local function noOf(sx,sy,sw,sh)
|
||||
@@ -485,13 +486,13 @@ function gui:canPress(mx, my) -- Get the intersection of the clip area and the s
|
||||
return not (mx > x + w or mx < x or my > y + h or my < y)
|
||||
end
|
||||
|
||||
function gui:isBeingCovered(mx, my)
|
||||
function gui:isBeingCovered(mx, my, respect)
|
||||
-- if not respect then return false end
|
||||
local children = gui:getAllChildren()
|
||||
for i = #children, 1, -1 do
|
||||
if children[i] == self then
|
||||
if children[i] == self or not respect then
|
||||
return false
|
||||
elseif children[i]:canPress(mx, my) and not (children[i] == self) and
|
||||
not (children[i].ignore) then
|
||||
elseif children[i]:canPress(mx, my) and not (children[i] == self) and not (children[i].ignore) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -590,7 +591,8 @@ function gui:getUniques(tab)
|
||||
color = self.color,
|
||||
borderColor = self.borderColor,
|
||||
drawBorder = self.drawborder,
|
||||
rotation = self.rotation
|
||||
rotation = self.rotation,
|
||||
shader = self.shader
|
||||
}
|
||||
|
||||
if tab then for i, v in pairs(tab) do base[i] = tab[i] end end
|
||||
@@ -619,6 +621,20 @@ local function testVisual(c, x, y, button, istouch, presses)
|
||||
return not(c:hasTag("visual") or c:parentHasTag("visual"))
|
||||
end
|
||||
|
||||
local extensions = {}
|
||||
|
||||
function gui.registerExtension(template)
|
||||
table.insert(extensions, template)
|
||||
end
|
||||
|
||||
function gui:extend(c)
|
||||
for i,v in pairs(extensions) do
|
||||
for key, value in pairs(v) do
|
||||
c[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Base Library
|
||||
function gui:newBase(typ, x, y, w, h, sx, sy, sw, sh, virtual)
|
||||
local c = {}
|
||||
@@ -633,7 +649,7 @@ function gui:newBase(typ, x, y, w, h, sx, sy, sw, sh, virtual)
|
||||
|
||||
local function testHierarchy(c, x, y, button, istouch, presses)
|
||||
if hierarchy then
|
||||
return not (global_drag or c:isBeingCovered(x, y))
|
||||
return not (global_drag or c:isBeingCovered(x, y, true))
|
||||
end
|
||||
return true
|
||||
end
|
||||
@@ -723,6 +739,12 @@ function gui:newBase(typ, x, y, w, h, sx, sy, sw, sh, virtual)
|
||||
end)
|
||||
|
||||
local _mouseRelRef = gui.Events.OnMouseReleased(function(x, y, button, istouch, presses)
|
||||
pressed = false -- we need to handle dragging stopped even if an element is not active
|
||||
if dragging and button == dragbutton then
|
||||
dragging = false
|
||||
global_drag = false
|
||||
c.OnDragEnd:Fire(c, dx, dy, x, y, istouch, presses)
|
||||
end
|
||||
if not c:isActive() then return end
|
||||
if c:canPress(x, y) then
|
||||
c.OnReleased:Fire(c, x, y, button, istouch, presses)
|
||||
@@ -731,12 +753,6 @@ function gui:newBase(typ, x, y, w, h, sx, sy, sw, sh, virtual)
|
||||
else
|
||||
c.OnReleasedOther:Fire(c, x, y, button, istouch, presses)
|
||||
end
|
||||
pressed = false
|
||||
if dragging and button == dragbutton then
|
||||
dragging = false
|
||||
global_drag = false
|
||||
c.OnDragEnd:Fire(c, dx, dy, x, y, istouch, presses)
|
||||
end
|
||||
end)
|
||||
|
||||
local _mousePressRef = gui.Events.OnMousePressed(function(x, y, button, istouch, presses)
|
||||
@@ -751,7 +767,7 @@ function gui:newBase(typ, x, y, w, h, sx, sy, sw, sh, virtual)
|
||||
object_focus = c
|
||||
end
|
||||
|
||||
if draggable and button == dragbutton and not c:isBeingCovered(x, y) and
|
||||
if draggable and button == dragbutton and not c:isBeingCovered(x, y, hierarchy) and
|
||||
not global_drag then
|
||||
dragging = true
|
||||
global_drag = true
|
||||
@@ -803,7 +819,9 @@ function gui:newBase(typ, x, y, w, h, sx, sy, sw, sh, virtual)
|
||||
return self
|
||||
end
|
||||
|
||||
function c:respectHierarchy(bool) hierarchy = bool end
|
||||
function c:respectHierarchy(bool)
|
||||
hierarchy = bool
|
||||
end
|
||||
|
||||
local function centerthread()
|
||||
if centerX or centerY then
|
||||
@@ -914,6 +932,41 @@ function gui:newBase(typ, x, y, w, h, sx, sy, sw, sh, virtual)
|
||||
if typ == frame then
|
||||
gui.Events.OnCreated:Fire(c) -- Trigger frame types instantly
|
||||
end
|
||||
-- shader stuff
|
||||
|
||||
function c:setShader(shader)
|
||||
if type(shader) == "string" then
|
||||
self.shader = love.graphics.newShader(shader)
|
||||
else
|
||||
self.shader = shader -- already a compiled love Shader object
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function c:clearShader()
|
||||
self.shader = nil
|
||||
end
|
||||
|
||||
function c:setShaderUniform(name, ...)
|
||||
if not self.shader then return end
|
||||
if self.shader:hasUniform(name) then
|
||||
self.shader:send(name, ...)
|
||||
end
|
||||
end
|
||||
|
||||
function c:enableShaderTime()
|
||||
self.__shaderTime = 0
|
||||
mainupdater.OnLoop(function(_, _, dt)
|
||||
if not self.shader then return end
|
||||
self.__shaderTime = self.__shaderTime + dt
|
||||
if self.shader:hasUniform("time") then
|
||||
self.shader:send("time", self.__shaderTime)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
gui:extend(c, typ)
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
@@ -1122,6 +1175,7 @@ function gui:newTextBase(typ, txt, x, y, w, h, sx, sy, sw, sh)
|
||||
textColor = c.textColor
|
||||
})
|
||||
end
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
@@ -1879,6 +1933,8 @@ function gui:newVideo(source, x, y, w, h, sx, sy, sw, sh)
|
||||
|
||||
function c:tell() return c.video:tell() end
|
||||
|
||||
function c:isPlaying() return c.video:isPlaying() end
|
||||
|
||||
updater:newThread("Video Handler",function()
|
||||
|
||||
local testCompletion = function() -- More intensive test
|
||||
@@ -2048,7 +2104,13 @@ local draw_handler = function(child, no_draw, dt)
|
||||
end
|
||||
end
|
||||
|
||||
if child.shader and band(ctype, image) == 2 then
|
||||
if child.shader then
|
||||
if child.shader:hasUniform("size") then
|
||||
child.shader:send("size", {w, h})
|
||||
end
|
||||
if child.shader:hasUniform("position") then
|
||||
child.shader:send("position", {x, y})
|
||||
end
|
||||
love.graphics.setShader(child.shader)
|
||||
end
|
||||
|
||||
@@ -2066,16 +2128,15 @@ local draw_handler = function(child, no_draw, dt)
|
||||
|
||||
love.graphics.setLineStyle("smooth")
|
||||
love.graphics.setLineWidth(1)
|
||||
|
||||
if drawB then
|
||||
love.graphics.setColor(bbg[1], bbg[2], bbg[3], vis)
|
||||
draw_factor(child,"line", x, y, w, h, rx, ry, nil, nil, segments)
|
||||
end
|
||||
|
||||
if drawB then
|
||||
if roundness == "top" then
|
||||
love.graphics.setColor(bg[1], bg[2], bg[3], vis)
|
||||
draw_factor(child,"fill", x, y + ry / 2, w, h - ry / 2 + 1)
|
||||
--love.graphics.rectangle("fill", x, y + ry / 2, w, h - ry / 2 + 1)
|
||||
love.graphics.setLineStyle("rough")
|
||||
love.graphics.setLineStyle("smooth")
|
||||
love.graphics.setColor(bbg[1], bbg[2], bbg[3], 1)
|
||||
love.graphics.setLineWidth(1)
|
||||
love.graphics.line(x, y + ry, x, y + h + 1, x + 1 + w, y + h + 1,
|
||||
@@ -2087,9 +2148,10 @@ local draw_handler = function(child, no_draw, dt)
|
||||
love.graphics.line(x - 1, y + ry / 2 + 2, x - 1, y + h + 2)
|
||||
love.graphics.line(x + w + 2, y + ry / 2 + 2, x + w + 2, y + h + 2)
|
||||
elseif roundness == "bottom" then
|
||||
love.graphics.setColor(bg[1], bg[2], bg[3], vis)
|
||||
draw_factor(child,"fill", x, y, w, h - ry + 2)
|
||||
--love.graphics.rectangle("fill", x, y, w, h - ry + 2)
|
||||
love.graphics.setLineStyle("rough")
|
||||
love.graphics.setLineStyle("smooth")
|
||||
love.graphics.setColor(bbg[1], bbg[2], bbg[3], 1)
|
||||
love.graphics.setLineWidth(2)
|
||||
love.graphics.line(x - 1, y + ry + 1, x - 1, y - 1, x + w + 1, y - 1,
|
||||
@@ -2123,14 +2185,114 @@ end
|
||||
|
||||
gui.draw_handler = draw_handler
|
||||
|
||||
local function has_blur_ancestor(child)
|
||||
local parent = child.parent
|
||||
while parent and parent ~= gui and parent ~= gui.virtual do
|
||||
if parent.__blur then return parent end
|
||||
parent = parent.parent
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function blur_draw(child, dt)
|
||||
local x, y, w, h = child:getAbsolutes()
|
||||
child.x = x
|
||||
child.y = y
|
||||
child.w = w
|
||||
child.h = h
|
||||
|
||||
local b = child.__blur
|
||||
local pw, ph = math.max(1, math.ceil(w)), math.max(1, math.ceil(h))
|
||||
|
||||
if not b.canvas1
|
||||
or b.canvas1:getWidth() ~= pw
|
||||
or b.canvas1:getHeight() ~= ph then
|
||||
b.canvas1 = love.graphics.newCanvas(pw, ph)
|
||||
b.canvas2 = love.graphics.newCanvas(pw, ph)
|
||||
end
|
||||
|
||||
local prevCanvas = love.graphics.getCanvas()
|
||||
local prevShader = love.graphics.getShader()
|
||||
local pr, pg, pb, pa = love.graphics.getColor()
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Pass 1: draw the object AND all its descendants onto canvas1
|
||||
-- -------------------------------------------------------
|
||||
love.graphics.setCanvas(b.canvas1)
|
||||
love.graphics.setShader()
|
||||
love.graphics.clear(0, 0, 0, 0)
|
||||
love.graphics.setScissor()
|
||||
|
||||
-- Shift everything into canvas space by offsetting by -x, -y
|
||||
love.graphics.push()
|
||||
love.graphics.translate(-x, -y)
|
||||
|
||||
-- Draw the parent object itself
|
||||
draw_handler(child, nil, dt)
|
||||
|
||||
-- Draw all descendants in order
|
||||
local descendants = child:getAllChildren()
|
||||
for i = 1, #descendants do
|
||||
local desc = descendants[i]
|
||||
-- Recalculate absolutes so positions are correct
|
||||
local dx, dy, dw, dh = desc:getAbsolutes()
|
||||
desc.x = dx
|
||||
desc.y = dy
|
||||
desc.w = dw
|
||||
desc.h = dh
|
||||
draw_handler(desc, nil, dt)
|
||||
end
|
||||
|
||||
love.graphics.pop()
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Pass 2: horizontal blur canvas1 → canvas2
|
||||
-- -------------------------------------------------------
|
||||
b.shader_h:send("size", {pw, ph})
|
||||
b.shader_h:send("radius", b.radius)
|
||||
|
||||
love.graphics.setCanvas(b.canvas2)
|
||||
love.graphics.clear(0, 0, 0, 0)
|
||||
love.graphics.setShader(b.shader_h)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(b.canvas1, 0, 0)
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Pass 3: vertical blur canvas2 → screen
|
||||
-- -------------------------------------------------------
|
||||
b.shader_v:send("size", {pw, ph})
|
||||
b.shader_v:send("radius", b.radius)
|
||||
|
||||
love.graphics.setCanvas(prevCanvas)
|
||||
love.graphics.setShader(b.shader_v)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(b.canvas2, x, y)
|
||||
|
||||
-- Restore state
|
||||
love.graphics.setShader(prevShader)
|
||||
love.graphics.setColor(pr, pg, pb, pa)
|
||||
love.graphics.setScissor()
|
||||
end
|
||||
|
||||
local draw_loop = drawer:newLoop(function(self, dt)
|
||||
local children = gui:getAllChildren()
|
||||
for i = 1, #children do
|
||||
local child = children[i]
|
||||
if child.effect then
|
||||
-- Skip if this child belongs to a blur parent
|
||||
-- (blur_draw handles drawing it during the canvas capture pass)
|
||||
if has_blur_ancestor(child) then
|
||||
-- update x/y/w/h so layout still works, but don't draw
|
||||
local x, y, w, h = child:getAbsolutes()
|
||||
child.x = x
|
||||
child.y = y
|
||||
child.w = w
|
||||
child.h = h
|
||||
elseif child.__blur then
|
||||
blur_draw(child, dt)
|
||||
elseif child.effect then
|
||||
child.effect(function() draw_handler(child, nil, dt) end)
|
||||
else
|
||||
draw_handler(child,nil,dt)
|
||||
draw_handler(child, nil, dt)
|
||||
end
|
||||
end
|
||||
first_loop = true
|
||||
@@ -2256,4 +2418,10 @@ gui.Events.OnResized(function(w, h)
|
||||
end
|
||||
end)
|
||||
|
||||
-- load shaders
|
||||
files = love.filesystem.getDirectoryItems("gui/shaders")
|
||||
for i,v in pairs(files) do
|
||||
require("gui.shaders."..v:sub(1,-5)).init(gui)
|
||||
end
|
||||
|
||||
return gui
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
local blur_h = love.graphics.newShader([[
|
||||
extern vec2 size;
|
||||
extern float radius;
|
||||
|
||||
vec4 effect(vec4 color, Image tex, vec2 tc, vec2 sc) {
|
||||
vec2 step = vec2(1.0 / size.x, 0.0);
|
||||
vec4 result = vec4(0.0);
|
||||
float total = 0.0;
|
||||
float sigma = radius * 0.5;
|
||||
float r = floor(radius);
|
||||
float i = -r;
|
||||
|
||||
while (i <= r) {
|
||||
float weight = exp(-0.5 * (i * i) / (sigma * sigma));
|
||||
result += Texel(tex, tc + step * i) * weight;
|
||||
total += weight;
|
||||
i = i + 1.0;
|
||||
}
|
||||
|
||||
return (result / total) * color;
|
||||
}
|
||||
]])
|
||||
|
||||
local blur_v = love.graphics.newShader([[
|
||||
extern vec2 size;
|
||||
extern float radius;
|
||||
|
||||
vec4 effect(vec4 color, Image tex, vec2 tc, vec2 sc) {
|
||||
vec2 step = vec2(0.0, 1.0 / size.y);
|
||||
vec4 result = vec4(0.0);
|
||||
float total = 0.0;
|
||||
float sigma = radius * 0.5;
|
||||
float r = floor(radius);
|
||||
float i = -r;
|
||||
|
||||
while (i <= r) {
|
||||
float weight = exp(-0.5 * (i * i) / (sigma * sigma));
|
||||
result += Texel(tex, tc + step * i) * weight;
|
||||
total += weight;
|
||||
i = i + 1.0;
|
||||
}
|
||||
|
||||
return (result / total) * color;
|
||||
}
|
||||
]])
|
||||
|
||||
local c = {}
|
||||
|
||||
function c:setBlur(radius)
|
||||
radius = radius or 8
|
||||
c.__blur = {
|
||||
radius = radius,
|
||||
canvas1 = nil, -- populated on first draw
|
||||
canvas2 = nil,
|
||||
shader_h = blur_h,
|
||||
shader_v = blur_v,
|
||||
}
|
||||
end
|
||||
|
||||
function c:clearBlur()
|
||||
c.__blur = nil
|
||||
end
|
||||
|
||||
function c:setBlurRadius(radius)
|
||||
if c.__blur then
|
||||
c.__blur.radius = radius
|
||||
end
|
||||
end
|
||||
|
||||
return {init = function(gui) gui.registerExtension(c) end}
|
||||
Reference in New Issue
Block a user