--[[------------------------------------------------ -- Love Frames - A GUI library for LOVE -- -- Copyright (c) 2013 Kenny Shields -- --]]------------------------------------------------ -- textinput object local newobject = loveframes.NewObject("textinput", "loveframes_object_textinput", true) --[[--------------------------------------------------------- - func: initialize() - desc: initializes the object --]]--------------------------------------------------------- function newobject:initialize() self.type = "textinput" self.keydown = "none" self.tabreplacement = " " self.maskchar = "*" self.font = loveframes.basicfont self.width = 200 self.height = 25 self.delay = 0 self.repeatdelay = 0.80 self.repeatrate = 0.02 self.offsetx = 0 self.offsety = 0 self.indincatortime = 0 self.indicatornum = 0 self.indicatorx = 0 self.indicatory = 0 self.textx = 0 self.texty = 0 self.textoffsetx = 5 self.textoffsety = 5 self.unicode = 0 self.limit = 0 self.line = 1 self.itemwidth = 0 self.itemheight = 0 self.extrawidth = 0 self.extraheight = 0 self.rightpadding = 0 self.bottompadding = 0 self.lastclicktime = 0 self.maxx = 0 self.buttonscrollamount = 0.10 self.mousewheelscrollamount = 5 self.usable = {} self.unusable = {} self.lines = {""} self.placeholder = "" self.internals = {} self.showindicator = true self.focus = false self.multiline = false self.vbar = false self.hbar = false self.alltextselected = false self.linenumbers = true self.linenumberspanel = false self.editable = true self.internal = false self.autoscroll = false self.masked = false self.OnEnter = nil self.OnTextChanged = nil self.OnFocusGained = nil self.OnFocusLost = nil self.OnCopy = nil self.OnPaste = nil 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 -- check to see if the object is being hovered over self:CheckHover() local time = love.timer.getTime() local keydown = self.keydown local unicode = self.unicode local parent = self.parent local base = loveframes.base local update = self.Update local font = self.font local theight = font:getHeight() local delay = self.delay local lines = self.lines local numlines = #lines local multiline = self.multiline local width = self.width local height = self.height local vbar = self.vbar local hbar = self.hbar local inputobject = loveframes.inputobject local internals = self.internals local repeatrate = self.repeatrate local hover = self.hover local version = love._version -- move to parent if there is a parent if parent ~= base then local parentx = parent.x local parenty = parent.y local staticx = self.staticx local staticy = self.staticy self.x = parentx + staticx self.y = parenty + staticy end if inputobject ~= self then self.focus = false self.alltextselected = false end -- keydown check if keydown ~= "none" then if time > delay then if (loveframes.util.IsCtrlDown()) and keydown == "v" then self:Paste() else self:RunKey(keydown, unicode, true) end self.delay = time + repeatrate end end self:PositionText() self:UpdateIndicator() -- calculations for multiline mode if multiline then local twidth = 0 local panel = self:GetLineNumbersPanel() local textoffsetx = self.textoffsetx local textoffsety = self.textoffsety local linenumbers = self.linenumbers local masked = self.masked local maskchar = self.maskchar -- get the longest line of text for k, v in ipairs(lines) do local linewidth = 0 if masked then linewidth = font:getWidth(v:gsub(".", maskchar)) else linewidth = font:getWidth(v) end if linewidth > twidth then twidth = linewidth end end -- item width calculation if vbar then self.itemwidth = twidth + 16 + textoffsetx * 2 else self.itemwidth = twidth end if panel then local panelwidth = panel.width self.itemwidth = self.itemwidth + panelwidth + textoffsetx + 5 end -- item height calculation if hbar then self.itemheight = theight * numlines + 16 + textoffsety * 2 else self.itemheight = theight * numlines end -- extra width and height calculations self.extrawidth = self.itemwidth - width self.extraheight = self.itemheight - height local itemwidth = self.itemwidth local itemheight = self.itemheight if itemheight > height then if not vbar then local scrollbody = loveframes.objects["scrollbody"]:new(self, "vertical") scrollbody.internals[1].internals[1].autoscroll = self.autoscroll table.insert(self.internals, scrollbody) self.vbar = true if hbar then local vbody = self:GetVerticalScrollBody() local vbodyheight = vbody:GetHeight() - 15 local hbody = self:GetHorizontalScrollBody() local hbodywidth = hbody:GetWidth() - 15 vbody:SetHeight(vbodyheight) hbody:SetWidth(hbodywidth) end end else if vbar then self:GetVerticalScrollBody():Remove() self.vbar = false self.offsety = 0 if self.hbar then local hbody = self:GetHorizontalScrollBody() local hbodywidth = hbody:GetWidth() - 15 hbody:SetWidth(hbodywidth) end end end if itemwidth > width then if not hbar then local scrollbody = loveframes.objects["scrollbody"]:new(self, "horizontal") scrollbody.internals[1].internals[1].autoscroll = self.autoscroll table.insert(self.internals, scrollbody) self.hbar = true if self.vbar then local vbody = self:GetVerticalScrollBody() local hbody = self:GetHorizontalScrollBody() vbody:SetHeight(vbody:GetHeight() - 15) hbody:SetWidth(hbody:GetWidth() - 15) end end else if hbar then self:GetHorizontalScrollBody():Remove() self.hbar = false self.offsetx = 0 if vbar then local vbody = self:GetVerticalScrollBody() if vbody then vbody:SetHeight(vbody:GetHeight() + 15) end end end end if linenumbers then if not self.linenumberspanel then local linenumberspanel = loveframes.objects["linenumberspanel"]:new(self) table.insert(internals, linenumberspanel) self.linenumberspanel = true end else if self.linenumberspanel then table.remove(internals, 1) self.linenumberspanel = false end end end for k, v in ipairs(internals) 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 x = self.x local y = self.y local width = self.width local height = self.height local skins = loveframes.skins.available local skinindex = loveframes.config["ACTIVESKIN"] local defaultskin = loveframes.config["DEFAULTSKIN"] local stencilfunc = function() love.graphics.rectangle("fill", x, y, width, height) end local loveversion = love._version local selfskin = self.skin local skin = skins[selfskin] or skins[skinindex] local drawfunc = skin.DrawTextInput or skins[defaultskin].DrawTextInput local drawoverfunc = skin.DrawOverTextInput or skins[defaultskin].DrawOverTextInput local draw = self.Draw local drawcount = loveframes.drawcount local internals = self.internals local vbar = self.vbar local hbar = self.hbar -- set the object's draw order self:SetDrawOrder() if vbar and hbar then stencilfunc = function() love.graphics.rectangle("fill", x, y, width - 16, height - 16) end end if loveversion == "0.8.0" then local stencil = love.graphics.newStencil(stencilfunc) love.graphics.setStencil(stencil) else love.graphics.setStencil(stencilfunc) end if draw then draw(self) else drawfunc(self) end love.graphics.setStencil() for k, v in ipairs(internals) do v:draw() end if not draw then drawoverfunc(self) 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 hover = self.hover local internals = self.internals local vbar = self.vbar local hbar = self.hbar local scrollamount = self.mousewheelscrollamount local focus = self.focus local alltextselected = self.alltextselected local onfocusgained = self.OnFocusGained local onfocuslost = self.OnFocusLost local time = love.timer.getTime() local inputobject = loveframes.inputobject if hover then if button == "l" then if inputobject ~= self then loveframes.inputobject = self end if not alltextselected then local lastclicktime = self.lastclicktime if (time > lastclicktime) and time < (lastclicktime + 0.25) then self.alltextselected = true end else self.alltextselected = false end self.focus = true self.lastclicktime = time self:GetTextCollisions(x, y) if onfocusgained and not focus then onfocusgained(self) end local baseparent = self:GetBaseParent() if baseparent and baseparent.type == "frame" then baseparent:MakeTop() end elseif button == "wu" then if vbar and not hbar then local vbar = self:GetVerticalScrollBody().internals[1].internals[1] vbar:Scroll(-scrollamount) elseif vbar and hbar then local vbar = self:GetVerticalScrollBody().internals[1].internals[1] vbar:Scroll(-scrollamount) elseif not vbar and hbar then local hbar = self:GetHorizontalScrollBody().internals[1].internals[1] hbar:Scroll(-scrollamount) end elseif button == "wd" then if vbar and not hbar then local vbar = self:GetVerticalScrollBody().internals[1].internals[1] vbar:Scroll(scrollamount) elseif vbar and hbar then local vbar = self:GetVerticalScrollBody().internals[1].internals[1] vbar:Scroll(scrollamount) elseif not vbar and hbar then local hbar = self:GetHorizontalScrollBody().internals[1].internals[1] hbar:Scroll(scrollamount) end end else if inputobject == self then loveframes.inputobject = false if onfocuslost then onfocuslost(self) end end end for k, v in ipairs(internals) 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 internals = self.internals for k, v in ipairs(internals) do v:mousereleased(x, y, button) end end --[[--------------------------------------------------------- - func: keypressed(key) - desc: called when the player presses a key --]]--------------------------------------------------------- function newobject:keypressed(key, unicode) 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 time = love.timer.getTime() local focus = self.focus local repeatdelay = self.repeatdelay local alltextselected = self.alltextselected local editable = self.editable local version = love._version self.delay = time + repeatdelay self.keydown = key if (loveframes.util.IsCtrlDown()) and focus then if key == "a" then self.alltextselected = true elseif key == "c" and alltextselected and version == "0.9.0" then local text = self:GetText() local oncopy = self.OnCopy love.system.setClipboardText(text) if oncopy then oncopy(self, text) end elseif key == "x" and alltextselected and version == "0.9.0" and editable then local text = self:GetText() local oncut = self.OnCut love.system.setClipboardText(text) if oncut then oncut(self, text) else self:SetText("") end elseif key == "v" and version == "0.9.0" and editable then self:Paste() end else local version = love._version if version == "0.8.0" then self:RunKey(key, unicode, true) else self:RunKey(key, unicode, false) end end end --[[--------------------------------------------------------- - func: keyreleased(key) - desc: called when the player releases a key --]]--------------------------------------------------------- function newobject:keyreleased(key) local state = loveframes.state local selfstate = self.state if state ~= selfstate then return end local visible = self.visible if not visible then return end self.keydown = "none" end --[[--------------------------------------------------------- - func: textinput(text) - desc: called when the inputs text --]]--------------------------------------------------------- function newobject:textinput(text) local unicode = unicode if text:find("kp.") then text = text:gsub("kp", "") end if text:len() == 1 then unicode = string.byte(text) else unicode = 0 end self:RunKey(text, unicode, true) end --[[--------------------------------------------------------- - func: RunKey(key, unicode) - desc: runs a key event on the object --]]--------------------------------------------------------- function newobject:RunKey(key, unicode, is_text) local visible = self.visible local focus = self.focus if not visible then return end if not focus then return end local x = self.x local offsetx = self.offsetx local lines = self.lines local line = self.line local numlines = #lines local curline = lines[line] local text = curline local ckey = "" local font = self.font local swidth = self.width local textoffsetx = self.textoffsetx local indicatornum = self.indicatornum local multiline = self.multiline local alltextselected = self.alltextselected local editable = self.editable local initialtext = self:GetText() local ontextchanged = self.OnTextChanged local onenter = self.OnEnter if key == "left" then indicatornum = self.indicatornum if not multiline then self:MoveIndicator(-1) local indicatorx = self.indicatorx if indicatorx <= x and indicatornum ~= 0 then local width = font:getWidth(text:sub(indicatornum, indicatornum + 1)) self.offsetx = offsetx - width elseif indicatornum == 0 and offsetx ~= 0 then self.offsetx = 0 end else if indicatornum == 0 then if line > 1 then self.line = line - 1 local numchars = #lines[self.line] self:MoveIndicator(numchars) end else self:MoveIndicator(-1) end end if alltextselected then self.line = 1 self.indicatornum = 0 self.alltextselected = false end return elseif key == "right" then indicatornum = self.indicatornum if not multiline then self:MoveIndicator(1) local indicatorx = self.indicatorx if indicatorx >= (x + swidth) and indicatornum ~= #text then local width = font:getWidth(text:sub(indicatornum, indicatornum)) self.offsetx = offsetx + width elseif indicatornum == #text and offsetx ~= ((font:getWidth(text)) - swidth + 10) and font:getWidth(text) + textoffsetx > swidth then self.offsetx = ((font:getWidth(text)) - swidth + 10) end else if indicatornum == #text then if line < numlines then self.line = line + 1 self:MoveIndicator(0, true) end else self:MoveIndicator(1) end end if alltextselected then self.line = #lines self.indicatornum = lines[#lines]:len() self.alltextselected = false end return elseif key == "up" then if multiline then if line > 1 then self.line = line - 1 if indicatornum > #lines[self.line] then self.indicatornum = #lines[self.line] end end end return elseif key == "down" then if multiline then if line < #lines then self.line = line + 1 if indicatornum > #lines[self.line] then self.indicatornum = #lines[self.line] end end end return end if not editable then return end -- key input checking system if key == "backspace" then ckey = key if alltextselected then self:Clear() self.alltextselected = false indicatornum = self.indicatornum else if text ~= "" and indicatornum ~= 0 then text = self:RemoveFromText(indicatornum) self:MoveIndicator(-1) lines[line] = text end if multiline then if line > 1 and indicatornum == 0 then local newindicatornum = 0 local oldtext = lines[line] table.remove(lines, line) self.line = line - 1 if #oldtext > 0 then newindicatornum = #lines[self.line] lines[self.line] = lines[self.line] .. oldtext self:MoveIndicator(newindicatornum) else self:MoveIndicator(#lines[self.line]) end end end local masked = self.masked local cwidth = 0 if masked then local maskchar = self.maskchar cwidth = font:getWidth(text:sub(#text):gsub(".", maskchar)) else cwidth = font:getWidth(text:sub(#text)) end if self.offsetx > 0 then self.offsetx = self.offsetx - cwidth elseif self.offsetx < 0 then self.offsetx = 0 end end elseif key == "delete" then if not editable then return end ckey = key if alltextselected then self:Clear() self.alltextselected = false indicatornum = self.indicatornum else if text ~= "" and indicatornum < #text then text = self:RemoveFromText(indicatornum + 1) lines[line] = text elseif indicatornum == #text and line < #lines then local oldtext = lines[line + 1] if #oldtext > 0 then newindicatornum = #lines[self.line] lines[self.line] = lines[self.line] .. oldtext end table.remove(lines, line + 1) end end elseif key == "return" or key == "kpenter" then ckey = key -- call onenter if it exists if onenter then onenter(self, text) end -- newline calculations for multiline mode if multiline then if alltextselected then self.alltextselected = false self:Clear() indicatornum = self.indicatornum line = self.line end local newtext = "" if indicatornum == 0 then newtext = self.lines[line] self.lines[line] = "" elseif indicatornum > 0 and indicatornum < #self.lines[line] then newtext = self.lines[line]:sub(indicatornum + 1, #self.lines[line]) self.lines[line] = self.lines[line]:sub(1, indicatornum) end if line ~= #lines then table.insert(self.lines, line + 1, newtext) self.line = line + 1 else table.insert(self.lines, newtext) self.line = line + 1 end self.indicatornum = 0 end elseif key == "tab" then ckey = key for i=1, #self.tabreplacement do local number = string.byte(self.tabreplacement:sub(i, i)) self.lines[self.line] = self:AddIntoText(number, self.indicatornum) self:MoveIndicator(1) end else if not is_text then return end self.unicode = unicode if unicode > 31 and unicode < 127 then -- do not continue if the text limit has been reached or exceeded if #text >= self.limit and self.limit ~= 0 then return end -- set the current key ckey = string.char(unicode) -- check for unusable characters if #self.usable > 0 then local found = false for k, v in ipairs(self.usable) do if v == ckey then found = true end end if not found then return end end -- check for usable characters if #self.unusable > 0 then local found = false for k, v in ipairs(self.unusable) do if v == ckey then found = true end end if found then return end end if alltextselected then self.alltextselected = false self:Clear() indicatornum = self.indicatornum text = "" lines = self.lines line = self.line end if indicatornum ~= 0 and indicatornum ~= #text then text = self:AddIntoText(unicode, indicatornum) lines[line] = text self:MoveIndicator(1) elseif indicatornum == #text then text = text .. ckey lines[line] = text self:MoveIndicator(1) elseif indicatornum == 0 then text = self:AddIntoText(unicode, indicatornum) lines[line] = text self:MoveIndicator(1) end lines = self.lines line = self.line curline = lines[line] text = curline if not multiline then local masked = self.masked local twidth = 0 local cwidth = 0 if masked then local maskchar = self.maskchar twidth = font:getWidth(text:gsub(".", maskchar)) cwidth = font:getWidth(ckey:gsub(".", maskchar)) else twidth = font:getWidth(text) cwidth = font:getWidth(ckey) end -- swidth - 1 is for the "-" character if (twidth + textoffsetx) >= (swidth - 1) then self.offsetx = self.offsetx + cwidth end end end end local curtext = self:GetText() if ontextchanged and initialtext ~= curtext then ontextchanged(self, ckey) end end --[[--------------------------------------------------------- - func: MoveIndicator(num, exact) - desc: moves the object's indicator --]]--------------------------------------------------------- function newobject:MoveIndicator(num, exact) local lines = self.lines local line = self.line local curline = lines[line] local text = curline local indicatornum = self.indicatornum if not exact then self.indicatornum = indicatornum + num else self.indicatornum = num end if self.indicatornum > #text then self.indicatornum = #text elseif self.indicatornum < 0 then self.indicatornum = 0 end self.showindicator = true self:UpdateIndicator() end --[[--------------------------------------------------------- - func: UpdateIndicator() - desc: updates the object's text insertion position indicator --]]--------------------------------------------------------- function newobject:UpdateIndicator() local time = love.timer.getTime() local indincatortime = self.indincatortime local indicatornum = self.indicatornum local lines = self.lines local line = self.line local curline = lines[line] local text = curline local font = self.font local theight = font:getHeight() local offsetx = self.offsetx local multiline = self.multiline local showindicator = self.showindicator local alltextselected = self.alltextselected local textx = self.textx local texty = self.texty local masked = self.masked if indincatortime < time then if showindicator then self.showindicator = false else self.showindicator = true end self.indincatortime = time + 0.50 end if alltextselected then self.showindicator = false end local width = 0 for i=1, indicatornum do if masked then local char = self.maskchar width = width + font:getWidth(char) else local char = text:sub(i, i) width = width + font:getWidth(char) end end if multiline then self.indicatorx = textx + width self.indicatory = texty + theight * line - theight else self.indicatorx = textx + width self.indicatory = texty end end --[[--------------------------------------------------------- - func: AddIntoText(t, p) - desc: adds text into the object's text at a given position --]]--------------------------------------------------------- function newobject:AddIntoText(t, p) local lines = self.lines local line = self.line local curline = lines[line] local text = curline local part1 = text:sub(1, p) local part2 = text:sub(p + 1) local new = part1 .. string.char(t) .. part2 return new end --[[--------------------------------------------------------- - func: RemoveFromText(p) - desc: removes text from the object's text a given position --]]--------------------------------------------------------- function newobject:RemoveFromText(p) local lines = self.lines local line = self.line local curline = lines[line] local text = curline local part1 = text:sub(1, p - 1) local part2 = text:sub(p + 1) local new = part1 .. part2 return new end --[[--------------------------------------------------------- - func: GetTextCollisions(x, y) - desc: gets text collisions with the mouse --]]--------------------------------------------------------- function newobject:GetTextCollisions(x, y) local font = self.font local lines = self.lines local numlines = #lines local line = self.line local curline = lines[line] local text = curline local xpos = 0 local line = 0 local vbar = self.vbar local hbar = self.hbar local multiline = self.multiline local selfx = self.x local selfy = self.y local selfwidth = self.width local masked = self.masked if multiline then local theight = font:getHeight() local liney = 0 local selfcol if vbar and not hbar then selfcol = loveframes.util.BoundingBox(selfx, x, selfy, y, selfwidth - 16, 1, self.height, 1) elseif hbar and not vbar then selfcol = loveframes.util.BoundingBox(selfx, x, selfy, y, selfwidth, 1, self.height - 16, 1) elseif not vbar and not hbar then selfcol = loveframes.util.BoundingBox(selfx, x, selfy, y, selfwidth, 1, self.height, 1) elseif vbar and hbar then selfcol = loveframes.util.BoundingBox(selfx, x, selfy, y, selfwidth - 16, 1, self.height - 16, 1) end if selfcol then local offsety = self.offsety local textoffsety = self.textoffsety for i=1, numlines do local linecol = loveframes.util.BoundingBox(selfx, x, (selfy - offsety) + textoffsety + (theight * i) - theight, y, self.width, 1, theight, 1) if linecol then liney = (selfy - offsety) + textoffsety + (theight * i) - theight self.line = i end end local line = self.line local curline = lines[line] for i=1, #curline do local char = text:sub(i, i) local width = 0 if masked then local maskchar = self.maskchar width = font:getWidth(maskchar) else width = font:getWidth(char) end local height = font:getHeight() local tx = self.textx + xpos local ty = self.texty local col = loveframes.util.BoundingBox(tx, x, liney, y, width, 1, height, 1) xpos = xpos + width if col then self:MoveIndicator(i - 1, true) break else self.indicatornum = #curline end if x < tx then self:MoveIndicator(0, true) end if x > (tx + width) then self:MoveIndicator(#curline, true) end end if #curline == 0 then self.indicatornum = 0 end end else for i=1, #text do local char = text:sub(i, i) local width = 0 if masked then local maskchar = self.maskchar width = font:getWidth(maskchar) else width = font:getWidth(char) end local height = font:getHeight() local tx = self.textx + xpos local ty = self.texty local col = loveframes.util.BoundingBox(tx, x, ty, y, width, 1, height, 1) xpos = xpos + width if col then self:MoveIndicator(i - 1, true) break end if x < tx then self:MoveIndicator(0, true) end if x > (tx + width) then self:MoveIndicator(#text, true) end end end end --[[--------------------------------------------------------- - func: PositionText() - desc: positions the object's text --]]--------------------------------------------------------- function newobject:PositionText() local multiline = self.multiline local x = self.x local y = self.y local offsetx = self.offsetx local offsety = self.offsety local textoffsetx = self.textoffsetx local textoffsety = self.textoffsety local linenumberspanel = self.linenumberspanel if multiline then if linenumberspanel then local panel = self:GetLineNumbersPanel() self.textx = ((x + panel.width) - offsetx) + textoffsetx self.texty = (y - offsety) + textoffsety else self.textx = (x - offsetx) + textoffsetx self.texty = (y - offsety) + textoffsety end else self.textx = (x - offsetx) + textoffsetx self.texty = (y - offsety) + textoffsety end end --[[--------------------------------------------------------- - func: SetTextOffsetX(num) - desc: sets the object's text x offset --]]--------------------------------------------------------- function newobject:SetTextOffsetX(num) self.textoffsetx = num end --[[--------------------------------------------------------- - func: SetTextOffsetY(num) - desc: sets the object's text y offset --]]--------------------------------------------------------- function newobject:SetTextOffsetY(num) self.textoffsety = num end --[[--------------------------------------------------------- - func: SetFont(font) - desc: sets the object's font --]]--------------------------------------------------------- function newobject:SetFont(font) self.font = font end --[[--------------------------------------------------------- - func: GetFont() - desc: gets the object's font --]]--------------------------------------------------------- function newobject:GetFont() return self.font end --[[--------------------------------------------------------- - func: SetFocus(focus) - desc: sets the object's focus --]]--------------------------------------------------------- function newobject:SetFocus(focus) local inputobject = loveframes.inputobject local onfocusgained = self.OnFocusGained local onfocuslost = self.OnFocusLost self.focus = focus if focus then loveframes.inputobject = self if onfocusgained then onfocusgained(self) end else if inputobject == self then loveframes.inputobject = false end if onfocuslost then onfocuslost(self) end end end --[[--------------------------------------------------------- - func: GetFocus() - desc: gets the object's focus --]]--------------------------------------------------------- function newobject:GetFocus() return self.focus end --[[--------------------------------------------------------- - func: GetIndicatorVisibility() - desc: gets the object's indicator visibility --]]--------------------------------------------------------- function newobject:GetIndicatorVisibility() return self.showindicator end --[[--------------------------------------------------------- - func: SetLimit(limit) - desc: sets the object's text limit --]]--------------------------------------------------------- function newobject:SetLimit(limit) self.limit = limit end --[[--------------------------------------------------------- - func: SetUsable(usable) - desc: sets what characters can be used for the object's text --]]--------------------------------------------------------- function newobject:SetUsable(usable) self.usable = usable end --[[--------------------------------------------------------- - func: GetUsable() - desc: gets what characters can be used for the object's text --]]--------------------------------------------------------- function newobject:GetUsable() return self.usable end --[[--------------------------------------------------------- - func: SetUnusable(unusable) - desc: sets what characters can not be used for the object's text --]]--------------------------------------------------------- function newobject:SetUnusable(unusable) self.unusable = unusable end --[[--------------------------------------------------------- - func: GetUnusable() - desc: gets what characters can not be used for the object's text --]]--------------------------------------------------------- function newobject:GetUnusable() return self.unusable end --[[--------------------------------------------------------- - func: Clear() - desc: clears the object's text --]]--------------------------------------------------------- function newobject:Clear() self.lines = {""} self.line = 1 self.offsetx = 0 self.offsety = 0 self.indicatornum = 0 end --[[--------------------------------------------------------- - func: SetText(text) - desc: sets the object's text --]]--------------------------------------------------------- function newobject:SetText(text) local tabreplacement = self.tabreplacement local multiline = self.multiline text = tostring(text) text = text:gsub(string.char(9), tabreplacement) text = text:gsub(string.char(13), "") if multiline then text = text:gsub(string.char(92) .. string.char(110), string.char(10)) local t = loveframes.util.SplitString(text, string.char(10)) if #t > 0 then self.lines = t else self.lines = {""} end self.line = #self.lines self.indicatornum = #self.lines[#self.lines] else text = text:gsub(string.char(92) .. string.char(110), "") text = text:gsub(string.char(10), "") self.lines = {text} self.line = 1 self.indicatornum = #text end end --[[--------------------------------------------------------- - func: GetText() - desc: gets the object's text --]]--------------------------------------------------------- function newobject:GetText() local multiline = self.multiline local lines = self.lines local text = "" if multiline then for k, v in ipairs(lines) do text = text .. v if k ~= #lines then text = text .. "\n" end end else text = lines[1] end return text end --[[--------------------------------------------------------- - func: SetMultiline(bool) - desc: enables or disables allowing multiple lines for text entry --]]--------------------------------------------------------- function newobject:SetMultiline(bool) local text = "" local lines = self.lines self.multiline = bool if bool then self:Clear() else for k, v in ipairs(lines) do text = text .. v end self:SetText(text) self.internals = {} self.vbar = false self.hbar = false self.linenumberspanel = false end end --[[--------------------------------------------------------- - func: GetMultiLine() - desc: gets whether or not the object is using multiple lines --]]--------------------------------------------------------- function newobject:GetMultiLine() return self.multiline end --[[--------------------------------------------------------- - func: GetVerticalScrollBody() - desc: gets the object's vertical scroll body --]]--------------------------------------------------------- function newobject:GetVerticalScrollBody() local vbar = self.vbar local internals = self.internals local item = false if vbar then for k, v in ipairs(internals) do if v.type == "scrollbody" and v.bartype == "vertical" then item = v end end end return item end --[[--------------------------------------------------------- - func: GetHorizontalScrollBody() - desc: gets the object's horizontal scroll body --]]--------------------------------------------------------- function newobject:GetHorizontalScrollBody() local hbar = self.hbar local internals = self.internals local item = false if hbar then for k, v in ipairs(internals) do if v.type == "scrollbody" and v.bartype == "horizontal" then item = v end end end return item end --[[--------------------------------------------------------- - func: HasVerticalScrollBar() - desc: gets whether or not the object has a vertical scroll bar --]]--------------------------------------------------------- function newobject:HasVerticalScrollBar() return self.vbar end --[[--------------------------------------------------------- - func: HasHorizontalScrollBar() - desc: gets whether or not the object has a horizontal scroll bar --]]--------------------------------------------------------- function newobject:HasHorizontalScrollBar() return self.hbar end --[[--------------------------------------------------------- - func: GetLineNumbersPanel() - desc: gets the object's line numbers panel --]]--------------------------------------------------------- function newobject:GetLineNumbersPanel() local panel = self.linenumberspanel local internals = self.internals local item = false if panel then for k, v in ipairs(internals) do if v.type == "linenumberspanel" then item = v end end end return item end --[[--------------------------------------------------------- - func: ShowLineNumbers(bool) - desc: sets whether or not to show line numbers when using multiple lines --]]--------------------------------------------------------- function newobject:ShowLineNumbers(bool) local multiline = self.multiline if multiline then self.linenumbers = bool end end --[[--------------------------------------------------------- - func: GetTextX() - desc: gets the object's text x --]]--------------------------------------------------------- function newobject:GetTextX() return self.textx end --[[--------------------------------------------------------- - func: GetTextY() - desc: gets the object's text y --]]--------------------------------------------------------- function newobject:GetTextY() return self.texty end --[[--------------------------------------------------------- - func: IsAllTextSelected() - desc: gets whether or not all of the object's text is selected --]]--------------------------------------------------------- function newobject:IsAllTextSelected() return self.alltextselected end --[[--------------------------------------------------------- - func: GetLines() - desc: gets the object's lines --]]--------------------------------------------------------- function newobject:GetLines() return self.lines end --[[--------------------------------------------------------- - func: GetOffsetX() - desc: gets the object's x offset --]]--------------------------------------------------------- function newobject:GetOffsetX() return self.offsetx end --[[--------------------------------------------------------- - func: GetOffsetY() - desc: gets the object's y offset --]]--------------------------------------------------------- function newobject:GetOffsetY() return self.offsety end --[[--------------------------------------------------------- - func: GetIndicatorX() - desc: gets the object's indicator's xpos --]]--------------------------------------------------------- function newobject:GetIndicatorX() return self.indicatorx end --[[--------------------------------------------------------- - func: GetIndicatorY() - desc: gets the object's indicator's ypos --]]--------------------------------------------------------- function newobject:GetIndicatorY() return self.indicatory end --[[--------------------------------------------------------- - func: GetLineNumbersEnabled() - desc: gets whether line numbers are enabled on the object or not --]]--------------------------------------------------------- function newobject:GetLineNumbersEnabled() return self.linenumbers end --[[--------------------------------------------------------- - func: GetItemWidth() - desc: gets the object's item width --]]--------------------------------------------------------- function newobject:GetItemWidth() return self.itemwidth end --[[--------------------------------------------------------- - func: GetItemHeight() - desc: gets the object's item height --]]--------------------------------------------------------- function newobject:GetItemHeight() return self.itemheight end --[[--------------------------------------------------------- - func: SetTabReplacement(tabreplacement) - desc: sets a string to replace tabs with --]]--------------------------------------------------------- function newobject:SetTabReplacement(tabreplacement) self.tabreplacement = tabreplacement end --[[--------------------------------------------------------- - func: GetTabReplacement() - desc: gets the object's tab replacement --]]--------------------------------------------------------- function newobject:GetTabReplacement() return self.tabreplacement end --[[--------------------------------------------------------- - func: SetEditable(bool) - desc: sets whether or not the user can edit the object's text --]]--------------------------------------------------------- function newobject:SetEditable(bool) self.editable = bool end --[[--------------------------------------------------------- - func: GetEditable - desc: gets whether or not the user can edit the object's text --]]--------------------------------------------------------- function newobject:GetEditable() return self.editable end --[[--------------------------------------------------------- - func: SetButtonScrollAmount(speed) - desc: sets the scroll amount of the object's scrollbar buttons --]]--------------------------------------------------------- function newobject:SetButtonScrollAmount(amount) self.buttonscrollamount = amount end --[[--------------------------------------------------------- - func: GetButtonScrollAmount() - desc: gets the scroll amount of the object's scrollbar buttons --]]--------------------------------------------------------- function newobject:GetButtonScrollAmount() return self.buttonscrollamount end --[[--------------------------------------------------------- - func: SetMouseWheelScrollAmount(amount) - desc: sets the scroll amount of the mouse wheel --]]--------------------------------------------------------- function newobject:SetMouseWheelScrollAmount(amount) self.mousewheelscrollamount = amount end --[[--------------------------------------------------------- - func: GetMouseWheelScrollAmount() - desc: gets the scroll amount of the mouse wheel --]]--------------------------------------------------------- function newobject:GetButtonScrollAmount() return self.mousewheelscrollamount end --[[--------------------------------------------------------- - func: SetAutoScroll(bool) - desc: sets whether or not the object should autoscroll when in multiline mode --]]--------------------------------------------------------- function newobject:SetAutoScroll(bool) local internals = self.internals self.autoscroll = bool if internals[2] then internals[2].internals[1].internals[1].autoscroll = bool end end --[[--------------------------------------------------------- - func: GetAutoScroll() - desc: gets whether or not the object should autoscroll when in multiline mode --]]--------------------------------------------------------- function newobject:GetAutoScroll() return self.autoscroll end --[[--------------------------------------------------------- - func: SetRepeatDelay(delay) - desc: sets the object's repeat delay --]]--------------------------------------------------------- function newobject:SetRepeatDelay(delay) self.repeatdelay = delay end --[[--------------------------------------------------------- - func: GetRepeatDelay() - desc: gets the object's repeat delay --]]--------------------------------------------------------- function newobject:GetRepeatDelay() return self.repeatdelay end --[[--------------------------------------------------------- - func: SetRepeatRate(rate) - desc: sets the object's repeat rate --]]--------------------------------------------------------- function newobject:SetRepeatRate(rate) self.repeatrate = rate end --[[--------------------------------------------------------- - func: GetRepeatRate() - desc: gets the object's repeat rate --]]--------------------------------------------------------- function newobject:GetRepeatRate() return self.repeatrate end --[[--------------------------------------------------------- - func: SetValue(value) - desc: sets the object's value (alias of SetText) --]]--------------------------------------------------------- function newobject:SetValue(value) self:SetText(value) end --[[--------------------------------------------------------- - func: GetValue() - desc: gets the object's value (alias of GetText) --]]--------------------------------------------------------- function newobject:GetValue() return self:GetText() end --[[--------------------------------------------------------- - func: SetVisible(bool) - desc: sets the object's visibility --]]--------------------------------------------------------- function newobject:SetVisible(bool) self.visible = bool if not bool then self.keydown = "none" end end --[[--------------------------------------------------------- - func: Copy() - desc: copies the object's text to the user's clipboard --]]--------------------------------------------------------- function newobject:Copy() local version = love._version if version == "0.9.0" then local text = self:GetText() love.system.setClipboardText(text) end end --[[--------------------------------------------------------- - func: Paste() - desc: pastes the current contents of the clipboard into the object's text --]]--------------------------------------------------------- function newobject:Paste() local text = love.system.getClipboardText() local usable = self.usable local unusable = self.unusable local limit = self.limit local alltextselected = self.alltextselected local onpaste = self.OnPaste local ontextchanged = self.OnTextChanged if limit > 0 then local curtext = self:GetText() local curlength = curtext:len() if curlength == limit then return else local inputlimit = limit - curlength if text:len() > inputlimit then text = text:sub(1, inputlimit) end end end local charcheck = function(a) if #usable > 0 then if not loveframes.util.TableHasValue(usable, a) then return "" end elseif #unusable > 0 then if loveframes.util.TableHasValue(unusable, a) then return "" end end end if #usable > 0 or #unusable > 0 then text = text:gsub(".", charcheck) end if alltextselected then self:SetText(text) self.alltextselected = false if ontextchanged then ontextchanged(self, text) end else local tabreplacement = self.tabreplacement local indicatornum = self.indicatornum local lines = self.lines local multiline = self.multiline if multiline then local parts = loveframes.util.SplitString(text, string.char(10)) local numparts = #parts local oldlinedata = {} local line = self.line local first = lines[line]:sub(0, indicatornum) local last = lines[line]:sub(indicatornum + 1) if numparts > 1 then for i=1, numparts do local part = parts[i]:gsub(string.char(13), "") part = part:gsub(string.char(9), " ") if i ~= 1 then table.insert(oldlinedata, lines[line]) lines[line] = part if i == numparts then self.indicatornum = part:len() lines[line] = lines[line] .. last self.line = line end else lines[line] = first .. part end line = line + 1 end for i=1, #oldlinedata do lines[line] = oldlinedata[i] line = line + 1 end if ontextchanged then ontextchanged(self, text) end elseif numparts == 1 then text = text:gsub(string.char(10), " ") text = text:gsub(string.char(13), " ") text = text:gsub(string.char(9), tabreplacement) local length = text:len() local new = first .. text .. last lines[line] = new self.indicatornum = indicatornum + length if ontextchanged then ontextchanged(self, text) end end else text = text:gsub(string.char(10), " ") text = text:gsub(string.char(13), " ") text = text:gsub(string.char(9), tabreplacement) local length = text:len() local linetext = lines[1] local part1 = linetext:sub(1, indicatornum) local part2 = linetext:sub(indicatornum + 1) local new = part1 .. text .. part2 lines[1] = new self.indicatornum = indicatornum + length if ontextchanged then ontextchanged(self, text) end end end if onpaste then onpaste(self, text) end end --[[--------------------------------------------------------- - func: SelectAll() - desc: selects all of the object's text --]]--------------------------------------------------------- function newobject:SelectAll() self.alltextselected = true end --[[--------------------------------------------------------- - func: DeselectAll() - desc: deselects all of the object's text --]]--------------------------------------------------------- function newobject:DeselectAll() self.alltextselected = false end --[[--------------------------------------------------------- - func: SetMasked(masked) - desc: sets whether or not the object is masked --]]--------------------------------------------------------- function newobject:SetMasked(masked) self.masked = masked end --[[--------------------------------------------------------- - func: GetMasked() - desc: gets whether or not the object is masked --]]--------------------------------------------------------- function newobject:GetMasked() return self.masked end --[[--------------------------------------------------------- - func: SetMaskChar(char) - desc: sets the object's mask character --]]--------------------------------------------------------- function newobject:SetMaskChar(char) self.maskchar = char end --[[--------------------------------------------------------- - func: GetMaskChar() - desc: gets the object's mask character --]]--------------------------------------------------------- function newobject:GetMaskChar() return self.maskchar end --[[--------------------------------------------------------- - func: SetPlaceholderText(text) - desc: sets the object's placeholder text --]]--------------------------------------------------------- function newobject:SetPlaceholderText(text) self.placeholder = text end --[[--------------------------------------------------------- - func: GetPlaceholderText() - desc: gets the object's placeholder text --]]--------------------------------------------------------- function newobject:GetPlaceholderText() return self.placeholder end