LoveFrames/objects/frame.lua
Kenny Shields 6d59ccc879 new tooltip methods and clipboard support
added tooltip method SetOffsetX(offsetx)
added tooltip method SetOffsetY(offsety)
added basic clipboard support for the textinput object (0.9.0 only)
fixed text object width issues
fixed an error caused by pressing a function key while a textinput
object was focused
fixed the tooltip object not setting the state of its text object
cleaned up the tooltip object
modified textinput:RunKey to account for changes in the 0.9.0 api
2013-09-14 11:37:01 -04:00

741 lines
18 KiB
Lua

--[[------------------------------------------------
-- Love Frames - A GUI library for LOVE --
-- Copyright (c) 2013 Kenny Shields --
--]]------------------------------------------------
-- frame class
local newobject = loveframes.NewObject("frame", "loveframes_object_frame", true)
--[[---------------------------------------------------------
- func: initialize()
- desc: initializes the object
--]]---------------------------------------------------------
function newobject:initialize()
self.type = "frame"
self.name = "Frame"
self.width = 300
self.height = 150
self.clickx = 0
self.clicky = 0
self.dockx = 0
self.docky = 0
self.dockzonesize = 10
self.internal = false
self.draggable = true
self.screenlocked = false
self.parentlocked = false
self.dragging = false
self.modal = false
self.modalbackground = false
self.showclose = true
self.dockedtop = false
self.dockedbottom = false
self.dockedleft = false
self.dockedright = false
self.topdockobject = false
self.bottomdockobject = false
self.leftdockobject = false
self.rightdockobject = false
self.dockable = false
self.internals = {}
self.children = {}
self.icon = nil
self.OnClose = nil
self.OnDock = nil
-- create docking zones
self.dockzones =
{
top = {x = 0, y = 0, width = 0, height = 0},
bottom = {x = 0, y = 0, width = 0, height = 0},
left = {x = 0, y = 0, width = 0, height = 0},
right = {x = 0, y = 0, width = 0, height = 0}
}
-- create the close button for the frame
local close = loveframes.objects["closebutton"]:new()
close.parent = self
close.OnClick = function(x, y, object)
local onclose = object.parent.OnClose
object.parent:Remove()
if onclose then
onclose(object.parent)
end
end
table.insert(self.internals, close)
end
--[[---------------------------------------------------------
- func: update(deltatime)
- desc: updates the element
--]]---------------------------------------------------------
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 mx, my = love.mouse.getPosition()
local width = self.width
local height = self.height
local showclose = self.showclose
local close = self.internals[1]
local dragging = self.dragging
local screenlocked = self.screenlocked
local parentlocked = self.parentlocked
local modal = self.modal
local base = loveframes.base
local basechildren = base.children
local numbasechildren = #basechildren
local draworder = self.draworder
local dockedtop = self.dockedtop
local dockedbottom = self.dockedbottom
local dockedleft = self.dockedleft
local dockedright = self.dockedright
local dockable = self.dockable
local dockzonesize = self.dockzonesize
local children = self.children
local internals = self.internals
local parent = self.parent
local update = self.Update
self:CheckHover()
-- update dockzones
self.dockzones.top = {x = self.x, y = self.y - dockzonesize, width = self.width, height = dockzonesize}
self.dockzones.bottom = {x = self.x, y = self.y + self.height, width = self.width, height = dockzonesize}
self.dockzones.left = {x = self.x - dockzonesize, y = self.y, width = dockzonesize, height = self.height}
self.dockzones.right = {x = self.x + self.width, y = self.y, width = dockzonesize, height = self.height}
-- dragging check
if dragging then
if parent == base then
if not dockedtop and not dockedbottom then
self.y = my - self.clicky
end
if not dockedleft and not dockedright then
self.x = mx - self.clickx
end
local basechildren = loveframes.base.children
-- check for frames to dock with
if dockable then
local ondock = self.OnDock
for k, v in ipairs(basechildren) do
if v.type == "frame" then
local topcol = loveframes.util.RectangleCollisionCheck(self.dockzones.bottom, v.dockzones.top)
local botcol = loveframes.util.RectangleCollisionCheck(self.dockzones.top, v.dockzones.bottom)
local leftcol = loveframes.util.RectangleCollisionCheck(self.dockzones.right, v.dockzones.left)
local rightcol = loveframes.util.RectangleCollisionCheck(self.dockzones.left, v.dockzones.right)
local candockobject = v.dockable
if candockobject then
if topcol and not dockedtop then
self.y = v.y - self.height
self.docky = my
self.dockedtop = true
self.topdockobject = v
if ondock then
ondock(object, v)
end
elseif botcol and not dockedbottom then
self.y = v.y + v.height
self.docky = my
self.dockedbottom = true
self.bottomdockobject = v
if ondock then
ondock(object, v)
end
elseif leftcol and not dockedleft then
self.x = v.x - self.width
self.dockx = mx
self.dockedleft = true
self.leftdockobject = v
if ondock then
ondock(object, v)
end
elseif rightcol and not dockedright then
self.x = v.x + v.width
self.dockx = mx
self.dockedright = true
self.rightdockobject = v
if ondock then
ondock(object, v)
end
end
end
end
end
end
local dockx = self.dockx
local docky = self.docky
local x = self.x
local y = self.y
-- check to see if the frame should be undocked
if dockedtop then
local topdockobject = self.topdockobject
local tdox = topdockobject.x
local tdowidth = topdockobject.width
if my > (docky + 20) or my < (docky - 20) or (x + width) < tdox or x > (tdox + tdowidth) then
self.dockedtop = false
self.docky = 0
end
end
if dockedbottom then
local bottomdockobject = self.bottomdockobject
local bdox = bottomdockobject.x
local bdowidth = bottomdockobject.width
if my > (docky + 20) or my < (docky - 20) or (x + width) < bdox or x > (bdox + bdowidth) then
self.dockedbottom = false
self.docky = 0
end
end
if dockedleft then
local leftdockobject = self.leftdockobject
local ldoy = leftdockobject.y
local ldoheight = leftdockobject.height
if mx > (dockx + 20) or mx < (dockx - 20) or (y + height) < ldoy or y > (ldoy + ldoheight) then
self.dockedleft = false
self.dockx = 0
end
end
if dockedright then
local rightdockobject = self.rightdockobject
local rdoy = rightdockobject.y
local rdoheight = rightdockobject.height
if mx > (dockx + 20) or mx < (dockx - 20) or (y + height) < rdoy or y > (rdoy + rdoheight) then
self.dockedright = false
self.dockx = 0
end
end
else
self.staticx = mx - self.clickx
self.staticy = my - self.clicky
end
end
-- if screenlocked then keep within screen
if screenlocked then
local screenwidth = love.graphics.getWidth()
local screenheight = love.graphics.getHeight()
local x = self.x
local y = self.y
if x < 0 then
self.x = 0
end
if x + width > screenwidth then
self.x = screenwidth - width
end
if y < 0 then
self.y = 0
end
if y + height > screenheight then
self.y = screenheight - height
end
end
-- keep the frame within its parent's boundaries if parentlocked
if parentlocked then
local parentwidth = self.parent.width
local parentheight = self.parent.height
local staticx = self.staticx
local staticy = self.staticy
if staticx < 0 then
self.staticx = 0
end
if staticx + width > parentwidth then
self.staticx = parentwidth - width
end
if staticy < 0 then
self.staticy = 0
end
if staticy + height > parentheight then
self.staticy = parentheight - height
end
end
if modal then
local tip = false
local key = 0
for k, v in ipairs(basechildren) do
if v.type == "tooltip" and v.show then
tip = v
key = k
end
end
if tip then
self:Remove()
self.modalbackground:Remove()
table.insert(basechildren, key - 2, self.modalbackground)
table.insert(basechildren, key - 1, self)
end
if self.modalbackground.draworder > self.draworder then
self:MakeTop()
end
end
if parent ~= base then
self.x = self.parent.x + self.staticx
self.y = self.parent.y + self.staticy
end
for k, v in ipairs(internals) do
v:update(dt)
end
for k, v in ipairs(children) do
v:update(dt)
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
local visible = self.visible
if not visible then
return
end
local children = self.children
local internals = self.internals
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.DrawFrame or skins[defaultskin].DrawFrame
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
for k, v in ipairs(internals) do
v:draw()
end
-- loop through the object's children and draw them
for k, v in ipairs(children) do
v:draw()
end
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 width = self.width
local height = self.height
local selfcol = loveframes.util.BoundingBox(x, self.x, y, self.y, 1, self.width, 1, self.height)
local internals = self.internals
local children = self.children
local dragging = self.dragging
local parent = self.parent
local base = loveframes.base
if selfcol then
local top = self:IsTopCollision()
-- initiate dragging if not currently dragging
if not dragging and top and button == "l" then
if y < self.y + 25 and self.draggable then
if parent == base then
self.clickx = x - self.x
self.clicky = y - self.y
else
self.clickx = x - self.staticx
self.clicky = y - self.staticy
end
self.dragging = true
end
end
if top and button == "l" then
self:MakeTop()
end
end
for k, v in ipairs(internals) do
v:mousepressed(x, y, button)
end
for k, v in ipairs(children) do
v:mousepressed(x, y, button)
end
end
--[[---------------------------------------------------------
- func: mousereleased(x, y, button)
- desc: called when the player releases a mouse button
--]]---------------------------------------------------------
function newobject:mousereleased(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 dragging = self.dragging
local children = self.children
local internals = self.internals
-- exit the dragging state
if dragging then
self.dragging = false
end
for k, v in ipairs(internals) do
v:mousereleased(x, y, button)
end
for k, v in ipairs(children) do
v:mousereleased(x, y, button)
end
end
--[[---------------------------------------------------------
- func: SetName(name)
- desc: sets the frame's name
--]]---------------------------------------------------------
function newobject:SetName(name)
self.name = name
end
--[[---------------------------------------------------------
- func: GetName()
- desc: gets the frame's name
--]]---------------------------------------------------------
function newobject:GetName()
return self.name
end
--[[---------------------------------------------------------
- func: SetDraggable(true/false)
- desc: sets whether the frame can be dragged or not
--]]---------------------------------------------------------
function newobject:SetDraggable(bool)
self.draggable = bool
end
--[[---------------------------------------------------------
- func: GetDraggable()
- desc: gets whether the frame can be dragged ot not
--]]---------------------------------------------------------
function newobject:GetDraggable()
return self.draggable
end
--[[---------------------------------------------------------
- func: SetScreenLocked(bool)
- desc: sets whether the frame can be moved passed the
boundaries of the window or not
--]]---------------------------------------------------------
function newobject:SetScreenLocked(bool)
self.screenlocked = bool
end
--[[---------------------------------------------------------
- func: GetScreenLocked()
- desc: gets whether the frame can be moved passed the
boundaries of window or not
--]]---------------------------------------------------------
function newobject:GetScreenLocked()
return self.screenlocked
end
--[[---------------------------------------------------------
- func: ShowCloseButton(bool)
- desc: sets whether the close button should be drawn
--]]---------------------------------------------------------
function newobject:ShowCloseButton(bool)
local close = self.internals[1]
close.visible = bool
self.showclose = bool
end
--[[---------------------------------------------------------
- func: MakeTop()
- desc: makes the object the top object in the drawing
order
--]]---------------------------------------------------------
function newobject:MakeTop()
local x, y = love.mouse.getPosition()
local key = 0
local base = loveframes.base
local basechildren = base.children
local numbasechildren = #basechildren
local parent = self.parent
-- check to see if the object's parent is not the base object
if parent ~= base then
local baseparent = self:GetBaseParent()
if baseparent.type == "frame" then
baseparent:MakeTop()
end
return
end
-- check to see if the object is the only child of the base object
if numbasechildren == 1 then
return
end
-- check to see if the object is already at the top
if basechildren[numbasechildren] == self then
return
end
-- make this the top object
for k, v in ipairs(basechildren) do
if v == self then
table.remove(basechildren, k)
table.insert(basechildren, self)
key = k
break
end
end
end
--[[---------------------------------------------------------
- func: SetModal(bool)
- desc: makes the object the top object in the drawing
order
--]]---------------------------------------------------------
function newobject:SetModal(bool)
local modalobject = loveframes.modalobject
local mbackground = self.modalbackground
local parent = self.parent
local base = loveframes.base
if parent ~= base then
return
end
self.modal = bool
if bool then
if modalobject then
modalobject:SetModal(false)
end
loveframes.modalobject = self
if not mbackground then
self.modalbackground = loveframes.objects["modalbackground"]:new(self)
self.modal = true
end
else
if modalobject == self then
loveframes.modalobject = false
if mbackground then
self.modalbackground:Remove()
self.modalbackground = false
self.modal = false
end
end
end
end
--[[---------------------------------------------------------
- func: GetModal()
- desc: gets whether or not the object is in a modal
state
--]]---------------------------------------------------------
function newobject:GetModal()
return self.modal
end
--[[---------------------------------------------------------
- func: SetVisible(bool)
- desc: set's whether the object is visible or not
--]]---------------------------------------------------------
function newobject:SetVisible(bool)
local children = self.children
local internals = self.internals
local closebutton = internals[1]
self.visible = bool
for k, v in ipairs(children) do
v:SetVisible(bool)
end
if self.showclose then
closebutton.visible = bool
end
end
--[[---------------------------------------------------------
- func: SetParentLocked(bool)
- desc: sets whether the frame can be moved passed the
boundaries of its parent or not
--]]---------------------------------------------------------
function newobject:SetParentLocked(bool)
self.parentlocked = bool
end
--[[---------------------------------------------------------
- func: GetParentLocked(bool)
- desc: gets whether the frame can be moved passed the
boundaries of its parent or not
--]]---------------------------------------------------------
function newobject:GetParentLocked()
return self.parentlocked
end
--[[---------------------------------------------------------
- func: SetIcon(icon)
- desc: sets the object's icon
--]]---------------------------------------------------------
function newobject:SetIcon(icon)
if type(icon) == "string" then
self.icon = love.graphics.newImage(icon)
else
self.icon = icon
end
end
--[[---------------------------------------------------------
- func: GetIcon()
- desc: gets the object's icon
--]]---------------------------------------------------------
function newobject:GetIcon()
local icon = self.icon
if icon then
return icon
end
return false
end
--[[---------------------------------------------------------
- func: SetDockable(dockable)
- desc: sets whether or not the object can dock onto
another from or be docked by another frame
--]]---------------------------------------------------------
function newobject:SetDockable(dockable)
self.dockable = dockable
end
--[[---------------------------------------------------------
- func: GetDockable()
- desc: gets whether or not the object can dock onto
another from or be docked by another frame
--]]---------------------------------------------------------
function newobject:GetDockable()
return self.dockable
end
--[[---------------------------------------------------------
- func: SetDockZoneSize(size)
- desc: sets the size of the object's docking zone
--]]---------------------------------------------------------
function newobject:SetDockZoneSize(size)
self.dockzonesize = size
end
--[[---------------------------------------------------------
- func: GetDockZoneSize(size)
- desc: gets the size of the object's docking zone
--]]---------------------------------------------------------
function newobject:GetDockZoneSize()
return self.dockzonesize
end