Files
love-luigi/luigi/renderer.lua
2015-11-10 17:01:17 -05:00

268 lines
7.7 KiB
Lua

local ROOT = (...):gsub('[^.]*$', '')
local Base = require(ROOT .. 'base')
local Event = require(ROOT .. 'event')
local Font = require(ROOT .. 'font')
local Renderer = Base:extend()
local imageCache = {}
local sliceCache = {}
function Renderer:loadImage (path)
if not imageCache[path] then
imageCache[path] = love.graphics.newImage(path)
end
return imageCache[path]
end
function Renderer:loadSlices (path)
local slices = sliceCache[path]
if not slices then
slices = {}
sliceCache[path] = slices
local image = self:loadImage(path)
local iw, ih = image:getWidth(), image:getHeight()
local w, h = math.floor(iw / 3), math.floor(ih / 3)
local Quad = love.graphics.newQuad
slices.image = image
slices.width = w
slices.height = h
slices.topLeft = Quad(0, 0, w, h, iw, ih)
slices.topCenter = Quad(w, 0, w, h, iw, ih)
slices.topRight = Quad(iw - w, 0, w, h, iw, ih)
slices.middleLeft = Quad(0, h, w, h, iw, ih)
slices.middleCenter = Quad(w, h, w, h, iw, ih)
slices.middleRight = Quad(iw - w, h, w, h, iw, ih)
slices.bottomLeft = Quad(0, ih - h, w, h, iw, ih)
slices.bottomCenter = Quad(w, ih - h, w, h, iw, ih)
slices.bottomRight = Quad(iw - w, ih - h, w, h, iw, ih)
end
return slices
end
function Renderer:renderSlices (widget)
local path = widget.slices
if not path then return end
local x1, y1, x2, y2 = widget:getRectangle(true)
local slices = self:loadSlices(path)
local batch = love.graphics.newSpriteBatch(slices.image)
local xScale = ((x2 - x1) - slices.width * 2) / slices.width
local yScale = ((y2 - y1) - slices.height * 2) / slices.height
batch:add(slices.middleCenter, x1 + slices.width, y1 + slices.height, 0,
xScale, yScale)
batch:add(slices.topCenter, x1 + slices.width, y1, 0,
xScale, 1)
batch:add(slices.bottomCenter, x1 + slices.width, y2 - slices.height, 0,
xScale, 1)
batch:add(slices.middleLeft, x1, y1 + slices.height, 0,
1, yScale)
batch:add(slices.middleRight, x2 - slices.width, y1 + slices.height, 0,
1, yScale)
batch:add(slices.topLeft, x1, y1)
batch:add(slices.topRight, x2 - slices.width, y1)
batch:add(slices.bottomLeft, x1, y2 - slices.height)
batch:add(slices.bottomRight, x2 - slices.width, y2 - slices.height)
love.graphics.draw(batch)
end
function Renderer:renderBackground (widget)
if not widget.background then return end
local x1, y1, x2, y2 = widget:getRectangle(true)
love.graphics.push('all')
love.graphics.setColor(widget.background)
love.graphics.rectangle('fill', x1, y1, x2 - x1, y2 - y1)
love.graphics.pop()
end
function Renderer:renderOutline (widget)
if not widget.outline then return end
local x1, y1, x2, y2 = widget:getRectangle(true)
love.graphics.push('all')
love.graphics.setColor(widget.outline)
love.graphics.rectangle('line', x1 + 0.5, y1 + 0.5, x2 - x1, y2 - y1)
love.graphics.pop()
end
-- returns icon coordinates and rectangle with remaining space
function Renderer:positionIcon (widget, x1, y1, x2, y2)
if not widget.icon then
return nil, nil, x1, y1, x2, y2
end
local icon = self:loadImage(widget.icon)
local iconWidth, iconHeight = icon:getWidth(), icon:getHeight()
local align = widget.align or ''
local padding = widget.padding or 0
local x, y
-- horizontal alignment
if align:find('right') then
x = x2 - iconWidth
x2 = x2 - iconWidth - padding
elseif align:find('center') then
x = x1 + (x2 - x1) / 2 - iconWidth / 2
else -- if align:find('left') then
x = x1
x1 = x1 + iconWidth + padding
end
-- vertical alignment
if align:find('bottom') then
y = y2 - iconHeight
elseif align:find('middle') then
y = y1 + (y2 - y1) / 2 - iconHeight / 2
else -- if align:find('top') then
y = y1
end
return x, y, x1, y1, x2, y2
end
-- returns text coordinates
function Renderer:positionText (widget, x1, y1, x2, y2)
if not widget.text then
return nil, nil, x1, y1, x2, y2
end
if not widget.fontData then
widget.fontData = Font(widget.font, widget.fontSize, widget.textColor)
end
local font = widget.fontData
local align = widget.align or ''
local padding = widget.padding or 0
font:setWidth(x2 - x1)
-- horizontal alignment
if align:find('right') then
font:setAlignment('right')
elseif align:find('center') then
font:setAlignment('center')
elseif align:find('justify') then
font:setAlignment('justify')
else -- if align:find('left') then
font:setAlignment('left')
end
local y
-- vertical alignment
if align:find('bottom') then
local textHeight = font:getWrappedHeight(widget.text)
y = y2 - textHeight
elseif align:find('middle') then
local textHeight = font:getWrappedHeight(widget.text)
y = y2 - (y2 - y1) / 2 - textHeight / 2
else -- if align:find('top') then
y = y1
end
return font, x1, y
end
function Renderer:renderIconAndText (widget)
local x1, y1, x2, y2 = widget:getRectangle(true, true)
-- if the drawable area has no width or height, don't render
if x2 <= x1 or y2 <= y1 then
return
end
love.graphics.push('all')
love.graphics.setScissor(x1, y1, x2 - x1, y2 - y1)
local iconX, iconY, textX, textY, font
-- calculate position for icon and text based on alignment and padding
iconX, iconY, x1, y1, x2, y2 = self:positionIcon(widget, x1, y1, x2, y2)
font, textX, textY = self:positionText(widget, x1, y1, x2, y2)
local icon = widget.icon and self:loadImage(widget.icon)
local text = widget.text
local align = widget.align or ''
local padding = widget.padding or 0
-- if aligned center, icon displays above the text
-- reposition icon and text for proper vertical alignment
if icon and text and align:find('center') then
local iconHeight = icon:getHeight()
if align:find('middle') then
local textHeight = font:getWrappedHeight(text)
local contentHeight = textHeight + padding + iconHeight
local offset = ((y2 - y1) - contentHeight) / 2
iconY = y1 + offset
textY = y1 + offset + padding + iconHeight
elseif align:find('top') then
iconY = y1
textY = y1 + padding + iconHeight
else -- if align:find('bottom')
local textHeight = font:getWrappedHeight(text)
textY = y2 - textHeight
iconY = textY - padding - iconHeight
end
end
-- draw the icon
if icon then
iconX, iconY = math.floor(iconX), math.floor(iconY)
if widget.tint then
love.graphics.setBlendMode('alpha', true)
love.graphics.setColor(widget.tint)
end
love.graphics.draw(icon, iconX, iconY)
end
-- draw the text
if text and x2 > x1 then
textX, textY = math.floor(textX), math.floor(textY)
love.graphics.setFont(font.font)
love.graphics.setColor(font.color)
local layout = font.layout
love.graphics.printf(text, textX, textY, x2 - x1, layout.align)
end
love.graphics.pop()
end
function Renderer:renderChildren (widget)
for i, child in ipairs(widget) do
self:render(child)
end
end
function Renderer:render (widget)
Event.PreDisplay:emit(widget, { target = widget }, function()
self:renderBackground(widget)
self:renderOutline(widget)
self:renderSlices(widget)
self:renderIconAndText(widget)
return self:renderChildren(widget)
end)
Event.Display:emit(widget, { target = widget })
end
return Renderer