clean up rendering, allow configurable themes

This commit is contained in:
airstruck
2015-10-23 00:13:58 -04:00
parent e490e2899f
commit 03f0a48ff8
6 changed files with 200 additions and 145 deletions

View File

@@ -17,8 +17,8 @@ local style = {
margin = 4, margin = 4,
}, },
toolButton_not_hovered = { toolButton_not_hovered = {
outline = false, background = false,
bend = 0, outline = { 200, 200, 200 },
}, },
statusbar = { statusbar = {
style = 'panel', style = 'panel',
@@ -28,10 +28,9 @@ local style = {
style = { 'short', 'panel' }, style = { 'short', 'panel' },
align = 'left middle', align = 'left middle',
outline = { 200, 200, 200 }, outline = { 200, 200, 200 },
height = 80, height = 120,
padding = 8, padding = 8,
background = { 255, 255, 255 }, background = { 255, 255, 255 },
bend = 0.2,
icon = 'icon/emblem-system.png', icon = 'icon/emblem-system.png',
}, },
} }
@@ -47,8 +46,12 @@ local mainForm = { title = "Test window", id = 'mainWindow', type = 'panel',
}, },
{ flow = 'x', { flow = 'x',
{ id = 'leftSideBox', width = 200, { id = 'leftSideBox', width = 200,
{ text = 'Hi, I\'m some centered text. ', style = 'listThing', { text = 'Hi, I\'m centered middle. ', style = 'listThing',
align = 'middle center' }, align = 'middle center' },
{ text = 'Hi, I\'m centered bottom. ', style = 'listThing',
align = 'bottom center' },
{ text = 'Hi, I\'m centered top. ', style = 'listThing',
align = 'top center' },
{ text = 'A man, a plan, a canal: Panama!', style = 'listThing' }, { text = 'A man, a plan, a canal: Panama!', style = 'listThing' },
}, },
{ type = 'sash', width = 4, }, { type = 'sash', width = 4, },
@@ -71,7 +74,7 @@ local mainForm = { title = "Test window", id = 'mainWindow', type = 'panel',
local layout = Layout(mainForm) local layout = Layout(mainForm)
layout:setStyle(style) layout:setStyle(style)
layout:setTheme(require 'luigi.theme.light') layout:setTheme(require 'luigi.theme.light' { highlight = { 150, 255, 150 } })
layout.leftSideBox:addChild { layout.leftSideBox:addChild {
text = 'Alright man this is a great song\nwith a really long title...', text = 'Alright man this is a great song\nwith a really long title...',

View File

@@ -19,10 +19,16 @@ function Layout:constructor (data)
end end
function Layout:setStyle (rules) function Layout:setStyle (rules)
if type(rules) == 'function' then
rules = rules()
end
self.style = Style(rules or {}, 'id', 'style') self.style = Style(rules or {}, 'id', 'style')
end end
function Layout:setTheme (rules) function Layout:setTheme (rules)
if type(rules) == 'function' then
rules = rules()
end
self.theme = Style(rules or {}, 'type') self.theme = Style(rules or {}, 'type')
end end

View File

@@ -6,12 +6,22 @@ local Font = require(ROOT .. 'font')
local Renderer = Base:extend() local Renderer = Base:extend()
local imageCache = {}
function Renderer:loadImage (path)
if not imageCache[path] then
imageCache[path] = love.graphics.newImage(path)
end
return imageCache[path]
end
function Renderer:renderBackground (widget, window) function Renderer:renderBackground (widget, window)
local bg = widget.background local bg = widget.background
if not bg then return end if not bg then return end
local bend = widget.bend local bend = widget.bend
local x1, y1, x2, y2 = widget:getRectangle(true) local x1, y1, x2, y2 = widget:getRectangle(true)
window:fill(x1, y1, x2, y2, bg, bend) window:fill(x1, y1, x2, y2, bg)
end end
function Renderer:renderOutline (widget, window) function Renderer:renderOutline (widget, window)
@@ -20,111 +30,147 @@ function Renderer:renderOutline (widget, window)
window:outline(x1, y1, x2, y2, widget.outline) window:outline(x1, y1, x2, y2, widget.outline)
end end
local imageCache = {} -- returns icon coordinates and rectangle with remaining space
function Renderer:positionIcon (widget, x1, y1, x2, y2)
local function loadImage (path) if not widget.icon then
if not imageCache[path] then return nil, nil, x1, y1, x2, y2
imageCache[path] = love.graphics.newImage(path)
end end
return imageCache[path] local icon = self:loadImage(widget.icon)
end local iconWidth, iconHeight = icon:getWidth(), icon:getHeight()
-- TODO: this function is a monster, fix it somehow
function Renderer:renderIconAndText (widget, window)
local x1, y1, x2, y2 = widget:getRectangle(true, true)
local icon = widget.icon and loadImage(widget.icon)
local align = widget.align or '' local align = widget.align or ''
local padding = widget.padding or 0 local padding = widget.padding or 0
local text = widget.text local x, y
local x, y, iconWidth, iconHeight
if icon then -- horizontal alignment
iconWidth, iconHeight = icon:getWidth(), icon:getHeight() if align:find('right') then
-- horizontal alignment x = x2 - iconWidth
if align:find('right') then x2 = x2 - iconWidth - padding
x = x2 - iconWidth elseif align:find('center') then
elseif align:find('center') then x = x1 + (x2 - x1) / 2 - iconWidth / 2
x = x1 + (x2 - x1) / 2 - iconWidth / 2 else -- if align:find('left') then
else -- if align:find('left') then x = x1
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
--[[
if text and align:find('center') then
if align:find('bottom') then
y = y - textHeight - padding
elseif align:find('middle') then
y = y - (textHeight + padding) / 2
end
end
--]]
love.graphics.draw(icon, x, y)
end end
-- render text -- vertical alignment
if not text then return end 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 if not widget.fontData then
widget.fontData = Font(widget.font, widget.fontSize, widget.textColor) widget.fontData = Font(widget.font, widget.fontSize, widget.textColor)
end end
local font = widget.fontData local font = widget.fontData
local align = widget.align or ''
if icon then local padding = widget.padding or 0
if align:find('center') then
-- y1 = y1 + iconHeight + padding
elseif align:find('right') then
x2 = x2 - iconWidth - padding
else
x1 = x1 + iconWidth + padding
end
end
font:setWidth(x2 - x1) font:setWidth(x2 - x1)
-- horizontal alignment
if align:find('right') then if align:find('right') then
font:setAlignment('right') font:setAlignment('right')
elseif align:find('center') then elseif align:find('center') then
font:setAlignment('center') font:setAlignment('center')
elseif align:find('justify') then elseif align:find('justify') then
font:setAlignment('justify') font:setAlignment('justify')
else -- if align:find('left') then else -- if align:find('left') then
font:setAlignment('left') font:setAlignment('left')
end end
local textHeight = font:getWrappedHeight(text) local y
local x, y
-- vertical alignment -- vertical alignment
if align:find('bottom') then if align:find('bottom') then
local textHeight = font:getWrappedHeight(widget.text)
y = y2 - textHeight y = y2 - textHeight
elseif align:find('middle') then elseif align:find('middle') then
local textHeight = font:getWrappedHeight(widget.text)
y = y2 - (y2 - y1) / 2 - textHeight / 2 y = y2 - (y2 - y1) / 2 - textHeight / 2
if icon and align:find('center') then
y = y1 + (iconHeight + padding) / 2
end
else -- if align:find('top') then else -- if align:find('top') then
y = y1 y = y1
if icon and align:find('center') then end
y = y1 + iconHeight + padding
return font, x1, y
end
function Renderer:renderIconAndText (widget, window)
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()
local textHeight = font:getWrappedHeight(text)
local contentHeight = textHeight + padding + iconHeight
local offset = ((y2 - y1) - contentHeight) / 2
if align:find('middle') then
iconY = y1 + offset
textY = y1 + offset + padding + iconHeight
elseif align:find('top') then
iconY = y1
textY = y1 + padding + iconHeight
else -- if align:find('bottom')
textY = y2 - textHeight
iconY = textY - padding - iconHeight
end end
end end
x = math.floor(x1) -- draw the icon
y = math.floor(y) if icon then
iconX, iconY = math.floor(iconX), math.floor(iconY)
if widget.tint then
love.graphics.setColor(widget.tint)
end
love.graphics.draw(icon, iconX, iconY)
end
window:write(x, y, x1, y1, x2, y2, text, font) -- draw the text
if text 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 end
function Renderer:renderChildren (widget) function Renderer:renderChildren (widget)

View File

@@ -1,39 +1,53 @@
local backColor = { 240, 240, 240 } return function (config)
local lineColor = { 220, 220, 220 } config = config or {}
local highlightColor = { 220, 220, 240 }
return { local backColor = config.backColor or { 240, 240, 240 }
panel = { local lineColor = config.lineColor or { 220, 220, 220 }
background = backColor, local white = config.white or { 255, 255, 255 }
padding = 4, local highlight = config.highlight or { 180, 180, 255 }
},
button = { return {
type = 'panel', panel = {
align = 'center middle', background = backColor,
outline = lineColor, padding = 4,
bend = 0.1, },
margin = 4, button = {
}, type = 'panel',
button_hovered = { align = 'center middle',
bend = 0.2, outline = lineColor,
}, bend = 0.1,
button_pressed = { margin = 4,
bend = -0.1, },
}, button_hovered = {
text = { background = white,
align = 'left middle', outline = highlight,
background = { 255, 255, 255 }, },
outline = lineColor, button_pressed = {
bend = -0.1, background = highlight,
margin = 4, outline = highlight,
padding = 4, },
}, text = {
sash = { align = 'left middle',
background = highlightColor background = { 255, 255, 255 },
}, outline = lineColor,
slider = { bend = -0.1,
type = 'panel', margin = 4,
outline = lineColor, padding = 4,
bend = 0.1, },
}, sash = {
} background = lineColor
},
sash_hovered = {
background = highlight
},
slider = {
type = 'panel',
outline = lineColor,
background = white,
},
slider_hovered = {
outline = highlight,
},
}
end

View File

@@ -20,7 +20,6 @@ function Slider:constructor(layout, data)
end) end)
self:onDisplay(function(event) self:onDisplay(function(event)
-- event:yield()
local x1, y1, x2, y2 = self:getRectangle(true, true) local x1, y1, x2, y2 = self:getRectangle(true, true)
local padding = self.padding or 0 local padding = self.padding or 0
self.layout.window:fill( self.layout.window:fill(
@@ -28,14 +27,14 @@ function Slider:constructor(layout, data)
y1 + (y2 - y1) / 2 - padding / 2, y1 + (y2 - y1) / 2 - padding / 2,
x2, x2,
y1 + (y2 - y1) / 2 + padding / 2, y1 + (y2 - y1) / 2 + padding / 2,
self.background, -(self.bend or 0) self.outline
) )
self.layout.window:fill( self.layout.window:fill(
x1 + position * (x2 - x1) - padding, x1 + position * (x2 - x1) - padding,
y1 + padding, y1 + padding,
x1 + position * (x2 - x1) + padding, x1 + position * (x2 - x1) + padding,
y2 - padding, y2 - padding,
self.background, self.bend self.background
) )
self.layout.window:outline( self.layout.window:outline(
x1 + position * (x2 - x1) - padding, x1 + position * (x2 - x1) - padding,

View File

@@ -75,41 +75,28 @@ function Window:hide ()
self:unhook() self:unhook()
end end
local function setColor (color)
love.graphics.setColor(color)
end
function Window:fill (x1, y1, x2, y2, color) function Window:fill (x1, y1, x2, y2, color)
setColor(color) love.graphics.push('all')
love.graphics.setColor(color)
love.graphics.rectangle('fill', x1, y1, x2 - x1, y2 - y1) love.graphics.rectangle('fill', x1, y1, x2 - x1, y2 - y1)
love.graphics.pop()
end end
function Window:outline (x1, y1, x2, y2, color) function Window:outline (x1, y1, x2, y2, color)
setColor(color) love.graphics.push('all')
love.graphics.setColor(color)
love.graphics.rectangle('line', x1, y1, x2 - x1, y2 - y1) love.graphics.rectangle('line', x1, y1, x2 - x1, y2 - y1)
love.graphics.pop()
end end
function Window:write (x, y, x1, y1, x2, y2, text, font) function Window:write (x, y, x1, y1, x2, y2, text, font)
local width, height = x2 - x1, y2 - y1
if width < 1 or height < 1 then
return
end
local sx, sy, sw, sh = love.graphics.getScissor()
love.graphics.setScissor(x1, y1, width, height)
local oldFont = love.graphics.getFont()
love.graphics.setFont(font.font)
setColor(font.color)
local layout = font.layout local layout = font.layout
love.graphics.printf(text, x, y, layout.width or width, layout.align)
love.graphics.setScissor(sx, sy, sw, sh) love.graphics.push('all')
love.graphics.setFont(oldFont) love.graphics.setFont(font.font)
love.graphics.setColor(font.color)
love.graphics.printf(text, x, y, layout.width or x2 - x1, layout.align)
love.graphics.pop()
end end
function Window:update (reshape) function Window:update (reshape)