LoveFrames/objects/text.lua
2013-11-09 09:30:08 -05:00

728 lines
18 KiB
Lua

--[[------------------------------------------------
-- Love Frames - A GUI library for LOVE --
-- Copyright (c) 2013 Kenny Shields --
--]]------------------------------------------------
--[[------------------------------------------------
-- note: the text wrapping of this object is
experimental and not final
--]]------------------------------------------------
-- text class
local newobject = loveframes.NewObject("text", "loveframes_object_text", true)
--[[---------------------------------------------------------
- func: initialize()
- desc: initializes the object
--]]---------------------------------------------------------
function newobject:initialize()
self.type = "text"
self.text = ""
self.font = loveframes.basicfont
self.width = 5
self.height = 5
self.maxw = 0
self.shadowxoffset = 1
self.shadowyoffset = 1
self.formattedtext = {}
self.original = {}
self.defaultcolor = {0, 0, 0, 255}
self.shadowcolor = {0, 0, 0, 255}
self.linkcolor = {0, 102, 255, 255}
self.linkhovercolor = {0, 0, 255, 255}
self.ignorenewlines = false
self.linkcursorset = false
self.shadow = false
self.internal = false
self.linksenabled = false
self.OnClickLink = nil
local skin = loveframes.util.GetActiveSkin()
local directives = skin.directives
if directives then
local text_default_color = directives.text_default_color
local text_default_shadowcolor = directives.text_default_shadowcolor
local text_default_font = directives.text_default_font
if text_default_color then
self.defaultcolor = text_default_color
end
if text_default_shadowcolor then
self.shadowcolor = text_default_shadowcolor
end
if text_default_font then
self.font = text_default_font
end
end
end
--[[---------------------------------------------------------
- func: update(deltatime)
- desc: updates the object
--]]---------------------------------------------------------
function newobject:update(dt)
local state = loveframes.state
local selfstate = self.state
if state ~= selfstate then
return
end
local visible = self.visible
local alwaysupdate = self.alwaysupdate
if not visible then
if not alwaysupdate then
return
end
end
local parent = self.parent
local base = loveframes.base
local update = self.Update
self:CheckHover()
local hover = self.hover
local linksenabled = self.linksenabled
local version = love._version
local linkcol = false
if hover and linksenabled then
local formattedtext = self.formattedtext
local x = self.x
local y = self.y
for k, v in ipairs(formattedtext) do
local link = v.link
if link then
local mx, my = love.mouse.getPosition()
local font = v.font
local linkx = v.x
local linky = v.y
local text = v.text
local twidth = font:getWidth(text)
local theight = font:getHeight()
local col = loveframes.util.BoundingBox(x + linkx, mx, y + linky, my, twidth, 1, theight, 1)
v.hover = false
if col then
local linkcursorset = self.linkcursorset
v.hover = true
if not linkcursorset and version == "0.9.0" then
local newcursor = love.mouse.getSystemCursor("hand")
love.mouse.setCursor(newcursor)
self.linkcursorset = true
end
linkcol = true
break
end
end
end
end
-- move to parent if there is a parent
if parent ~= base then
self.x = self.parent.x + self.staticx
self.y = self.parent.y + self.staticy
end
local linkcursorset = self.linkcursorset
if not linkcol and linkcursorset and version == "0.9.0" then
self.linkcursorset = false
love.mouse.setCursor()
end
if update then
update(self, dt)
end
end
--[[---------------------------------------------------------
- func: draw()
- desc: draws the object
--]]---------------------------------------------------------
function newobject:draw()
local state = loveframes.state
local selfstate = self.state
if state ~= selfstate then
return
end
if not self.visible then
return
end
local skins = loveframes.skins.available
local skinindex = loveframes.config["ACTIVESKIN"]
local defaultskin = loveframes.config["DEFAULTSKIN"]
local selfskin = self.skin
local skin = skins[selfskin] or skins[skinindex]
local drawfunc = skin.DrawText or skins[defaultskin].DrawText
local draw = self.Draw
local drawcount = loveframes.drawcount
-- set the object's draw order
self:SetDrawOrder()
if draw then
draw(self)
else
drawfunc(self)
end
self:DrawText()
end
--[[---------------------------------------------------------
- func: mousepressed(x, y, button)
- desc: called when the player presses a mouse button
--]]---------------------------------------------------------
function newobject:mousepressed(x, y, button)
local state = loveframes.state
local selfstate = self.state
if state ~= selfstate then
return
end
local visible = self.visible
if not visible then
return
end
local hover = self.hover
if hover and button == "l" then
local baseparent = self:GetBaseParent()
if baseparent and baseparent.type == "frame" then
baseparent:MakeTop()
end
local linksenabled = self.linksenabled
if linksenabled then
local formattedtext = self.formattedtext
local x = self.x
local y = self.y
for k, v in ipairs(formattedtext) do
local link = v.link
if link then
local linkx = v.x
local linky = v.y
local font = v.font
local text = v.text
local twidth = font:getWidth(text)
local theight = font:getHeight()
local col = loveframes.util.BoundingBox(x + linkx, x, y + linky, y, twidth, 1, theight, 1)
if col then
local onclicklink = self.OnClickLink
if onclicklink then
onclicklink(self, text)
end
end
end
end
end
end
end
--[[---------------------------------------------------------
- func: SetText(text)
- desc: sets the object's text
--]]---------------------------------------------------------
function newobject:SetText(t)
local dtype = type(t)
local maxw = self.maxw
local font = self.font
local defaultcolor = self.defaultcolor
local inserts = {}
local prevfont = font
local tdata, prevcolor
self.text = ""
self.formattedtext = {}
if dtype == "string" then
tdata = {t}
self.original = {t}
elseif dtype == "number" then
tdata = {tostring(t)}
self.original = {tostring(t)}
elseif dtype == "table" then
tdata = t
self.original = t
else
return
end
for k, v in ipairs(tdata) do
dtype = type(v)
if k == 1 and dtype ~= "table" then
prevcolor = defaultcolor
end
if dtype == "table" then
prevcolor = v
elseif dtype == "userdata" then
prevfont = v
elseif dtype == "number" then
table.insert(self.formattedtext, {font = prevfont, color = prevcolor, text = tostring(v)})
elseif dtype == "string" then
if self.ignorenewlines then
v = v:gsub(" \n ", " ")
v = v:gsub("\n", "")
end
v = v:gsub(string.char(9), " ")
local parts = loveframes.util.SplitString(v, " ")
for i, j in ipairs(parts) do
table.insert(self.formattedtext, {font = prevfont, color = prevcolor, text = j})
end
end
end
if maxw > 0 then
for k, v in ipairs(self.formattedtext) do
local data = v.text
local width = v.font:getWidth(data)
local curw = 0
local new = ""
local key = k
if width > maxw then
table.remove(self.formattedtext, k)
for n=1, #data do
local item = data:sub(n, n)
local itemw = v.font:getWidth(item)
if n ~= #data then
if (curw + itemw) > maxw then
table.insert(inserts, {key = key, font = v.font, color = v.color, text = new})
new = item
curw = 0 + itemw
key = key + 1
else
new = new .. item
curw = curw + itemw
end
else
new = new .. item
table.insert(inserts, {key = key, font = v.font, color = v.color, text = new})
end
end
end
end
end
for k, v in ipairs(inserts) do
table.insert(self.formattedtext, v.key, {font = v.font, color = v.color, text = v.text})
end
local textdata = self.formattedtext
local maxw = self.maxw
local font = self.font
local twidth = 0
local drawx = 0
local drawy = 0
local lines = 0
local textwidth = 0
local lastwidth = 0
local totalwidth = 0
local x = self.x
local y = self.y
local prevtextwidth = 0
local prevtextheight = 0
local prevlargestheight = 0
local largestwidth = 0
local largestheight = 0
local initialwidth = 0
local linksenabled = self.linksenabled
for k, v in ipairs(textdata) do
local text = v.text
local color = v.color
if linksenabled then
if #text > 7 and text:sub(1, 7) == "http://" then
v.link = true
end
end
if type(text) == "string" then
self.text = self.text .. text
local width = v.font:getWidth(text)
local height = v.font:getHeight("a")
if height > largestheight then
largestheight = height
prevlargestheight = height
end
totalwidth = totalwidth + width
if maxw > 0 then
if k ~= 1 then
if string.byte(text) == 10 then
twidth = 0
drawx = 0
width = 0
drawy = drawy + largestheight
largestheight = 0
text = ""
elseif (twidth + width) > maxw then
twidth = 0 + width
drawx = 0
drawy = drawy + largestheight
largestheight = 0
else
twidth = twidth + width
drawx = drawx + prevtextwidth
end
else
twidth = twidth + width
end
prevtextwidth = width
prevtextheight = height
v.x = drawx
v.y = drawy
else
if k ~= 1 then
if string.byte(text) == 10 then
twidth = 0
drawx = 0
width = 0
drawy = drawy + largestheight
largestheight = 0
text = ""
if lastwidth < textwidth then
lastwidth = textwidth
end
if largestwidth < textwidth then
largestwidth = textwidth
end
textwidth = 0
else
drawx = drawx + prevtextwidth
textwidth = textwidth + width
end
else
initialwidth = width
end
prevtextwidth = width
prevtextheight = height
v.x = drawx
v.y = drawy
end
end
end
if lastwidth == 0 then
textwidth = totalwidth
end
if textwidth < largestwidth then
textwidth = largestwidth + initialwidth
end
if maxw > 0 then
self.width = maxw
else
self.width = textwidth
end
self.height = drawy + prevlargestheight
end
--[[---------------------------------------------------------
- func: GetText()
- desc: gets the object's text
--]]---------------------------------------------------------
function newobject:GetText()
return self.text
end
--[[---------------------------------------------------------
- func: GetFormattedText()
- desc: gets the object's formatted text
--]]---------------------------------------------------------
function newobject:GetFormattedText()
return self.formattedtext
end
--[[---------------------------------------------------------
- func: DrawText()
- desc: draws the object's text
--]]---------------------------------------------------------
function newobject:DrawText()
local textdata = self.formattedtext
local x = self.x
local y = self.y
local shadow = self.shadow
local shadowxoffset = self.shadowxoffset
local shadowyoffset = self.shadowyoffset
local shadowcolor = self.shadowcolor
local inlist, list = self:IsInList()
for k, v in ipairs(textdata) do
local textx = v.x
local texty = v.y
local text = v.text
local color = v.color
local font = v.font
local link = v.link
local theight = font:getHeight("a")
if inlist then
local listy = list.y
local listhieght = list.height
if (y + texty) <= (listy + listhieght) and y + ((texty + theight)) >= listy then
love.graphics.setFont(font)
if shadow then
love.graphics.setColor(unpack(shadowcolor))
love.graphics.print(text, x + textx + shadowxoffset, y + texty + shadowyoffset)
end
if link then
local linkcolor = self.linkcolor
local linkhovercolor = self.linkhovercolor
local hover = v.hover
if hover then
love.graphics.setColor(linkhovercolor)
else
love.graphics.setColor(linkcolor)
end
else
love.graphics.setColor(unpack(color))
end
love.graphics.print(text, x + textx, y + texty)
end
else
love.graphics.setFont(font)
if shadow then
love.graphics.setColor(unpack(shadowcolor))
love.graphics.print(text, x + textx + shadowxoffset, y + texty + shadowyoffset)
end
love.graphics.setColor(unpack(color))
love.graphics.print(text, x + textx, y + texty)
end
end
end
--[[---------------------------------------------------------
- func: SetMaxWidth(width)
- desc: sets the object's maximum width
--]]---------------------------------------------------------
function newobject:SetMaxWidth(width)
local original = self.original
self.maxw = width
self:SetText(original)
end
--[[---------------------------------------------------------
- func: GetMaxWidth()
- desc: gets the object's maximum width
--]]---------------------------------------------------------
function newobject:GetMaxWidth()
return self.maxw
end
--[[---------------------------------------------------------
- func: SetWidth(width)
- desc: sets the object's width
--]]---------------------------------------------------------
function newobject:SetWidth(width)
self:SetMaxWidth(width)
end
--[[---------------------------------------------------------
- func: SetHeight()
- desc: sets the object's height
--]]---------------------------------------------------------
function newobject:SetHeight(height)
return
end
--[[---------------------------------------------------------
- func: SetSize()
- desc: sets the object's size
--]]---------------------------------------------------------
function newobject:SetSize(width, height)
self:SetMaxWidth(width)
end
--[[---------------------------------------------------------
- func: SetFont(font)
- desc: sets the object's font
- note: font argument must be a font object
--]]---------------------------------------------------------
function newobject:SetFont(font)
local original = self.original
self.font = font
if original then
self:SetText(original)
end
end
--[[---------------------------------------------------------
- func: GetFont()
- desc: gets the object's font
--]]---------------------------------------------------------
function newobject:GetFont()
return self.font
end
--[[---------------------------------------------------------
- func: GetLines()
- desc: gets the number of lines the object's text uses
--]]---------------------------------------------------------
function newobject:GetLines()
return self.lines
end
--[[---------------------------------------------------------
- func: SetIgnoreNewlines(bool)
- desc: sets whether the object should ignore \n or not
--]]---------------------------------------------------------
function newobject:SetIgnoreNewlines(bool)
self.ignorenewlines = bool
end
--[[---------------------------------------------------------
- func: GetIgnoreNewlines()
- desc: gets whether the object should ignore \n or not
--]]---------------------------------------------------------
function newobject:GetIgnoreNewlines()
return self.ignorenewlines
end
--[[---------------------------------------------------------
- func: SetShadow(bool)
- desc: sets whether or not the object should draw a
shadow behind its text
--]]---------------------------------------------------------
function newobject:SetShadow(bool)
self.shadow = bool
end
--[[---------------------------------------------------------
- func: GetShadow()
- desc: gets whether or not the object should draw a
shadow behind its text
--]]---------------------------------------------------------
function newobject:GetShadow()
return self.shadow
end
--[[---------------------------------------------------------
- func: SetShadowOffsets(offsetx, offsety)
- desc: sets the object's x and y shadow offsets
--]]---------------------------------------------------------
function newobject:SetShadowOffsets(offsetx, offsety)
self.shadowxoffset = offsetx
self.shadowyoffset = offsety
end
--[[---------------------------------------------------------
- func: GetShadowOffsets()
- desc: gets the object's x and y shadow offsets
--]]---------------------------------------------------------
function newobject:GetShadowOffsets()
return self.shadowxoffset, self.shadowyoffset
end
--[[---------------------------------------------------------
- func: SetShadowColor(r, g, b, a)
- desc: sets the object's shadow color
--]]---------------------------------------------------------
function newobject:SetShadowColor(r, g, b, a)
self.shadowcolor = {r, g, b, a}
end
--[[---------------------------------------------------------
- func: GetShadowColor()
- desc: gets the object's shadow color
--]]---------------------------------------------------------
function newobject:GetShadowColor()
return self.shadowcolor
end
--[[---------------------------------------------------------
- func: SetDefaultColor(r, g, b, a)
- desc: sets the object's default text color
--]]---------------------------------------------------------
function newobject:SetDefaultColor(r, g, b, a)
self.defaultcolor = {r, g, b, a}
end
--[[---------------------------------------------------------
- func: GetDefaultColor()
- desc: gets whether or not the object should draw a
shadow behind its text
--]]---------------------------------------------------------
function newobject:GetDefaultColor()
return self.defaultcolor
end
--[[---------------------------------------------------------
- func: SetLinksEnabled(enabled)
- desc: sets whether or not the object should process
urls into clickable links
--]]---------------------------------------------------------
function newobject:SetLinksEnabled(enabled)
self.linksenabled = enabled
end
--[[---------------------------------------------------------
- func: GetLinksEnabled()
- desc: gets whether or not the object should process
urls into clickable links
--]]---------------------------------------------------------
function newobject:GetLinksEnabled()
return self.linksenabled
end