LoveFrames/objects/text.lua
2018-04-15 02:23:26 +01:00

838 lines
20 KiB
Lua

--[[------------------------------------------------
-- Love Frames - A GUI library for LOVE --
-- Copyright (c) 2012-2014 Kenny Shields --
--]]------------------------------------------------
--[[------------------------------------------------
-- note: the text wrapping of this object is
experimental and not final
--]]------------------------------------------------
-- get the current require path
local path = string.sub(..., 1, string.len(...) - string.len(".objects.text"))
local loveframes = require(path .. ".libraries.common")
-- text object
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.lines = 0
self.formattedtext = {}
self.original = {}
self.defaultcolor = {0, 0, 0, 255/255}
self.shadowcolor = {0, 0, 0, 255/255}
self.linkcolor = {0, 102/255, 255/255, 255/255}
self.linkhovercolor = {0, 0, 255/255, 255/255}
self.ignorenewlines = false
self.shadow = false
self.linkcol = false
self.internal = false
self.linksenabled = false
self.detectlinks = false
self.OnClickLink = nil
local skin = loveframes.util.GetActiveSkin()
if not skin then
skin = loveframes.config["DEFAULTSKIN"]
end
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 linkcol = false
if hover and linksenabled and not loveframes.resizeobject 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
v.hover = true
linkcol = true
end
end
end
self.linkcol = linkcol
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
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 == 1 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 objx = self.x
local objy = 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(objx + linkx, x, objy + 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 prevcolor = defaultcolor
local prevlinkcolor = self.linkcolor
local prevlinkhovercolor = self.linkhovercolor
local prevfont = font
local link = false
local tdata
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 dtype == "table" then
if v.color then
prevcolor = v.color
end
if v.linkcolor then
prevlinkcolor = v.linkcolor
end
if v.linkhovercolor then
prevlinkhovercolor = v.linkhovercolor
end
if v.font then
prevfont = v.font
end
if v.link then
link = true
else
link = false
end
elseif dtype == "number" then
table.insert(self.formattedtext, {
font = prevfont,
color = prevcolor,
linkcolor = prevlinkcolor,
linkhovercolor = prevlinkhovercolor,
link = link,
text = tostring(v)
})
elseif dtype == "string" then
if self.ignorenewlines then
v = v:gsub("\n", " ")
end
v = v:gsub(string.char(9), " ")
v = v:gsub("\n", " \n ")
local parts = loveframes.util.SplitString(v, " ")
for i, j in ipairs(parts) do
table.insert(self.formattedtext, {
font = prevfont,
color = prevcolor,
linkcolor = prevlinkcolor,
linkhovercolor = prevlinkhovercolor,
link = link,
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, string.len(data) do
local item = data:sub(n, n)
local itemw = v.font:getWidth(item)
if n ~= string.len(data) then
if (curw + itemw) > maxw then
table.insert(inserts, {
key = key,
font = v.font,
color = v.color,
linkcolor = prevlinkcolor,
linkhovercolor = v.linkhovercolor,
link = v.link,
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,
linkcolor = prevlinkcolor,
linkhovercolor = v.linkhovercolor,
link = v.link,
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,
linkcolor = prevlinkcolor,
linkhovercolor = v.linkhovercolor,
link = v.link,
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 = 1
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 detectlinks = self.detectlinks
for k, v in ipairs(textdata) do
local text = v.text
local color = v.color
if detectlinks then
if string.len(text) > 7 and (text:sub(1, 7) == "http://" or text:sub(1, 8) == "https://") 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 = ""
lines = lines + 1
elseif (twidth + width) > maxw then
twidth = 0 + width
drawx = 0
drawy = drawy + largestheight
largestheight = 0
lines = lines + 1
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 string.byte(text) == 10 then
twidth = 0
drawx = 0
width = 0
drawy = drawy + largestheight
largestheight = 0
text = ""
lines = lines + 1
if lastwidth < textwidth then
lastwidth = textwidth
end
if largestwidth < textwidth then
largestwidth = textwidth
end
textwidth = 0
else
drawx = drawx + prevtextwidth
textwidth = textwidth + width
end
prevtextwidth = width
prevtextheight = height
v.x = drawx
v.y = drawy
end
end
end
self.lines = lines
if lastwidth == 0 then
textwidth = totalwidth
end
if textwidth < largestwidth then
textwidth = largestwidth
end
if maxw > 0 then
self.width = maxw
else
self.width = textwidth
end
self.height = drawy + prevlargestheight
return self
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 = v.linkcolor
local linkhovercolor = v.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
if link then
local linkcolor = v.linkcolor
local linkhovercolor = v.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
end
return self
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)
return self
end
--[[---------------------------------------------------------
- func: GetMaxWidth()
- desc: gets the object's maximum width
--]]---------------------------------------------------------
function newobject:GetMaxWidth()
return self.maxw
end
--[[---------------------------------------------------------
- func: SetWidth(width, relative)
- desc: sets the object's width
--]]---------------------------------------------------------
function newobject:SetWidth(width, relative)
if relative then
self:SetMaxWidth(self.parent.width * width)
else
self:SetMaxWidth(width)
end
return self
end
--[[---------------------------------------------------------
- func: SetHeight()
- desc: sets the object's height
--]]---------------------------------------------------------
function newobject:SetHeight(height)
return
end
--[[---------------------------------------------------------
- func: SetSize(width, height, relative)
- desc: sets the object's size
--]]---------------------------------------------------------
function newobject:SetSize(width, height, relative)
if relative then
self:SetMaxWidth(self.parent.width * width)
else
self:SetMaxWidth(width)
end
return self
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
return self
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
return self
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
return self
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
return self
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}
return self
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}
return self
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
return self
end
--[[---------------------------------------------------------
- func: GetLinksEnabled()
- desc: gets whether or not the object should process
urls into clickable links
--]]---------------------------------------------------------
function newobject:GetLinksEnabled()
return self.linksenabled
end
--[[---------------------------------------------------------
- func: SetDetectLinks(detect)
- desc: sets whether or not the object should detect
links when processing new text
--]]---------------------------------------------------------
function newobject:SetDetectLinks(detect)
self.detectlinks = detect
return self
end
--[[---------------------------------------------------------
- func: GetDetectLinks()
- desc: gets whether or not the object should detect
links when processing new text
--]]---------------------------------------------------------
function newobject:GetDetectLinks()
return self.detectlinks
end