diff --git a/clock.lua b/clock.lua index f6a2ef2..631601f 100644 --- a/clock.lua +++ b/clock.lua @@ -1,41 +1,6 @@ -local MAX_MINUTES = 60 * 12 - -local Time = {} -Time.__index = Time - -local function newTimeRaw(rawMinutes) - local t = { - rawminutes = rawMinutes % MAX_MINUTES - } - return setmetatable(t, Time) -end - -function Time:new(hour, minute) - return newTimeRaw((hour or 12) % 12 * 60 + (minute or 0)) -end - -function Time:get() - local h = math.floor(self.rawminutes / 60 % 12) - if h == 0 then h = 12 end - local m = self.rawminutes % 60 - return h, m -end - -function Time:getHoursSinceTwelve() - local h = self.rawminutes / 60 - if h < 1 then h = h + 12 end - return h -end - -function Time:addMinutes(minutes) - return newTimeRaw(self.rawminutes + minutes) -end - -function Time:fmt() - local h, m = self:get() - return string.format("%2d:%02d", h, m) -end +local Time = require"time" +local numberFont = love.graphics.newFont(100) local function pointToTurns(x, y) local rawTurns = math.atan2(y, x) / 2 / math.pi @@ -68,17 +33,58 @@ function Hand:draw(turns) end +local function drawFaceCanvas(r) + local oldCanvas = love.graphics.getCanvas() + local canvas = love.graphics.newCanvas(2*r,2*r) + love.graphics.setCanvas(canvas) + love.graphics.push("all") + love.graphics.translate(r, r) + love.graphics.setColor(0.93, 0.8, 0) + love.graphics.circle("fill", 0, 0, r) + love.graphics.setColor(1, 1, 1) + love.graphics.circle("fill", 0, 0, 0.84*r) + love.graphics.setColor(0,0,0) + for i=1,60 do + love.graphics.rotate(math.pi/30) + love.graphics.circle("fill", 0.6*r, 0, 0.01*r) + end + love.graphics.setColor(0.87,0,0) + for i=1,12 do + love.graphics.rotate(math.pi/6) + love.graphics.circle("fill", 0.6*r, 0, 0.015*r) + end + + local font = love.graphics.newFont("font/JosefinSans-Regular.ttf", r/6) + love.graphics.setColor(0,0,0) + for i=1,12 do + local s = tostring(i) + local h = font:getHeight() + local w = font:getWidth(s) + local tr = love.math.newTransform(0,0,-math.pi/6*i+math.pi) + tr = tr:apply(love.math.newTransform(w*0.5, h*0.4)) + local x, y = tr:inverseTransformPoint(0, 0.72*r) + love.graphics.printf(s, font, x, y, r) + end + love.graphics.pop() + love.graphics.setCanvas(oldCanvas) + return canvas +end + local Clock = {} Clock.__index = Clock -function Clock:new(time, diameter, transform) +function Clock:new(time, diameter) + local radius = diameter and diameter/2 or 300 + local transform = love.math.newTransform(radius, radius) local c = { time = (time or Time:new()), minuteHand = Hand:new(240, {1,0,0}), hourHand = Hand:new(180, {0,0.5,0.5}), dragging = false, - transform = transform or love.math.newTransform(), - radius = diameter and diameter/2 or 300 + radius = radius, + onSetHands = nil, + transform = transform, + faceCanvas = drawFaceCanvas(radius) } return setmetatable(c, self) end @@ -94,11 +100,9 @@ end function Clock:draw() local _, m = self.time:get() local h = self.time:getHoursSinceTwelve() - love.graphics.push("all") + love.graphics.draw(self.faceCanvas) love.graphics.applyTransform(self.transform) - love.graphics.setColor(0.5, 0.5, 0.5) - love.graphics.circle("fill", 0, 0, self.radius) self.minuteHand:draw(m / 60) self.hourHand:draw(h / 12) love.graphics.pop() @@ -122,13 +126,13 @@ local function clockPointAt(clock, x, y) dMinutes = dMinutes - 60 end clock.time = clock.time:addMinutes(dMinutes) + if clock.onSetHands then clock.onSetHands(clock.time) end end function Clock:movemouse(x, y) if self.dragging then local localX, localY = self.transform:inverseTransformPoint(x, y) clockPointAt(self, x, y) - -- TODO update clock hands end end @@ -136,7 +140,6 @@ function Clock:pressmouse(x, y, button) if not self.dragging and button == 1 and clockContainsPoint(self, x,y) then self.dragging = true clockPointAt(self, x, y) - -- TODO update clock hands end end diff --git a/conf.lua b/conf.lua index 9fda08f..b7a8741 100644 --- a/conf.lua +++ b/conf.lua @@ -1,3 +1,4 @@ function love.conf(t) t.window.msaa = 3 + t.window.fullscreen = true end \ No newline at end of file diff --git a/font/JosefinSans-Regular.ttf b/font/JosefinSans-Regular.ttf new file mode 100644 index 0000000..c116243 Binary files /dev/null and b/font/JosefinSans-Regular.ttf differ diff --git a/font/OFL.txt b/font/OFL.txt new file mode 100644 index 0000000..8931ce9 --- /dev/null +++ b/font/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name "Josefin Sans". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/loveframes/changelog.txt b/loveframes/changelog.txt new file mode 100644 index 0000000..437957f --- /dev/null +++ b/loveframes/changelog.txt @@ -0,0 +1,954 @@ +================================================ +Version 11.3 - Alpha (Apr 28 - 2020) +================================================ +[CHANGED] String functions len, sub, gsub, find replaced with utf8 versions +[ADDED] New properties: skin.controls.smallfont and skin.controls.imagebuttonfont + +================================================ +Version 11.2 - Alpha (Mar 6 - 2019) +================================================ +[ADDED] New functions button.SetImage and button.GetImage +[ADDED] LoveFrames Demo +[ADDED] New Default skin with subskins + +[FIXED] Colors as numbers + +[CHANGED] Massive internal update and cleaning by ingsoc451 (https://love2d.org/forums/memberlist.php?mode=viewprofile&u=137519) +[CHANGED] Require system +[CHANGED] loveframes.util and loveframes.templates functions moved to loveframes +[CHANGED] utf8 moved to third-party + +================================================ +Version 11.0 - Alpha (Apr 15 - 2018) +================================================ +[FIXED] updated to LÖVE 11.0 + +================================================ +Version 0.10.1 - Alpha (Nov 13 - 2016) +================================================ +[ADDED] a new property: image.stretch (boolean) +[ADDED] a new property: button.image (image) -- left aligned image +[ADDED] new properties: button.groupIndex (int) and button.checked (boolean) +[ADDED] new properties: imagebutton.groupIndex (int) and imagebutton.checked (boolean) +[ADDED] a new property: checkbox.groupIndex +[ADDED] a new function: wheelmoved + +[FIXED] textinput - Unicode support +[FIXED] Update for LÖVE 0.10.1 +[FIXED] Update stencil calls + +================================================ +Version 0.10 - Alpha (Release Date TBD) +================================================ +[ADDED] a new object: tree +[ADDED] a new object: radiobutton +[ADDED] a new button method: GetDown() +[ADDED] support for relative object sizing +[ADDED] a new columnlist method: GetCellText(rowid, columnid) +[ADDED] a new button method: SetToggleable(bool) +[ADDED] a new button method: GetToggleable() +[ADDED] toggle functionality to the button object +[ADDED] resizing functinality to the columnlistheader object +[ADDED] horizontal scrolling to the columnlist object +[ADDED] a new columnlistarea method: GetVerticalScrollBody() +[ADDED] a new columnlistarea method: GetHorizontalScrollBody() +[ADDED] a new columnlist method: GetTotalColumnWidth() +[ADDED] a new columnlist method: SetColumnWidth(id, width) +[ADDED] a new columnlist method: GetColumnWidth(id) +[ADDED] a new columnlist method: ResizeColumns() +[ADDED] a new columnlist method: SetDefaultColumnWidth(width) +[ADDED] a new columnlist method: GetDefaultColumnWidth() +[ADDED] a new columnlist method: SetColumnResizeEnabled(bool) +[ADDED] a new columnlist method: GetColumnResizeEnabled() +[ADDED] a new columnlist method: SizeColumnToData() +[ADDED] a new columnlist method: SetColumnOrder(curid, newid) +[ADDED] a new columnlistheader method: SetName(name) +[ADDED] a new scrollbody method: GetScrollBar() +[ADDED] a new scrollbar method: ScrollTo(percent) +[ADDED] a new frame method: SetMaxSize(width, height) +[ADDED] a new frame method: GetMaxSize() +[ADDED] a new frame method: SetMinSize(width, height) +[ADDED] a new frame method: GetMinSize() +[ADDED] a new textinput method: ClearLine(line) +[ADDED] a new textinput method: SetTrackingEnabled(bool) +[ADDED] a new textinput method: GetTrackingEnabled() +[ADDED] a new list method: GetAutoScroll() + +[FIXED] bug that would cause tabbuttons to be positioned incorrectly when scrolling with the mouse wheel +[FIXED] collision detection issue caused by list child objects not being updated when outside of their parent's bounding box +[FIXED] checkbox text becoming invisible when calling checkbox:SetText after switching to a custom state +[FIXED] an error caused by calling base:MoveToTop on an object parented to a panel object +[FIXED] nested list positioning issues +[FIXED] textinput.alltextselected being set to true when calling the SelectAll method on an empty single-line textinput +[FIXED] a bug that allowed frames to be resized when they where not being hovered +[FIXED] text:GetLines always returning nil +[FIXED] creating a newline in a multiline textinput with the return key would not reset the object's xoffset +[FIXED] the last character of a long line in a multiline textinput not being shown due to a text offset calculation issue +[FIXED] scrollbody now resizes to parent +[FIXED] textinput indicator now always visible +[FIXED] columnlist rows can have non-drawable extra info ("Blue" skin doesn't crash on it) + +[CHANGED] columnlist row colors are now adjusted when a row is removed from the list +[CHANGED] columnlistarea.rowcolorindex now resets to 1 when the list is cleared +[CHANGED] columnlist:SetRowColumnText to columnlist:SetCellText +[CHANGED] columnlist:AdjustColumns to columnlist:PositionColumns +[CHANGED] license to zlib/libpng +[CHANGED] the loveframes table is now returned by init.lua +[CHANGED] template files are now expected to return a template table instead of calling loveframes.templates.Register +[CHANGED] textinput tries to scroll horizontally only if alt is pressed +[CHANGED] project-wide utf8 support +[CHANGED] the textinput's indicator is now always visible when being moved + +================================================ +Version 0.9.8.1 - Alpha (May 17 - 2014) +================================================ +[ADDED] ability to specify a custom skin directory in a skin's lua file + +[FIXED] frame:MakeTop not returning self in certain situations +[FIXED] ability to insert tabs into a textinput when all of the object's text was selected +[FIXED] ability to cause a single-line textinput to enter all text selected mode when no text had been entered + +[CHANGED] returning false in a frame.OnClose event callback will prevent the frame from being removed +[CHANGED] tabs:AddTab now returns the tabbutton object associated with the new tab + +================================================ +Version 0.9.8 - Alpha (April 3 - 2014) +================================================ +[ADDED] support for method chaining +[ADDED] a new tooltip method: GetPadding() +[ADDED] a new frame method: SetAlwaysOnTop(bool) +[ADDED] a new frame method: GetAlwaysOnTop() + +[FIXED] text object link detection issues +[FIXED] loveframes.util.GetDirectoryContents returning incorrect require paths + +[CHANGED] the text object will now detect newlines that are not separated by spaces + +[REMOVED] support for LOVE 0.8.0 + +================================================ +Version 0.9.7.2 - Alpha (January 29 - 2014) +================================================ +[ADDED] a new closebutton method: SetAutoPosition(bool) +[ADDED] a new closebutton method: GetAutoPosition() +[ADDED] checkbox skin directives + +[FIXED] ability to use the tab button in numberbox textinputs +[FIXED] text width being calculated incorrectly when max width was set to 0 +[FIXED] base menus can no longer extend beyond the boundaries of the application window + +[CHANGED] minor performance improvements for the following objects: list, columnlistarea, tabs +[CHANGED] closebuttons are now automatically positioned by default +[CHANGED] optimized loveframes.util.TableHasKey (thanks CyaNox) + +================================================ +Version 0.9.7.1 - Alpha (January 15 - 2014) +================================================ +[ADDED] a new tooltip method: GetOffsetX() +[ADDED] a new tooltip method: GetOffsetY() +[ADDED] a new tooltip method: GetOffsets() +[ADDED] a new tooltip method: GetFollowCursor() +[ADDED] a new tooltip method: GetObject() +[ADDED] a new tooltip method: GetFont() +[ADDED] a new tooltip method: GetFollowObject() + +[CHANGED] Moved library files to /libraries +[CHANGED] Upgraded to middleclass 3.0.1 + +================================================ +Version 0.9.7 - Alpha (January 12 - 2014) +================================================ +[ADDED] a new base method: textinput(text) +[ADDED] a new frame method: SetResizable(bool) +[ADDED] a new frame method: GetResizable() +[ADDED] a new frame method: SetMinWidth(width) +[ADDED] a new frame method: GetMinWidth() +[ADDED] a new frame method: SetMaxWidth(width) +[ADDED] a new frame method: GetMaxWidth() +[ADDED] a new frame method: SetMinHeight(height) +[ADDED] a new frame method: GetMinHeight() +[ADDED] a new frame method: SetMaxHeight(height) +[ADDED] a new frame method: GetMaxHeight() +[ADDED] a new progressbar method: SetText(text) +[ADDED] a new progressbar method: GetText() +[ADDED] a new tabs method: SetButtonAreaX(x) +[ADDED] a new tabs method: GetButtonAreaX() +[ADDED] a new tabs method: SetButtonAreaWidth(width) +[ADDED] a new tabs method: GetButtonAreaWidth() +[ADDED] a new tabs method: SetAutoButtonAreaWidth(bool) +[ADDED] a new tabs method: GetAutoButtonAreaWidth() +[ADDED] a new text method: SetLinksEnabled(enabled) +[ADDED] a new text method: GetLinksEnabled() +[ADDED] a new text method: SetDetectLinks(detect) +[ADDED] a new text method: GetDetectLinks() +[ADDED] a new textinput method: Copy() +[ADDED] a new textinput method: Paste() +[ADDED] a new textinput method: SelectAll() +[ADDED] a new textinput method: DeselectAll() +[ADDED] a new textinput method: SetMasked(masked) +[ADDED] a new textinput method: GetMasked() +[ADDED] a new textinput method: SetMaskChar(char) +[ADDED] a new textinput method: GetMaskChar() +[ADDED] a new textinput method: SetPlaceholderText(text) +[ADDED] a new textinput method: GetPlaceholderText() +[ADDED] a new tooltip method: SetOffsetX(xoffset) +[ADDED] a new tooltip method: SetOffsetY(yoffset) +[ADDED] a new tooltip method: GetText() +[ADDED] a new text event callback: OnClickLink(object, text) +[ADDED] a new frame event callback: OnResize(object, width, height) +[ADDED] a new util library function: TableHasValue(table, value) +[ADDED] a new util library function: GetHoverObject() +[ADDED] a new callback: textinput(text) +[ADDED] a new object: form +[ADDED] a new object: menu +[ADDED] a new skin drawing function: DrawOverTabPanel(object) +[ADDED] basic clipboard support for the textinput object (0.9.0 only) +[ADDED] custom cursor support for the textinput object +[ADDED] support for love.filesystem changes to loveframes.util.GetDirectoryContents (0.9.0 only) +[ADDED] support for text object link processing +[ADDED] loveframes.downobject +[ADDED] skin directives for the text object +[ADDED] functionality for resizing frames + +[FIXED] error when pressing a function key while a textinput was focused +[FIXED] the tooltip object not setting the state of its text object +[FIXED] the text object not being set to the correct width in certain situations +[FIXED] textinput:GetText appending an extra newline to then end of the text string +[FIXED] text processing issues with the textinput object (0.9.0 only) +[FIXED] text positioning issues when moving the textinput's indicator with the left and right arrow keys +[FIXED] textinput character limit blocking user input when all text was selected + +[CHANGED] cleaned up the tooltip object +[CHANGED] upgraded to middleclass 3.0 +[CHANGED] loveframes.hoverobject is now used to store the currently hovered object +[CHANGED] textinput:SetText now repositions the object's indicator +[CHANGED] reworked the collision detection system for MAJOR performance improvements +[CHANGED] text object formatting options are now specified in a single table rathar than as individual arguments +[CHANGED] text can now be specified as a link via text formatting options +[CHANGED] text formatting options now support link colors + +[REMOVED] base:IsTopCollision + +================================================ +Version 0.9.6.3 - Alpha (August 15 - 2013) +================================================ +[CHANGED] the collapsible category icons used in the stock skins + +[REMOVED] loveframes.info + +================================================ +Version 0.9.6.2 - Alpha (August 14 - 2013) +================================================ +[FIXED] collapsiblecategory:GetOpen() returning a nil value + +[CHANGED] loveframes.util.RemoveAll() now resets loveframes.hoverobject, loveframes.modalobject, loveframes.inputobject and loveframes.hover +[CHANGED] the frame topbar image of both stock skins back to the gradients used before the 0.9.6 update +[CHANGED] the stock skins now draw a gradient for the body of the frame object instead of a solid color +[CHANGED] slight color/drawing adjustments to the blue stock skin +[CHANGED] the collapsiblecategory object now checks its child object's visibility while closed to make sure it hasn't been marked as visible (mainly to prevent collision issues) + +[REMOVED] examples menu from the debug library +[REMOVED] skin selector menu from the debug library +[REMOVED] many skin controls from the orange stock skin that are no longer needed + +================================================ +Version 0.9.6.1 - Alpha (August 12 - 2013) +================================================ +[FIXED] blue stock skin setting an invalid image directory in its skin file + +================================================ +Version 0.9.6 - Alpha (August 12 - 2013) +================================================ +[ADDED] a new base method: GetSkinName() +[ADDED] a new frame method: SetDockable(dockable) +[ADDED] a new frame method: GetDockable() +[ADDED] a new frame method: SetDockZoneSize(size) +[ADDED] a new frame method: GetDockZoneSize() +[ADDED] a new columnlist method: RemoveColumn(id) +[ADDED] a new columnlist method: SetColumnName(id, name) +[ADDED] a new columnlist method: RemoveRow(id) +[ADDED] a new columnlist method: SetRowColumnText(text, rowid, columnid) +[ADDED] a new columnlist method: SetRowColumnData(rowid, columndata) +[ADDED] a new columnlist method: SizeToChildren(max) +[ADDED] a new frame event callback: OnDock(object, dockobject) +[ADDED] a new tabs method: SetTabObject(id, object) +[ADDED] a new util library function: RectangleCollisionCheck(rect1, rect2) +[ADDED] a new util library function: DeepCopy(orig) +[ADDED] a new skins library function: Get(name) +[ADDED] a new skins library function: SetControl(name, control, value) +[ADDED] a new skins library function: SetImage(name, image, value) +[ADDED] a new skins library function: SetImageDirectory(name, dir) +[ADDED] a new skins library function: ReloadImages(name) +[ADDED] docking functionality for frame objects +[ADDED] skins can now define a separate skin to use as a base via skin.base +[ADDED] skins can now load images from external directories via the skin.imagedir setting (defaults to loveframes/skins/skin_name/images) +[ADDED] compatibility for LOVE 0.9.0 + +[CHANGED] scroll buttons for the tabs object are no longer removed and re-created everytime a new tab is added +[CHANGED] base:GetSkin() now returns the skin's table instead of the skin's name +[CHANGED] the frame topbar image of both stock skins to a different gradient + +[FIXED] columnlist:GetSelectedRows() returning a nil value +[FIXED] syntax typo in textinput:RemoveFromText() (was textinput:RemoveFromeText()) +[FIXED] object creation via datatables +[FIXED] multichoice:SelectChoice(choice) causing an error if called when a multichoice list was not open +[FIXED] multichoice object value not being reset when calling multichoice:Clear() +[FIXED] an error that would occurred when attempting to scroll a tabs object with the scroll wheel when the object had no tabs +[FIXED] colunlist:AddRow not working correctly with LuaJIT versions of LOVE + +[REMOVED] drawing code from the orange stock skin (now inherits from the blue stock skin) + +================================================ +Version 0.9.5.14 - Alpha (June 25 - 2013) +================================================ +[FIXED] columnlistheaders and columnlistrows not being clickable when the columnlist object was using a custom state + +================================================ +Version 0.9.5.13 - Alpha (June 24 - 2013) +================================================ +[FIXED] tabbuttons not being clickable when the tabs object was using a custom state + +================================================ +Version 0.9.5.12 - Alpha (June 10 - 2013) +================================================ +[ADDED] a new columnlist method: SelectRow(row, ctrl) +[ADDED] a new columnlist method: DeselectRow(row) +[ADDED] a new columnlist method: GetSelectedRows() +[ADDED] a new columnlist method: SetSelectionEnabled(bool) +[ADDED] a new columnlist method: GetSelectionEnabled() +[ADDED] a new columnlist method: SetMultiselectEnabled(bool) +[ADDED] a new columnlist method: GetMultiselectEnabled() +[ADDED] a new columnlistrow method: SetSelected(bool) +[ADDED] a new columnlistrow method: GetSelected() +[ADDED] a new columnlistrow method: SetColumnData(data) +[ADDED] a new columnlist event callback: OnRowRightClicked(parent, row, data) +[ADDED] a new columnlist event callback: OnRowSelected(parent, row, data) +[ADDED] a new util library function: GetCollisionCount() +[ADDED] a new util library function: GetHover() + +[CHANGED] the default mousewheel scroll-amount value for the list object +[CHANGED] the list object no longer uses delta time for scrolling by default +[CHANGED] some of the columnlistrow object's colors in the default skins + +================================================ +Version 0.9.5.11 - Alpha (May 15 - 2013) +================================================ +[ADDED] ability to use multiple fonts with the text object +[ADDED] a new checkbox method: SetEnabled(bool) +[ADDED] a new checkbox method: GetEnabled() +[ADDED] a new multichoice method: SetEnabled(bool) +[ADDED] a new multichoice method: GetEnabled() + +[FIXED] being able to check a checkbox object while it was not down +[FIXED] collisions being detected on objects outside of the active state + +[CHANGED] the checkbox event callback "OnChanged" now returns both the object and a boolean value representing whether or not the object is checked + +================================================ +Version 0.9.5.10 - Alpha (May 12 - 2013) +================================================ +[ADDED] a new slider method: GetDecimals() +[ADDED] a new numberbox method: SetDecimals(decimals) +[ADDED] a new numberbox method: GetDecimals() +[ADDED] a new textinput method: SetVisible(bool) (override of the base SetVisible method) + +[FIXED] the textinput object's "keydown" property not reseting when the object was not visible + +================================================ +Version 0.9.5.9 - Alpha (May 8 - 2013) +================================================ +[FIXED] the slider event callback "OnRelease" not being called when the sliderbutton was released while being dragged in a non-hover state + +================================================ +Version 0.9.5.8 - Alpha (May 5 - 2013) +================================================ +[ADDED] a new textinput method: SetValue(value) (alias of SetText(text)) +[ADDED] a new textinput method: GetValue() (alias of GetText()) +[ADDED] a new object: grid +[ADDED] a new frame method: SetIcon(icon) +[ADDED] a new frame method: GetIcon() +[ADDED] a new callback for the slider object: OnRelease(object) +[ADDED] a new text method: SetDefaultColor(r, g, b, a) +[ADDED] a new text method: GetDefaultColor() + +[FIXED] OnOpened and OnClosed callbacks for tabbuttons not being asigned properly +[FIXED] the image object not checking its hover state +[FIXED] tabbutton rendering issues with the default skins +[FIXED] some padding issues with multiline textinputs + +[CHANGED] loveframes.config["DIRECTORY"] no longer needs to be set when installing Love Frames + +================================================ +Version 0.9.5.7 - Alpha (April 9 - 2013) +================================================ +[FIXED] some issues with positioning the textinput object's text + +================================================ +Version 0.9.5.6 - Alpha (March 29 - 2013) +================================================ +[FIXED] numberbox:SetValue not functioning correctly + +================================================ +Version 0.9.5.5 - Alpha (March 23 - 2013) +================================================ +[ADDED] a new textinput method: SetRepeatDelay(delay) +[ADDED] a new textinput method: GetRepeatDelay() +[ADDED] a new textinput method: SetRepeatRate(rate) +[ADDED] a new textinput method: GetRepeatRate() + +[FIXED] several typos in the numberbox object's code + +[CHANGED] love frames now resets the active font to the font used before calling loveframes.draw() + +[REMOVED] loveframes.util.CheckForUpdates() + +================================================ +Version 0.9.5.4 - Alpha (March 5 - 2013) +================================================ +[ADDED] a new multichoice method: RemoveChoice(choice) +[ADDED] a new multichoice method: Clear() + +[FIXED] a bug with list:RemoveItem that would cause an error when providing an argument that wasn't a number +[FIXED] the list object not resizing correctly when using list:RemoveItem and providing a number as an argument + +================================================ +Version 0.9.5.3 - Alpha (February 25 - 2013) +================================================ +[FIXED] mousereleased never getting called on the collapsiblecategory's object + +================================================ +Version 0.9.5.2 - Alpha (February 24 - 2013) +================================================ +[ADDED] a new slider method: SetEnabled(bool) +[ADDED] a new slider method: GetEnabled() + +[FIXED] a resizing/layout issue with the columnlist object +[FIXED] base:GetChildren being declared twice and base:GetInternals never being declared + +[CHANGED] major performance improvements for the default skins Blue and Orange + +[REMOVED] the "Blue (basic)" skin +[REMOVED] the "Orange (basic)" skin + +================================================ +Version 0.9.5.1 - Alpha (February 17 - 2013) +================================================ +[ADDED] a new slider method: SetScrollable(bool) +[ADDED] a new slider method: GetScrollable() +[ADDED] a new slider method: SetScrollIncrease(increase) +[ADDED] a new slider method: GetScrollIncrease() +[ADDED] a new slider method: SetScrollDecrease(decrease) +[ADDED] a new slider method: GetScrollDecrease() +[ADDED] a new multichoice method: Sort(func) +[ADDED] a new multichoice method: SetSortFunction(func) +[ADDED] a new multichoice method: GetSortFunction() +[ADDED] support for scrolling the slider object with the mouse wheel + +[FIXED] the slider event callback "OnValueChanged" not passing the slider's new value in certain situations +[FIXED] some typos in the changelog + +[CHANGED] the default mouse wheel scroll amounts of the scrollable objects (list, columnlist, tabs, multichoice) +[CHANGED] minor modifications to the default skins +[CHANGED] objects can now be centered when using the base methods SetPos, SetX, and SetY (ex: object:SetPos(x, y, true) would center the object at the position specified by x and y) + +================================================ +Version 0.9.5 - Alpha (February 11 - 2013) +================================================ +[ADDED] state system +[ADDED] a new base method: SetState(name) +[ADDED] a new base method: GetState() +[ADDED] a new list method: EnableHorizontalStacking(bool) +[ADDED] a new list method: GetHorizontalStacking() +[ADDED] a new list method: SetDTScrolling(bool) +[ADDED] a new list method: GetDTScrolling() +[ADDED] a new image method: GetImageSize() +[ADDED] a new image method: GetImageWidth() +[ADDED] a new image method: GetImageHeight() +[ADDED] a new imagebutton method: GetImageSize() +[ADDED] a new imagebutton method: GetImageWidth() +[ADDED] a new imagebutton method: GetImageHeight() +[ADDED] a new tabs method: RemoveTab(id) +[ADDED] a new tabs method: SetButtonScrollAmount(amount) +[ADDED] a new tabs method: GetButtonScrollAmount() +[ADDED] a new tabs method: SetMouseWheelScrollAmount(amount) +[ADDED] a new tabs method: GetMouseWheelScrollAmount() +[ADDED] a new tabs method: SetDTScrolling(bool) +[ADDED] a new tabs method: GetDTScrolling() +[ADDED] a new columlist method: SetDTScrolling(bool) +[ADDED] a new columlist method: GetDTScrolling() +[ADDED] a new multichoice method: SetDTScrolling(bool) +[ADDED] a new multichoice method: GetDTScrolling() +[ADDED] a new base library function: loveframes.SetState(name) +[ADDED] a new base library function: loveframes.GetState() +[ADDED] a new object: numberbox +[ADDED] support for horizontal stacking of list object items when the list object's display mode is set to vertical + +[FIXED] an error that would occur when providing a number as the text argument for textinput:SetText(text) +[FIXED] default skin images becoming distorted at certain positions on the screen +[FIXED] being able to clear a textinput's text by pressing an unusable key while all of the object's text was selected +[FIXED] several typos in the changelog + +[CHANGED] the SetColor method for the image object now checks for four arguments (r, g, b, a) instead of one (table) +[CHANGED] major code cleanup for the default skins +[CHANGED] list:RemoveItem now accepts either an object or a number as an argument +[CHANGED] miscellaneous changes/improvements to various sections of code +[CHANGED] the images of the default skins + +================================================ +Version 0.9.4.15 - Alpha (January 5 - 2013) +================================================ +[ADDED] a new base method: GetInternals() +[ADDED] a new tooltip method: SetFollowObject(bool) + +================================================ +Version 0.9.4.14 - Alpha (January 4 - 2013) +================================================ +[ADDED] a new util library function: loveframes.util.CheckForUpdates() (note: this function is meant to be used by the developer and is never called internally by Love Frames) +[ADDED] a new callback for the tabbutton object: OnOpened(object) +[ADDED] a new callback for the tabbutton object: OnClosed(object) + +[CHANGED] tabs:AddTab(name, object, tip, image) now accepts two new optional arguments for tabbutton callbacks (ex: tabs:AddTab(name, object, tip, image, onopened, onclosed)) + +================================================ +Version 0.9.4.13 - Alpha (January 4 - 2013) +================================================ +[ADDED] a new base method: IsInList() + +[FIXED] not being able to add the boolean "false" to a template as a property with loveframes.templates.AddProperty(templatename, object, property, value) + +[CHANGED] small code cleanup +[CHANGED] improved performance of the text object while parented within a list object + +================================================ +Version 0.9.4.12 - Alpha (December 30 - 2012) +================================================ +[CHANGED] newlines created on the text object with \n now display properly +[CHANGED] Love Frames now resets the current drawing color to the last color used before calling loveframes.draw() after it has finished all drawing operations + +================================================ +Version 0.9.4.11 - Alpha (December 28 - 2012) +================================================ +[ADDED] a new textinput method: SetAutoScroll(bool) +[ADDED] a new textinput method: GetAutoScroll() + +[FIXED] a couple of typos in the changelog +[FIXED] a sliderbutton calculation error that caused the sliderbutton object to flash while moving the cursor out of it's bounding box while it was down + +[CHANGED] cleaned up a lot of code +[CHANGED] the text object's shadow color now defaults to black + +================================================ +Version 0.9.4.10 - Alpha (December 26 - 2012) +================================================ +[FIXED] registration errors for the new default skins + +================================================ +Version 0.9.4.9 - Alpha (December 26 - 2012) +================================================ +[ADDED] a new default skin: Blue (basic) +[ADDED] a new default skin: Orange (basic) +[ADDED] a new skins library function: loveframes.skins.GetAvailable() + +[CHANGED] the license from CC BY-SA 3.0 to CC BY 3.0 +[CHANGED] made minor improvements to the default skins + +================================================ +Version 0.9.4.8 - Alpha (December 24 - 2012) +================================================ +[ADDED] a new text method: SetShadow(bool) +[ADDED] a new text method: GetShadow() +[ADDED] a new text method: SetShadowOffsets(offsetx, offsety) +[ADDED] a new text method: GetShadowOffsets() +[ADDED] a new text method: SetShadowColor(r, g, b, a) +[ADDED] a new text method: GetShadowColor() +[ADDED] a new columnlist method: SetColumnHeight(height) + +[FIXED] templates not being applied to objects + +[CHANGED] the columnlistarea object now has better sorting +[CHANGED] the frame object no longer sets the size of it's close button + +================================================ +Version 0.9.4.7 - Alpha (December 19 - 2012) +================================================ +[FIXED] textinput:SetFocus(focus) not working properly + +[CHANGED] the tooltip object is now positionable via the standard positioning methods when not set to follow the cursor + +================================================ +Version 0.9.4.6 - Alpha (December 10 - 2012) +================================================ +[ADDED] a new textinput event callback: OnFocusGained(object) +[ADDED] a new textinput event callback: OnFocusLost(object) + +[FIXED] the textinput object accepting input from the tab and delete keys when object.editable was set to false + +[CHANGED] all "." are now replaced with "/" in loveframes.config["DIRECTORY"] after all inernal Love Frames libraries have been loaded (this is to prevent filesystem errors) + +[REMOVED] loveframes.util.TrimString(string) + +================================================ +Version 0.9.4.5 - Alpha (November 28 - 2012) +================================================ +[FIXED] text inputs not losing focus in certain situations + +================================================ +Version 0.9.4.4 - Alpha (November 24 - 2012) +================================================ +[ADDED] a new text input method: SetButtonScrollAmount(amount) +[ADDED] a new text input method: GetButtonScrollAmount() +[ADDED] a new text input method: SetMouseWheelScrollAmount(amount) +[ADDED] a new text input method: GetMouseWheelScrollAmount() +[ADDED] a new multichoice method: SetButtonScrollAmount(amount) +[ADDED] a new multichoice method: GetButtonScrollAmount() +[ADDED] a new multichoice method: SetMouseWheelScrollAmount(amount) +[ADDED] a new multichoice method: GetMouseWheelScrollAmount() +[ADDED] a new column list method: SetButtonScrollAmount(amount) +[ADDED] a new column list method: GetButtonScrollAmount() +[ADDED] a new column list method: SetMouseWheelScrollAmount(amount) +[ADDED] a new column list method: GetMouseWheelScrollAmount() +[ADDED] a new frame method: SetParentLocked(bool) +[ADDED] a new frame method: GetParentLocked() +[ADDED] a new base method: CenterWithinArea(x, y, width, height) +[ADDED] a new library function: loveframes.NewObject(id, name, inherit_from_base) + +[FIXED] an error that would occur when clicking a scroll button on a multiline text input or a multichoice list +[FIXED] a button calculation error that caused the button object to flash while moving the cursor out of it's bounding box while it was down +[FIXED] several errors that could occur when a collapsible category did not have an object +[FIXED] the value of the slider object becoming -0 in certain situations + +[CHANGED] the close button object is no longer positioned internally by the frame object and should now be positioned by it's skin drawing function +[CHANGED] all Love Frames objects are now stored within loveframes.objects +[CHANGED] frames are now draggable when parented to any object +[CHANGED] the look of the debug overlay + +================================================ +Version 0.9.4.3 - Alpha (November 20 - 2012) +================================================ +[ADDED] a new base method: SetProperty(name, value) +[ADDED] a new base method: GetProperty(name) +[ADDED] a new list method: SetButtonScrollAmount(amount) +[ADDED] a new list method: GetButtonScrollAmount() +[ADDED] a new list method: SetMouseWheelScrollAmount(amount) +[ADDED] a new list method: GetMouseWheelScrollAmount() + +[FIXED] a typo in the changelog +[FIXED] image:SetScale(scalex, scaley) not working correctly + +[CHANGED] loveframes.util.GetDirContents(dir, t) to loveframes.util.GetDirectoryContents(dir, t) +[CHANGED] arguments passed to require() to use "." instead of "/" which should fix file loading issues on certain systems + +================================================ +Version 0.9.4.2 - Alpha (November 5 - 2012) +================================================ +[ADDED] a new text object method: SetIgnoreNewlines(bool) +[ADDED] a new text object method: GetIgnoreNewlines() +[ADDED] a new image method: SetOrientation(orientation) +[ADDED] a new image method: GetOrientation() +[ADDED] a new image method: SetScaleX(scalex) +[ADDED] a new image method: GetScaleX() +[ADDED] a new image method: SetScaleY(scaley) +[ADDED] a new image method: GetScaleY() +[ADDED] a new image method: SetScale(scalex, scaley) +[ADDED] a new image method: GetScale() +[ADDED] a new image method: SetOffsetX(x) +[ADDED] a new image method: GetOffsetX() +[ADDED] a new image method: SetOffsetY(y) +[ADDED] a new image method: GetOffsetY() +[ADDED] a new image method: SetOffset(x, y) +[ADDED] a new image method: GetOffset() +[ADDED] a new image method: SetShearX(x) +[ADDED] a new image method: GetShearX() +[ADDED] a new image method: SetShearY(y) +[ADDED] a new image method: GetShearY() +[ADDED] a new image method: SetShear(x, y) +[ADDED] a new image method: GetShear() + +[FIXED] an error that occurred after pressing enter on a multiline text input while all of it's text was selected +[FIXED] text in a single line text input always being cleared when the enter key was pressed +[FIXED] a syntax error in both default skin files that caused imaged to draw incorrect when they had a custom color +[FIXED] an error that would occur when a tab button was being drawn while no font had been set +[FIXED] textinput.OnTextChanged being called too early when using the backspace key or the delete key + +[CHANGED] tab buttons no longer calculate their width from within their internal drawing function and should be sized externally from now on +[CHANGED] the text object now calculates what characters should be drawn while in a list object to improve performance with list objects that contain large amounts of text + +================================================ +Version 0.9.4.1 - Alpha (October 25 - 2012) +================================================ +[ADDED] support for using the delete key in text input objects + +[FIXED] textinupt:GetText() only returning the first line of a multiline text input +[FIXED] an error that occurred when clicking a multiline text input object after setting the object's text to an empty string with textinput:SetText("") +[FIXED] incorrect positioning of the text input object's indicator when using the left or right arrow keys to move the object's indicator to a different line +[FIXED] the text input object not creating a new line correctly in certain situations + +[CHANGED] textinput:GetText() now returns the object's text with line breaks + +================================================ +Version 0.9.4 - Alpha (October 22 - 2012) +================================================ +[ADDED] a new base method: SetDrawOrder() +[ADDED] a new base method: GetDrawOrder() +[ADDED] a new text method: GetFormattedText() +[ADDED] a new text input method: SetEditable(bool) +[ADDED] a new text input method: GetEditable() +[ADDED] a new text input method: GetUsable() +[ADDED] a new text input method: GetUnusable() +[ADDED] a new system for creating and applying templates to objects + +[FIXED] a few incorrect entries in the changelog and added a few entries that were meant to be in the last upadte's changelog +[FIXED] an error that occurred when clicking on a frame that was parented to another frame +[FIXED] frames not being positioned properly if they were parented to another object +[FIXED] modal background object not drawing over umodaled objects in certain situations +[FIXED] an error that would occur when a multichoice list object would become scrollable +[FIXED] an error that would occur when a the column list object had no columns +[FIXED] multichoice object not positioning itself properly if it's parent was the base object + +[CHANGED] text:GetText() now returns a string of the object's text +[CHANGED] parented frames can no longer be modaled + +================================================ +Version 0.9.3.2 - Alpha (Spetember 29 - 2012) +================================================ +[ADDED] a new base method: SetDrawOrder() +[ADDED] a new text input method: SetMultiline(bool) +[ADDED] a new text input method: GetVerticalScrollBody() +[ADDED] a new text input method: GetHorizontalScrollBody() +[ADDED] a new text input method: SetText(text) +[ADDED] a new text input method: HasVerticalScrollBar() +[ADDED] a new text input method: HasHorizontalScrollBar() +[ADDED] a new text input method: GetFont() +[ADDED] a new text input method: GetLineNumbersPanel() +[ADDED] a new text input method: GetMultiline() +[ADDED] a new text input method: GetTextX() +[ADDED] a new text input method: GetTextY() +[ADDED] a new text input method: IsAllTextSelected() +[ADDED] a new text input method: GetLines() +[ADDED] a new text input method: GetOffsetX() +[ADDED] a new text input method: GetOffsetY() +[ADDED] a new text input method: GetIndicatorX() +[ADDED] a new text input method: GetIndicatorY() +[ADDED] a new text input method: SetLineNumbersEnabled(enabled) +[ADDED] a new text input method: GetLineNumbersEnabled() +[ADDED] a new text input method: GetItemWidth() +[ADDED] a new text input method: GetItemHeight() +[ADDED] a new text input method: SetTabReplacement(tabreplacement) +[ADDED] a new text input method: GetTabReplacement() +[ADDED] a new scroll area method: GetBarType() +[ADDED] a new scroll bar method: IsDragging() +[ADDED] a new scroll bar method: GetBarType() +[ADDED] a new scroll button method: GetScrollType() +[ADDED] a new tab button method: GetText() +[ADDED] a new tab button method: GetImage() +[ADDED] a new tab button method: GetTabNumber() +[ADDED] a new column list header method: GetName() +[ADDED] a new column list row method: GetColumnData() +[ADDED] a new multichoice method: SetText(text) +[ADDED] a new multichoice method: GetText() +[ADDED] a new multichoice row method: GetText() +[ADDED] a new progressbar method: GetBarWidth() +[ADDED] a new internal object: linenumberspanel +[ADDED] multiline support for the text input object +[ADDED] ability to select all text within a text input + +[FIXED] progressbar object not positioning itself properly if it's parent was the base object +[FIXED] a typo in the syntax of loveframes.util.SplitString (was "SplitSring", changed to "SplitString") +[FIXED] the tooltip object not disappearing if it was visible when it's object's visibility was changed to false +[FIXED] the text input object's x offset not being adjusted initially when the width of it's text would would become wider than it's drawing area + +[CHANGED] massive code cleanup for optimization and +[CHANGED] frames can now be parent to other objects +[CHANGED] textinput:RunBlink() to textinput:UpdateIndicator() +[CHANGED] textinput:GetBlinkerVisisbility() to textinput:GetIndicatorVisisbility() +[CHANGED] textinput:MoveBlinker(num, exact) to textinput:MoveIndicator(num, exact) +[CHANGED] scrollbars now only autoscroll when their list's item width/height is greater than their previous list's previous item width/height +[CHANGED] cleaned up the code for the default skins +[CHANGED] the text input object's text is no longer drawn internally by the object and should now be draw within the object's skin drawing function +[CHANGED] the column list row object's text is no longer drawn internally by the object and should now be draw within the object's skin drawing function + +[REMOVED] textinput:SetTextColor(color) +[REMOVED] columnlistrow:SetTextColor() + +================================================ +Version 0.9.3.1 - Alpha (Spetember 2 - 2012) +================================================ +[ADDED] numpad enter key support for textinput.OnEnter + +[FIXED] duplicate progressbar:GetCompleted() in progressbar.lua + +[CHANGED] textinput.OnTextEnterd to textinput.OnTextChanged +[CHANGED] textinput.OnTextChanged is now also called when the backspace key is pressed + +================================================ +Version 0.9.3 - Alpha (Spetember 1 - 2012) +================================================ +[ADDED] a new base method: GetParents() +[ADDED] a new base method: IsTopInternal() +[ADDED] a new base method: IsInternal() +[ADDED] a new base method: GetType() +[ADDED] a new checkbox method: GetFont() +[ADDED] a new checkbox method: GetBoxSize() +[ADDED] a new checkbox method: GetBoxWidth() +[ADDED] a new checkbox method: GetBoxHeight() +[ADDED] a new image method: GetImage() +[ADDED] a new image method: GetColor() +[ADDED] a new progressbar method: GetCompleted() +[ADDED] a new tabs method: GetTabNumber() +[ADDED] a new text method: GetLines() +[ADDED] a new text input method: SetLimit(limit) +[ADDED] a new text input method: SetUsable(usable) +[ADDED] a new text input method: SetUnusable(unusable) +[ADDED] a new text input method: Clear() +[ADDED] a new text input method: GetText() +[ADDED] support for line breaks in the text object (please refer to the wiki on how to format your text with line breaks) +[ADDED] more information to the debug overlay + +[FIXED] tooltips and multichoice lists not functioning properly when a frame was modaled +[FIXED] the text object not moving it's base parent to the top when clicked if it's base parent was a frame +[FIXED] the list object not scrolling via the mouse wheel due to certain collision detection issues +[FIXED] tooltips flickering when in a hover state +[FIXED] slider calculation errors +[FIXED] skin.DrawRepeatingImage creating a new image every frame +[FIXED] checkbox text not scrolling properly when in a list +[FIXED] the collapsible category's object not scrolling properly when in a list +[FIXED] skins not assuming the drawing functions of the default skins if drawing functions in the active skin were missing +[FIXED] some objects not being removed +[FIXED] sliders not scrolling properly when in a list + +[CHANGED] various code for a small cleanup +[CHANGED] tooltips from being children of the base object to internals of the base object +[CHANGED] tooltips from never being removed to being removed when their assigned object is removed +[CHANGED] tooltips are now not able to be assigned to the base object (this would cause an error due to the way the tooltip currently functions) +[CHANGED] the text object now positions it's text when SetText is called instead of positioning it's text every frame +[CHANGED] slider:SetMax(max), slider:SetMin(min) and slider:SetMinMax(min, max) now adjust the slider's value if the slider's value is out of the range specified +[CHANGED] loveframes.Create(data, parent) now activates an error screen if the object specified is invalid + +================================================ +Version 0.9.2.5 - Alpha (June 1 - 2012) +================================================ +[FIXED] the text object not drawing numbers in certain situations +[FIXED] a typo in the changelog + +================================================ +Version 0.9.2.4 - Alpha (May 29 - 2012) +================================================ +[FIXED] the SetSkin(skin) method causing a stack overflow + +================================================ +Version 0.9.2.3 - Alpha (May 27 - 2012) +================================================ +[FIXED] imagebutton.OnClick not working +[FIXED] text being added into the text input object at incorrect positions when the value of textinput.blinknum was 0 +[FIXED] a bug that would cause an error when running SetVisible on a frame object when the value of frame.showclose was true + +================================================ +Version 0.9.2.2 - Alpha (May 23 - 2012) +================================================ +[FIXED] vertical slider value direction (top is now max value and bottom is 0) +[FIXED] text flashing in the text input object when moving the text blinker to the right +[FIXED] text blinker in the text input object not appearing in certain situations + +================================================ +Version 0.9.2 - Alpha (May 22 - 2012) +================================================ +[ADDED] a new slider method: SetButtonSize(width, height) +[ADDED] a new slider method: GetButtonSize() +[ADDED] a new slider method: SetSlideType(slidetype) +[ADDED] a new slider button method: MoveToY(y) + +[REMOVED] slider method: SetButtonYCenter(y) +[REMOVED] slider method: GetButtonYCenter() + +[FIXED] the column list row object not setting it's text's font to it's font +[FIXED] a small gradient error in the default skins +[FIXED] an error that caused tab buttons to be over-scrolled with the middle mouse button +[FIXED] the scroll area object not moving it's base parent to the top when clicked (only in situations where it's base parent was a frame) + +[CHANGED] sliders no longer use ycenter for button positioning +[CHANGED] slider functionality, sliders can now be horizontal or vertical (use slider:SetSlideType("horizontal" or "vertical")) +[CHANGED] lots of code for optimization and general cleanliness +[CHANGED] a few minor things in the default skins +[CHANGED] scrollbar hover behavior when being moved via it's scroll area being clicked (will no longer auto hover when it reaches the mouse position) + +================================================ +Version 0.9.1.6 - Alpha (May 17 - 2012) +================================================ +[ADDED] a new column list method: Clear() +[ADDED] a new column list method: SetAutoScroll(bool) +[ADDED] "autoscroll" property for the column list object +[ADDED] a new column list area method: Clear() +[ADDED] a new skin function: DrawOverTextInput() + +[FIXED] not being able to move the column list object's scrollbar by clicking on it's scroll area +[FIXED] column list rows color indexes becoming disordered when the list was sorted via a column list header + +[CHANGED] a few minor things in the default skins + +================================================ +Version 0.9.1.5 - Alpha (May 16 - 2012) +================================================ +[FIXED] a bug that caused scrollbars to always autoscroll + +================================================ +Version 0.9.1.4 - Alpha (May 16 - 2012) +================================================ +[FIXED] list:SetAutoScroll() not working + +================================================ +Version 0.9.1.3 - Alpha (May 14 - 2012) +================================================ +[ADDED] modal system for frames +[ADDED] a new frame method: SetModal(bool) +[ADDED] a new frame method: GetModal() +[ADDED] a new frame method: SetVisible(bool) - this override is part of a frame bug fix +[ADDED] "showclose" property for the frame object +[ADDED] a new internal object: modal background +[ADDED] a new base method: IsActive() +[ADDED] a new base method: CenterX() +[ADDED] a new base method: CenterY() +[ADDED] a new skin function: DrawOverColumList() + +[FIXED] a bug that made the frame's close button become visible when the frame was made visible and show close button was set to false + +================================================ +Version 0.9.1.2 - Alpha (May 12 - 2012) +================================================ +[ADDED] a system for preventing objects from being hovered over when another object is being pressed or is "down" +[ADDED] "down" property for the checkbox object +[ADDED] "down" property for the collapsible category object +[ADDED] a new method for the tabs object: SetToolTipFont(font) + +[FIXED] list:GetScrollBar() crashing when the list had no scroll bar +[FIXED] not being able to move the text input blinker to the front or end of the it's text by clicking on it's whitespace +[FIXED] the multichoice row object being "down" when mouse buttons other than the left mouse button were pressed + +[CHANGED] collapsible category opening and closing system (will now only open or close when "down") + +================================================ +Version 0.9.1 - Alpha (May 8 - 2012) +================================================ +[ADDED] a new object: image button +[ADDED] a new skin: Orange +[ADDED] a new column list row method: SetTextPos(x, y) +[ADDED] a new column list row method: SetFont(font) +[ADDED] a new column list row method: GetFont() +[ADDED] a new column list row method: GetColorIndex() +[ADDED] a new column list row method: SetTextColor(color) +[ADDED] a new debug function: SkinSelector() +[ADDED] license.txt +[ADDED] changelog.txt + +[CHANGED] the name of the default skin to Blue +[CHANGED] the drawing system of the image object, the image object will now draw it's image from it's skin function instead of it's internal drawing function +[CHANGED] argument type for adding an image to a tab in tabs:AddTab(), the image argument can now be either an image object or a string containg the path to an image +[CHANGED] the look of the debug overlay +[CHANGED] some of the code within the default skins to improve performance (as suggested by kikito) + +[REMOVED] license.html +[REMOVED] loveframes.graphics and graphics.lua + +================================================ +Version 0.9 - Alpha (May 5 - 2012) +================================================ +Initial commit diff --git a/loveframes/init.lua b/loveframes/init.lua new file mode 100644 index 0000000..a5fb7af --- /dev/null +++ b/loveframes/init.lua @@ -0,0 +1,308 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +local path = ... + +local loveframes = {} + +-- special require for loveframes specific modules +loveframes.require = function(name) + local ret = require(name) + if type(ret) == 'function' then return ret(loveframes) end + return ret +end + +-- loveframes specific modules +loveframes.require(path .. ".libraries.utils") +loveframes.require(path .. ".libraries.templates") +loveframes.require(path .. ".libraries.objects") +loveframes.require(path .. ".libraries.skins") + +-- generic libraries +loveframes.class = require(path .. ".third-party.middleclass") +loveframes.utf8 = require(path .. ".third-party.utf8") + + +-- library info +loveframes.author = "Kenny Shields" +loveframes.version = "11.3" +loveframes.stage = "Alpha" + +-- library configurations +loveframes.config = {} +loveframes.config["DIRECTORY"] = nil +loveframes.config["DEFAULTSKIN"] = "Default" +loveframes.config["ACTIVESKIN"] = "Default" +loveframes.config["INDEXSKINIMAGES"] = true +loveframes.config["DEBUG"] = false +loveframes.config["ENABLE_SYSTEM_CURSORS"] = true + +-- misc library vars +loveframes.state = "none" +loveframes.drawcount = 0 +loveframes.collisioncount = 0 +loveframes.objectcount = 0 +loveframes.hoverobject = false +loveframes.modalobject = false +loveframes.inputobject = false +loveframes.downobject = false +loveframes.resizeobject = false +loveframes.dragobject = false +loveframes.hover = false +loveframes.input_cursor_set = false +loveframes.prevcursor = nil +loveframes.basicfont = love.graphics.newFont(12) +loveframes.basicfontsmall = love.graphics.newFont(10) +loveframes.collisions = {} + +-- install directory of the library +local dir = loveframes.config["DIRECTORY"] or path + +-- replace all "." with "/" in the directory setting +dir = loveframes.utf8.gsub(loveframes.utf8.gsub(dir, "\\", "/"), "(%a)%.(%a)", "%1/%2") +loveframes.config["DIRECTORY"] = dir + +-- enable key repeat +love.keyboard.setKeyRepeat(true) + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates all library objects +--]]--------------------------------------------------------- +function loveframes.update(dt) + + local base = loveframes.base + local input_cursor_set = loveframes.input_cursor_set + + loveframes.collisioncount = 0 + loveframes.objectcount = 0 + loveframes.hover = false + loveframes.hoverobject = false + + local downobject = loveframes.downobject + if #loveframes.collisions > 0 then + local top = loveframes.collisions[#loveframes.collisions] + if not downobject then + loveframes.hoverobject = top + else + if downobject == top then + loveframes.hoverobject = top + end + end + end + + if loveframes.config["ENABLE_SYSTEM_CURSORS"] then + local hoverobject = loveframes.hoverobject + local arrow = love.mouse.getSystemCursor("arrow") + local curcursor = love.mouse.getCursor() + if hoverobject then + local ibeam = love.mouse.getSystemCursor("ibeam") + local mx, my = love.mouse.getPosition() + if hoverobject.type == "textinput" and not loveframes.resizeobject then + if curcursor ~= ibeam then + love.mouse.setCursor(ibeam) + end + elseif hoverobject.type == "frame" then + if not hoverobject.dragging and hoverobject.canresize then + if loveframes.BoundingBox(hoverobject.x, mx, hoverobject.y, my, 5, 1, 5, 1) then + local sizenwse = love.mouse.getSystemCursor("sizenwse") + if curcursor ~= sizenwse then + love.mouse.setCursor(sizenwse) + end + elseif loveframes.BoundingBox(hoverobject.x + hoverobject.width - 5, mx, hoverobject.y + hoverobject.height - 5, my, 5, 1, 5, 1) then + local sizenwse = love.mouse.getSystemCursor("sizenwse") + if curcursor ~= sizenwse then + love.mouse.setCursor(sizenwse) + end + elseif loveframes.BoundingBox(hoverobject.x + hoverobject.width - 5, mx, hoverobject.y, my, 5, 1, 5, 1) then + local sizenesw = love.mouse.getSystemCursor("sizenesw") + if curcursor ~= sizenesw then + love.mouse.setCursor(sizenesw) + end + elseif loveframes.BoundingBox(hoverobject.x, mx, hoverobject.y + hoverobject.height - 5, my, 5, 1, 5, 1) then + local sizenesw = love.mouse.getSystemCursor("sizenesw") + if curcursor ~= sizenesw then + love.mouse.setCursor(sizenesw) + end + elseif loveframes.BoundingBox(hoverobject.x + 5, mx, hoverobject.y, my, hoverobject.width - 10, 1, 2, 1) then + local sizens = love.mouse.getSystemCursor("sizens") + if curcursor ~= sizens then + love.mouse.setCursor(sizens) + end + elseif loveframes.BoundingBox(hoverobject.x + 5, mx, hoverobject.y + hoverobject.height - 2, my, hoverobject.width - 10, 1, 2, 1) then + local sizens = love.mouse.getSystemCursor("sizens") + if curcursor ~= sizens then + love.mouse.setCursor(sizens) + end + elseif loveframes.BoundingBox(hoverobject.x, mx, hoverobject.y + 5, my, 2, 1, hoverobject.height - 10, 1) then + local sizewe = love.mouse.getSystemCursor("sizewe") + if curcursor ~= sizewe then + love.mouse.setCursor(sizewe) + end + elseif loveframes.BoundingBox(hoverobject.x + hoverobject.width - 2, mx, hoverobject.y + 5, my, 2, 1, hoverobject.height - 10, 1) then + local sizewe = love.mouse.getSystemCursor("sizewe") + if curcursor ~= sizewe then + love.mouse.setCursor(sizewe) + end + else + if not loveframes.resizeobject then + local arrow = love.mouse.getSystemCursor("arrow") + if curcursor ~= arrow then + love.mouse.setCursor(arrow) + end + end + end + end + elseif hoverobject.type == "text" and hoverobject.linkcol and not loveframes.resizeobject then + local hand = love.mouse.getSystemCursor("hand") + if curcursor ~= hand then + love.mouse.setCursor(hand) + end + end + if curcursor ~= arrow then + if hoverobject.type ~= "textinput" and hoverobject.type ~= "frame" and not hoverobject.linkcol and not loveframes.resizeobject then + love.mouse.setCursor(arrow) + elseif hoverobject.type ~= "textinput" and curcursor == ibeam then + love.mouse.setCursor(arrow) + end + end + else + if curcursor ~= arrow and not loveframes.resizeobject then + love.mouse.setCursor(arrow) + end + end + end + + loveframes.collisions = {} + base:update(dt) + +end + +--[[--------------------------------------------------------- + - func: draw() + - desc: draws all library objects +--]]--------------------------------------------------------- +function loveframes.draw() + + local base = loveframes.base + local r, g, b, a = love.graphics.getColor() + local font = love.graphics.getFont() + + base:draw() + + loveframes.drawcount = 0 + + if loveframes.config["DEBUG"] then + loveframes.DebugDraw() + end + + love.graphics.setColor(r, g, b, a) + + if font then + love.graphics.setFont(font) + end + +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function loveframes.mousepressed(x, y, button) + + local base = loveframes.base + base:mousepressed(x, y, button) + + -- close open menus + local bchildren = base.children + local hoverobject = loveframes.hoverobject + for k, v in ipairs(bchildren) do + local otype = v.type + local visible = v.visible + if hoverobject then + local htype = hoverobject.type + if otype == "menu" and visible and htype ~= "menu" and htype ~= "menuoption" then + v:SetVisible(false) + end + else + if otype == "menu" and visible then + v:SetVisible(false) + end + end + end + +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function loveframes.mousereleased(x, y, button) + + local base = loveframes.base + base:mousereleased(x, y, button) + + -- reset the hover object + if button == 1 then + loveframes.downobject = false + loveframes.selectedobject = false + end + +end + +--[[--------------------------------------------------------- + - func: wheelmoved(x, y) + - desc: called when the player moves a mouse wheel +--]]--------------------------------------------------------- +function loveframes.wheelmoved(x, y) + + local base = loveframes.base + base:wheelmoved(x, y) + +end + +--[[--------------------------------------------------------- + - func: keypressed(key, isrepeat) + - desc: called when the player presses a key +--]]--------------------------------------------------------- +function loveframes.keypressed(key, isrepeat) + + local base = loveframes.base + base:keypressed(key, isrepeat) + +end + +--[[--------------------------------------------------------- + - func: keyreleased(key) + - desc: called when the player releases a key +--]]--------------------------------------------------------- +function loveframes.keyreleased(key) + + local base = loveframes.base + base:keyreleased(key) + +end + +--[[--------------------------------------------------------- + - func: textinput(text) + - desc: called when the user inputs text +--]]--------------------------------------------------------- +function loveframes.textinput(text) + + local base = loveframes.base + base:textinput(text) + +end + + +loveframes.LoadObjects(dir .. "/objects") +loveframes.LoadTemplates(dir .. "/templates") +loveframes.LoadSkins(dir .. "/skins") + +-- create the base gui object +local base = loveframes.objects["base"] +loveframes.base = base:new() + +return loveframes diff --git a/loveframes/libraries/objects.lua b/loveframes/libraries/objects.lua new file mode 100644 index 0000000..624b53d --- /dev/null +++ b/loveframes/libraries/objects.lua @@ -0,0 +1,150 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +loveframes.objects = {} + + +--[[--------------------------------------------------------- + - func: Create(type, parent) + - desc: creates a new object or multiple new objects + (based on the method used) and returns said + object or objects for further manipulation +--]]--------------------------------------------------------- +function loveframes.Create(data, parent) + + if type(data) == "string" then + + local objects = loveframes.objects + local object = objects[data] + local objectcount = loveframes.objectcount + + if not object then + loveframes.Error("Error creating object: Invalid object '" ..data.. "'.") + end + + -- create the object + local newobject = object:new() + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(newobject) + + -- if the object is a tooltip, return it and go no further + if data == "tooltip" then + return newobject + end + + -- remove the object if it is an internal + if newobject.internal then + newobject:Remove() + return + end + + -- parent the new object by default to the base gui object + newobject.parent = loveframes.base + table.insert(loveframes.base.children, newobject) + + -- if the parent argument is not nil, make that argument the object's new parent + if parent then + newobject:SetParent(parent) + end + + loveframes.objectcount = objectcount + 1 + + -- return the object for further manipulation + return newobject + + elseif type(data) == "table" then + + -- table for creation of multiple objects + local objects = {} + + -- this function reads a table that contains a layout of object properties and then + -- creates objects based on those properties + local function CreateObjects(t, o, c) + local child = c or false + local validobjects = loveframes.objects + for k, v in pairs(t) do + -- current default object + local object = validobjects[v.type]:new() + -- insert the object into the table of objects being created + table.insert(objects, object) + -- parent the new object by default to the base gui object + object.parent = loveframes.base + table.insert(loveframes.base.children, object) + if o then + object:SetParent(o) + end + -- loop through the current layout table and assign the properties found + -- to the current object + for i, j in pairs(v) do + if i ~= "children" and i ~= "func" then + if child then + if i == "x" then + object["staticx"] = j + elseif i == "y" then + object["staticy"] = j + else + object[i] = j + end + else + object[i] = j + end + elseif i == "children" then + CreateObjects(j, object, true) + end + end + if v.func then + v.func(object) + end + end + end + + -- create the objects + CreateObjects(data) + + return objects + + end + +end + +--[[--------------------------------------------------------- + - func: NewObject(id, name, inherit_from_base) + - desc: creates a new object +--]]--------------------------------------------------------- +function loveframes.NewObject(id, name, inherit_from_base) + + local objects = loveframes.objects + local object = false + + if inherit_from_base then + local base = objects["base"] + object = loveframes.class(name, base) + objects[id] = object + else + object = loveframes.class(name) + objects[id] = object + end + + return object + +end + +function loveframes.LoadObjects(dir) + local objectlist = loveframes.GetDirectoryContents(dir) + -- loop through a list of all gui objects and require them + for k, v in ipairs(objectlist) do + if v.extension == "lua" then + loveframes.require(v.requirepath) + end + end +end +--return objects + +---------- module end ---------- +end \ No newline at end of file diff --git a/loveframes/libraries/skins.lua b/loveframes/libraries/skins.lua new file mode 100644 index 0000000..1935f2a --- /dev/null +++ b/loveframes/libraries/skins.lua @@ -0,0 +1,117 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- skins library +loveframes.skins = {} + +--[[--------------------------------------------------------- + - func: RegisterSkin(skin) + - desc: registers a skin +--]]--------------------------------------------------------- +function loveframes.RegisterSkin(skin) + local skins = loveframes.skins + local name = skin.name + local author = skin.author + local version = skin.version + local basename = skin.base + local newskin = false + + if name == "" or not name then + loveframes.Error("Skin registration error: Invalid or missing name data.") + end + + if author == "" or not author then + loveframes.Error("Skin registration error: Invalid or missing author data.") + end + + if version == "" or not version then + loveframes.Error("Skin registration error: Invalid or missing version data.") + end + + local namecheck = skins[name] + if namecheck then + loveframes.Error("Skin registration error: A skin with the name '" ..name.. "' already exists.") + end + + local dir = skin.directory or loveframes.config["DIRECTORY"] .. "/skins/" ..name + local dircheck = love.filesystem.getInfo(dir) ~= nil and love.filesystem.getInfo(dir)["type"] == "directory" + if not dircheck then + loveframes.Error("Skin registration error: Could not find a directory for skin '" ..name.. "'.") + end + + local imagedir = skin.imagedir or dir .. "/images" + local imagedircheck = love.filesystem.getInfo(imagedir) ~= nil and love.filesystem.getInfo(imagedir)["type"] == "directory" + if not imagedircheck then + loveframes.Error("Skin registration error: Could not find an image directory for skin '" ..name.. "'.") + end + + if basename then + --local basename = base + local base = skins[basename] + if not base then + loveframes.Error("Could not find base skin '" ..basename.. "' for skin '" ..name.. "'.") + end + newskin = loveframes.DeepCopy(base) + newskin.name = name + newskin.author = author + newskin.version = version + newskin.imagedir = imagedir + local skincontrols = skin.controls + local basecontrols = base.controls + if skincontrols and basecontrols then + for k, v in pairs(skincontrols) do + newskin.controls[k] = v + end + for k, v in pairs(skin) do + if type(v) == "function" then + newskin[k] = v + end + end + end + end + + if not newskin then + newskin = skin + end + + newskin.dir = dir + local images = {} + + local indeximages = loveframes.config["INDEXSKINIMAGES"] + if indeximages then + local imagelist = loveframes.GetDirectoryContents(imagedir) + local filename, extension, image + for k, v in ipairs(imagelist) do + extension = v.extension + filename = v.name .. "." .. extension + if extension == "png" then + image = love.graphics.newImage(v.fullpath) + image:setFilter("nearest", "nearest") + images[filename] = image + end + end + end + newskin.images = images + skins[name] = newskin +end + +function loveframes.LoadSkins(dir) + local skinlist = loveframes.GetDirectoryContents(dir) + -- loop through a list of all gui skins and require them + local skin + for k, v in ipairs(skinlist) do + if v.extension == "lua" then + skin = loveframes.require(v.requirepath) + --loveframes.RegisterSkin(skin) + end + end +end +--return skins + +---------- module end ---------- +end diff --git a/loveframes/libraries/templates.lua b/loveframes/libraries/templates.lua new file mode 100644 index 0000000..663ead3 --- /dev/null +++ b/loveframes/libraries/templates.lua @@ -0,0 +1,85 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- templates library +loveframes.templates = {} + +--[[--------------------------------------------------------- + - func: RegisterTemplate(template) + - desc: registers a template +--]]--------------------------------------------------------- +function loveframes.RegisterTemplate(template) + -- display an error message if template is not a table + if type(template) ~= "table" then + loveframes.Error("Could not register template: Template argument must be a table.") + end + + local name = template.name + local base = loveframes.objects["base"] + + -- display an error message if a template name was not given + if not name then + loveframes.Error("Could not register template: No template name given.") + end + + if name == "Base" then + base:include(template.properties["*"]) + end + + -- insert the template into the available templates table + loveframes.templates[name] = template + +end + +--[[--------------------------------------------------------- + - func: ApplyTemplatesToObject(object) + - desc: applies the properties of registered templates + to an object +--]]--------------------------------------------------------- +function loveframes.ApplyTemplatesToObject(object) + local templates = loveframes.templates + local type = object.type + -- loop through all available templates + for k, v in pairs(templates) do + -- make sure the base template doesn't get applied more than once + if k ~= "Base" then + local properties = v.properties + local hasall = loveframes.TableHasKey(properties, "*") + local hasobject = false + if not hasall then + hasobject = loveframes.TableHasKey(properties, type) + end + if hasall then + for k, v in pairs(properties["*"]) do + object[k] = v + end + elseif hasobject then + -- apply the template properties to the object + for k, v in pairs(properties[type]) do + object[k] = v + end + end + end + end + +end + +function loveframes.LoadTemplates(dir) + local templatelist = loveframes.GetDirectoryContents(dir) + -- loop through a list of all gui templates and require them + for k, v in ipairs(templatelist) do + if v.extension == "lua" then + local template = require(v.requirepath) + loveframes.RegisterTemplate(template) + end + end +end +--return templates + +---------- module end ---------- +end diff --git a/loveframes/libraries/utils.lua b/loveframes/libraries/utils.lua new file mode 100644 index 0000000..3ee4be5 --- /dev/null +++ b/loveframes/libraries/utils.lua @@ -0,0 +1,452 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- util library +--local util = {} + +--[[--------------------------------------------------------- + - func: SetState(name) + - desc: sets the current state +--]]--------------------------------------------------------- +function loveframes.SetState(name) + + loveframes.state = name + loveframes.base.state = name + +end + +--[[--------------------------------------------------------- + - func: GetState() + - desc: gets the current state +--]]--------------------------------------------------------- +function loveframes.GetState() + + return loveframes.state + +end + +--[[--------------------------------------------------------- + - func: SetActiveSkin(name) + - desc: sets the active skin +--]]--------------------------------------------------------- +function loveframes.SetActiveSkin(name) + local skin = name and loveframes.skins[name] + if not skin then print("SetActiveSkin: no such skin") return end + + loveframes.config["ACTIVESKIN"] = name + local object = loveframes.base + object:SetSkin(name) +end + +--[[--------------------------------------------------------- + - func: GetActiveSkin() + - desc: gets the active skin +--]]--------------------------------------------------------- +function loveframes.GetActiveSkin() + local index = loveframes.config["ACTIVESKIN"] + return loveframes.skins[index] +end + +--[[--------------------------------------------------------- + - func: BoundingBox(x1, x2, y1, y2, w1, w2, h1, h2) + - desc: checks for a collision between two boxes + - note: I take no credit for this function +--]]--------------------------------------------------------- +function loveframes.BoundingBox(x1, x2, y1, y2, w1, w2, h1, h2) + if x1 > x2 + w2 - 1 or y1 > y2 + h2 - 1 or x2 > x1 + w1 - 1 or y2 > y1 + h1 - 1 then + return false + else + return true + end +end + +--[[--------------------------------------------------------- + - func: GetCollisions(object, table) + - desc: gets all objects colliding with the mouse +--]]--------------------------------------------------------- +function loveframes.GetCollisions(object, t) + local x, y = love.mouse.getPosition() + local curstate = loveframes.state + local object = object or loveframes.base + local visible = object.visible + local children = object.children + local internals = object.internals + local objectstate = object.state + local t = t or {} + + if objectstate == curstate and visible then + local objectx = object.x + local objecty = object.y + local objectwidth = object.width + local objectheight = object.height + local col = loveframes.BoundingBox(x, objectx, y, objecty, 1, objectwidth, 1, objectheight) + local collide = object.collide + if col and collide then + local clickbounds = object.clickbounds + if clickbounds then + local cx = clickbounds.x + local cy = clickbounds.y + local cwidth = clickbounds.width + local cheight = clickbounds.height + local clickcol = loveframes.BoundingBox(x, cx, y, cy, 1, cwidth, 1, cheight) + if clickcol then + table.insert(t, object) + end + else + table.insert(t, object) + end + end + if children then + for k, v in ipairs(children) do + loveframes.GetCollisions(v, t) + end + end + if internals then + for k, v in ipairs(internals) do + local type = v.type + if type ~= "tooltip" then + loveframes.GetCollisions(v, t) + end + end + end + end + + return t +end + +--[[--------------------------------------------------------- + - func: GetAllObjects(object, table) + - desc: gets all active objects +--]]--------------------------------------------------------- +function loveframes.GetAllObjects(object, t) + local object = object or loveframes.base + local internals = object.internals + local children = object.children + local t = t or {} + + table.insert(t, object) + + if internals then + for k, v in ipairs(internals) do + loveframes.GetAllObjects(v, t) + end + end + + if children then + for k, v in ipairs(children) do + loveframes.GetAllObjects(v, t) + end + end + + return t + +end + +--[[--------------------------------------------------------- + - func: GetDirectoryContents(directory, table) + - desc: gets the contents of a directory and all of + its subdirectories +--]]--------------------------------------------------------- +function loveframes.GetDirectoryContents(dir, t) + local dir = dir + local t = t or {} + local dirs = {} + local files = love.filesystem.getDirectoryItems(dir) + + for k, v in ipairs(files) do + local isdir = love.filesystem.getInfo(dir.. "/" ..v) ~= nil and love.filesystem.getInfo(dir.. "/" ..v)["type"] == "directory" --love.filesystem.isDirectory(dir.. "/" ..v) + if isdir == true then + table.insert(dirs, dir.. "/" ..v) + else + local parts = loveframes.SplitString(v, "([.])") + local extension = #parts > 1 and parts[#parts] + if #parts > 1 then + parts[#parts] = nil + end + local name = table.concat(parts, ".") + table.insert(t, { + path = dir, + fullpath = dir.. "/" ..v, + requirepath = loveframes.utf8.gsub(dir, "/", ".") .. "." ..name, + name = name, + extension = extension + }) + end + end + + for k, v in ipairs(dirs) do + t = loveframes.GetDirectoryContents(v, t) + end + + return t +end + + +--[[--------------------------------------------------------- + - func: Round(num, idp) + - desc: rounds a number based on the decimal limit + - note: I take no credit for this function +--]]--------------------------------------------------------- +function loveframes.Round(num, idp) + local mult = 10^(idp or 0) + + if num >= 0 then + return math.floor(num * mult + 0.5) / mult + else + return math.ceil(num * mult - 0.5) / mult + end +end + +--[[--------------------------------------------------------- + - func: SplitString(string, pattern) + - desc: splits a string into a table based on a given pattern + - note: I take no credit for this function +--]]--------------------------------------------------------- +function loveframes.SplitString(str, pat) + local t = {} -- NOTE: use {n = 0} in Lua-5.0 + + if pat == " " then + local fpat = "(.-)" .. pat + local last_end = 1 + local s, e, cap = loveframes.utf8.find(str, fpat, 1) + while s do + if s ~= #str then + cap = cap .. " " + end + if s ~= 1 or cap ~= "" then + table.insert(t,cap) + end + last_end = e+1 + s, e, cap = loveframes.utf8.find(str, fpat, last_end) + end + if last_end <= #str then + cap = loveframes.utf8.sub(str, last_end) + table.insert(t, cap) + end + else + local fpat = "(.-)" .. pat + local last_end = 1 + local s, e, cap = loveframes.utf8.find(str, fpat, 1) + while s do + if s ~= 1 or cap ~= "" then + table.insert(t,cap) + end + last_end = e+1 + s, e, cap = loveframes.utf8.find(str, fpat, last_end) + end + if last_end <= #str then + cap = loveframes.utf8.sub(str, last_end) + table.insert(t, cap) + end + end + + return t +end + +--[[--------------------------------------------------------- + - func: RemoveAll() + - desc: removes all gui elements +--]]--------------------------------------------------------- +function loveframes.RemoveAll() + loveframes.base.children = {} + loveframes.base.internals = {} + + loveframes.hoverobject = false + loveframes.downobject = false + loveframes.modalobject = false + loveframes.inputobject = false + loveframes.hover = false +end + +--[[--------------------------------------------------------- + - func: TableHasValue(table, value) + - desc: checks to see if a table has a specific value +--]]--------------------------------------------------------- +function loveframes.TableHasValue(table, value) + for k, v in pairs(table) do + if v == value then + return true + end + end + + return false +end + +--[[--------------------------------------------------------- + - func: TableHasKey(table, key) + - desc: checks to see if a table has a specific key +--]]--------------------------------------------------------- +function loveframes.TableHasKey(table, key) + return table[key] ~= nil + +end + +--[[--------------------------------------------------------- + - func: Error(message) + - desc: displays a formatted error message +--]]--------------------------------------------------------- +function loveframes.Error(message) + error("[Love Frames] " ..message) +end + +--[[--------------------------------------------------------- + - func: GetCollisionCount() + - desc: gets the total number of objects colliding with + the mouse +--]]--------------------------------------------------------- +function loveframes.GetCollisionCount() + return loveframes.collisioncount +end + +--[[--------------------------------------------------------- + - func: GetHover() + - desc: returns loveframes.hover, can be used to check + if the mouse is colliding with a visible + Love Frames object +--]]--------------------------------------------------------- +function loveframes.GetHover() + return loveframes.hover +end + +--[[--------------------------------------------------------- + - func: RectangleCollisionCheck(rect1, rect2) + - desc: checks for a collision between two rectangles + based on two tables containing rectangle sizes + and positions +--]]--------------------------------------------------------- +function loveframes.RectangleCollisionCheck(rect1, rect2) + return loveframes.BoundingBox(rect1.x, rect2.x, rect1.y, rect2.y, rect1.width, rect2.width, rect1.height, rect2.height) +end + +--[[--------------------------------------------------------- + - func: DeepCopy(orig) + - desc: copies a table + - note: I take not credit for this function +--]]--------------------------------------------------------- +function loveframes.DeepCopy(orig) + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[loveframes.DeepCopy(orig_key)] = loveframes.DeepCopy(orig_value) + end + setmetatable(copy, loveframes.DeepCopy(getmetatable(orig))) + else -- number, string, boolean, etc + copy = orig + end + return copy +end + +--[[--------------------------------------------------------- + - func: GetHoverObject() + - desc: returns loveframes.hoverobject +--]]--------------------------------------------------------- +function loveframes.GetHoverObject() + + return loveframes.hoverobject + +end + +--[[--------------------------------------------------------- + - func: IsCtrlDown() + - desc: checks for ctrl, for use with multiselect, copy, + paste, and such. On OS X it actually looks for cmd. +--]]--------------------------------------------------------- +function loveframes.IsCtrlDown() + if love._os == "OS X" then + return love.keyboard.isDown("lgui") or love.keyboard.isDown("rgui") + end + return love.keyboard.isDown("lctrl") or love.keyboard.isDown("rctrl") +end + +function loveframes.Color(s, a) + local r, g, b = string.match(s, '#?(%x%x)(%x%x)(%x%x)') + if r == nil then return end + return tonumber(r, 16) / 0xFF, tonumber(g, 16) / 0xFF, tonumber(b, 16) / 0xFF, a or 1 +end + +--[[--------------------------------------------------------- + - func: draw() + - desc: draws debug information +--]]--------------------------------------------------------- +function loveframes.DebugDraw() + local infox = 5 + local infoy = 40 + local topcol = {type = "None", children = {}, x = 0, y = 0, width = 0, height = 0} + local hoverobject = loveframes.hoverobject + --local objects = loveframes.GetAllObjects() + local version = loveframes.version + local stage = loveframes.stage + local basedir = loveframes.config["DIRECTORY"] + local loveversion = love._version + local fps = love.timer.getFPS() + local deltatime = love.timer.getDelta() + local font = loveframes.basicfontsmall + + if hoverobject then + topcol = hoverobject + end + + -- show frame docking zones + if topcol.type == "frame" then + for k, v in pairs(topcol.dockzones) do + love.graphics.setLineWidth(1) + love.graphics.setColor(255/255, 0, 0, 100/255) + love.graphics.rectangle("fill", v.x, v.y, v.width, v.height) + love.graphics.setColor(255/255, 0, 0, 255/255) + love.graphics.rectangle("line", v.x, v.y, v.width, v.height) + end + end + + -- outline the object that the mouse is hovering over + love.graphics.setColor(255/255, 204/255, 51/255, 255/255) + love.graphics.setLineWidth(2) + love.graphics.rectangle("line", topcol.x - 1, topcol.y - 1, topcol.width + 2, topcol.height + 2) + + -- draw main debug box + love.graphics.setFont(font) + love.graphics.setColor(0, 0, 0, 200/255) + love.graphics.rectangle("fill", infox, infoy, 200, 70) + love.graphics.setColor(255/255, 0, 0, 255/255) + love.graphics.print("Love Frames - Debug (" ..version.. " - " ..stage.. ")", infox + 5, infoy + 5) + love.graphics.setColor(255/255, 255/255, 255/255, 255/255) + love.graphics.print("LOVE Version: " ..loveversion, infox + 10, infoy + 20) + love.graphics.print("FPS: " ..fps, infox + 10, infoy + 30) + love.graphics.print("Delta Time: " ..deltatime, infox + 10, infoy + 40) + love.graphics.print("Total Objects: " ..loveframes.objectcount, infox + 10, infoy + 50) + + -- draw object information if needed + if topcol.type ~= "base" then + love.graphics.setColor(0, 0, 0, 200/255) + love.graphics.rectangle("fill", infox, infoy + 75, 200, 100) + love.graphics.setColor(255/255, 0, 0, 255/255) + love.graphics.print("Object Information", infox + 5, infoy + 80) + love.graphics.setColor(255/255, 255/255, 255/255, 255/255) + love.graphics.print("Type: " ..topcol.type, infox + 10, infoy + 95) + if topcol.children then + love.graphics.print("# of children: " .. #topcol.children, infox + 10, infoy + 105) + else + love.graphics.print("# of children: 0", infox + 10, infoy + 105) + end + if topcol.internals then + love.graphics.print("# of internals: " .. #topcol.internals, infox + 10, infoy + 115) + else + love.graphics.print("# of internals: 0", infox + 10, infoy + 115) + end + love.graphics.print("X: " ..topcol.x, infox + 10, infoy + 125) + love.graphics.print("Y: " ..topcol.y, infox + 10, infoy + 135) + love.graphics.print("Width: " ..topcol.width, infox + 10, infoy + 145) + love.graphics.print("Height: " ..topcol.height, infox + 10, infoy + 155) + end +end + +--return util + +---------- module end ---------- +end diff --git a/loveframes/license.txt b/loveframes/license.txt new file mode 100644 index 0000000..c71303f --- /dev/null +++ b/loveframes/license.txt @@ -0,0 +1,20 @@ +Copyright (c) 2014 Kenny Shields + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from +the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software in a +product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not +be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. diff --git a/loveframes/objects/base.lua b/loveframes/objects/base.lua new file mode 100644 index 0000000..67d8bda --- /dev/null +++ b/loveframes/objects/base.lua @@ -0,0 +1,1295 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- base object +local newobject = loveframes.NewObject("base", "loveframes_object_base") + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the element +--]]--------------------------------------------------------- +function newobject:initialize() + self.type = "base" + self.width, self.height = love.graphics.getDimensions() + self.internal = true + self.children = {} + self.internals = {} + +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + if loveframes.state ~= self.state then + return + end + + local width, height = love.graphics.getDimensions() + + if self.width ~= width then + self.width = width + end + + if self.height ~= height then + self.height = height + end + + local children = self.children + if children then + for k, v in ipairs(children) do + v:update(dt) + end + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:update(dt) + end + end +end + +--[[--------------------------------------------------------- + - func: draw() + - desc: draws the object +--]]--------------------------------------------------------- +function newobject:draw() + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + self:SetDrawOrder() + + local drawfunc = self.Draw or self.drawfunc + if drawfunc then + drawfunc(self) + end + + local children = self.children + if children then + for k, v in ipairs(children) do + v:draw() + end + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:draw() + end + end + + drawfunc = self.DrawOver or self.drawoverfunc + if drawfunc then + drawfunc(self) + end +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local children = self.children + if children then + for k, v in ipairs(children) do + v:mousepressed(x, y, button) + end + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:mousepressed(x, y, button) + end + end +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function newobject:mousereleased(x, y, button) + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local children = self.children + if children then + for k, v in ipairs(children) do + v:mousereleased(x, y, button) + end + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:mousereleased(x, y, button) + end + end +end + +--[[--------------------------------------------------------- + - func: wheelmoved(x, y) + - desc: called when the player moves a mouse wheel +--]]--------------------------------------------------------- +function newobject:wheelmoved(x, y) + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local children = self.children + if children then + for k, v in ipairs(children) do + v:wheelmoved(x, y) + end + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:wheelmoved(x, y) + end + end +end + +--[[--------------------------------------------------------- + - func: keypressed(key, isrepeat) + - desc: called when the player presses a key +--]]--------------------------------------------------------- +function newobject:keypressed(key, isrepeat) + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local children = self.children + if children then + for k, v in ipairs(children) do + v:keypressed(key, isrepeat) + end + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:keypressed(key, isrepeat) + end + end +end + +--[[--------------------------------------------------------- + - func: keyreleased(key) + - desc: called when the player releases a key +--]]--------------------------------------------------------- +function newobject:keyreleased(key) + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local children = self.children + if children then + for k, v in ipairs(children) do + v:keyreleased(key) + end + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:keyreleased(key) + end + end +end + + +--[[--------------------------------------------------------- + - func: textinput(text) + - desc: called when the user inputs text +--]]--------------------------------------------------------- +function newobject:textinput(text) + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local children = self.children + if children then + for k, v in ipairs(children) do + v:textinput(text) + end + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:textinput(text) + end + end +end + + +--[[--------------------------------------------------------- + - func: SetPos(x, y, center) + - desc: sets the object's position +--]]--------------------------------------------------------- +function newobject:SetPos(x, y, center) + + local base = loveframes.base + local parent = self.parent + + if center then + local width = self.width + local height = self.height + x = x - width/2 + y = y - height/2 + end + + if parent == base then + self.x = x + self.y = y + else + self.staticx = x + self.staticy = y + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetX(x, center) + - desc: sets the object's x position +--]]--------------------------------------------------------- +function newobject:SetX(x, center) + + local base = loveframes.base + local parent = self.parent + + if center then + local width = self.width + x = x - width/2 + end + + if parent == base then + self.x = x + else + self.staticx = x + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetY(y, center) + - desc: sets the object's y position +--]]--------------------------------------------------------- +function newobject:SetY(y, center) + + local base = loveframes.base + local parent = self.parent + + if center then + local height = self.height + y = y - height/2 + end + + if parent == base then + self.y = y + else + self.staticy = y + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetPos() + - desc: gets the object's position +--]]--------------------------------------------------------- +function newobject:GetPos() + + return self.x, self.y + +end + +--[[--------------------------------------------------------- + - func: GetX() + - desc: gets the object's x position +--]]--------------------------------------------------------- +function newobject:GetX() + + return self.x + +end + +--[[--------------------------------------------------------- + - func: GetY() + - desc: gets the object's y position +--]]--------------------------------------------------------- +function newobject:GetY() + + return self.y + +end + +--[[--------------------------------------------------------- + - func: GetStaticPos() + - desc: gets the object's static position +--]]--------------------------------------------------------- +function newobject:GetStaticPos() + + return self.staticx, self.staticy + +end + +--[[--------------------------------------------------------- + - func: GetStaticX() + - desc: gets the object's static x position +--]]--------------------------------------------------------- +function newobject:GetStaticX() + + return self.staticx + +end + +--[[--------------------------------------------------------- + - func: GetStaticY() + - desc: gets the object's static y position +--]]--------------------------------------------------------- +function newobject:GetStaticY() + + return self.staticy + +end + +--[[--------------------------------------------------------- + - func: Center() + - desc: centers the object in the game window or in + its parent if it has one +--]]--------------------------------------------------------- +function newobject:Center() + + local base = loveframes.base + local parent = self.parent + + if parent == base then + local width = love.graphics.getWidth() + local height = love.graphics.getHeight() + self.x = width/2 - self.width * (self.scalex or 1)/2 + self.y = height/2 - self.height * (self.scaley or 1)/2 + else + local width = parent.width + local height = parent.height + self.staticx = width/2 - self.width * (self.scalex or 1)/2 + self.staticy = height/2 - self.height * (self.scaley or 1)/2 + end + + return self + +end + +--[[--------------------------------------------------------- + - func: CenterX() + - desc: centers the object by its x value +--]]--------------------------------------------------------- +function newobject:CenterX() + + local base = loveframes.base + local parent = self.parent + + if parent == base then + local width = love.graphics.getWidth() + self.x = width/2 - self.width * (self.scalex or 1)/2 + else + local width = parent.width + self.staticx = width/2 - self.width * (self.scalex or 1)/2 + end + + return self + +end + +--[[--------------------------------------------------------- + - func: CenterY() + - desc: centers the object by its y value +--]]--------------------------------------------------------- +function newobject:CenterY() + + local base = loveframes.base + local parent = self.parent + + if parent == base then + local height = love.graphics.getHeight() + self.y = height/2 - self.height * (self.scaley or 1)/2 + else + local height = parent.height + self.staticy = height/2 - self.height * (self.scaley or 1)/2 + end + + return self + +end + +--[[--------------------------------------------------------- + - func: CenterWithinArea() + - desc: centers the object within the given area +--]]--------------------------------------------------------- +function newobject:CenterWithinArea(x, y, width, height) + + local selfwidth = self.width + local selfheight = self.height + + self.x = x + width/2 - selfwidth/2 + self.y = y + height/2 - selfheight/2 + + return self + +end + +--[[--------------------------------------------------------- + - func: SetSize(width, height, r1, r2) + - desc: sets the object's size +--]]--------------------------------------------------------- +function newobject:SetSize(width, height, r1, r2) + + if r1 then + self.width = self.parent.width * width + else + self.width = width + end + + if r2 then + self.height = self.parent.height * height + else + self.height = height + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetWidth(width, relative) + - desc: sets the object's width +--]]--------------------------------------------------------- +function newobject:SetWidth(width, relative) + + if relative then + self.width = self.parent.width * width + else + self.width = width + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetHeight(height, relative) + - desc: sets the object's height +--]]--------------------------------------------------------- +function newobject:SetHeight(height, relative) + + if relative then + self.height = self.parent.height * height + else + self.height = height + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetSize() + - desc: gets the object's size +--]]--------------------------------------------------------- +function newobject:GetSize() + + return self.width, self.height + +end + +--[[--------------------------------------------------------- + - func: GetWidth() + - desc: gets the object's width +--]]--------------------------------------------------------- +function newobject:GetWidth() + + return self.width + +end + +--[[--------------------------------------------------------- + - func: GetHeight() + - desc: gets the object's height +--]]--------------------------------------------------------- +function newobject:GetHeight() + + return self.height + +end + +--[[--------------------------------------------------------- + - func: SetVisible(bool) + - desc: sets the object's visibility +--]]--------------------------------------------------------- +function newobject:SetVisible(bool) + + local children = self.children + local internals = self.internals + + self.visible = bool + + if children then + for k, v in ipairs(children) do + v:SetVisible(bool) + end + end + + if internals then + for k, v in ipairs(internals) do + v:SetVisible(bool) + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetVisible() + - desc: gets the object's visibility +--]]--------------------------------------------------------- +function newobject:GetVisible() + + return self.visible + +end + +--[[--------------------------------------------------------- + - func: SetParent(parent) + - desc: sets the object's parent +--]]--------------------------------------------------------- +function newobject:SetParent(parent) + + local tparent = parent + local cparent = self.parent + local ptype = tparent.type + local stype = self.type + + if ptype ~= "frame" and ptype ~= "panel" and ptype ~= "list" then + return + end + + self:Remove() + self.parent = tparent + self:SetState(tparent.state) + + table.insert(tparent.children, self) + return self + +end + +--[[--------------------------------------------------------- + - func: GetParent() + - desc: gets the object's parent +--]]--------------------------------------------------------- +function newobject:GetParent() + + local parent = self.parent + return parent + +end + +--[[--------------------------------------------------------- + - func: Remove() + - desc: removes the object +--]]--------------------------------------------------------- +function newobject:Remove() + + local pinternals = self.parent.internals + local pchildren = self.parent.children + + if pinternals then + for k, v in ipairs(pinternals) do + if v == self then + table.remove(pinternals, k) + end + end + end + + if pchildren then + for k, v in ipairs(pchildren) do + if v == self then + table.remove(pchildren, k) + end + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetClickBounds(x, y, width, height) + - desc: sets a boundary box for the object's collision + detection +--]]--------------------------------------------------------- +function newobject:SetClickBounds(x, y, width, height) + + local internals = self.internals + local children = self.children + + self.clickbounds = {x = x, y = y, width = width, height = height} + + if internals then + for k, v in ipairs(internals) do + v:SetClickBounds(x, y, width, height) + end + end + + if children then + for k, v in ipairs(children) do + v:SetClickBounds(x, y, width, height) + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetClickBounds() + - desc: gets the boundary box for the object's collision + detection +--]]--------------------------------------------------------- +function newobject:GetClickBounds() + + return self.clickbounds + +end + +--[[--------------------------------------------------------- + - func: RemoveClickBounds() + - desc: removes the collision detection boundary for the + object +--]]--------------------------------------------------------- +function newobject:RemoveClickBounds() + + local internals = self.internals + local children = self.children + + self.clickbounds = nil + + if internals then + for k, v in ipairs(internals) do + v:RemoveClickBounds() + end + end + + if children then + for k, v in ipairs(children) do + v:RemoveClickBounds() + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: InClickBounds() + - desc: checks if the mouse is inside the object's + collision detection boundaries +--]]--------------------------------------------------------- +function newobject:InClickBounds() + + local x, y = love.mouse.getPosition() + local bounds = self.clickbounds + + if bounds then + local col = loveframes.BoundingBox(x, bounds.x, y, bounds.y, 1, bounds.width, 1, bounds.height) + return col + else + return false + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetBaseParent(object, t) + - desc: finds the object's base parent +--]]--------------------------------------------------------- +function newobject:GetBaseParent(t) + + local t = t or {} + local base = loveframes.base + local parent = self.parent + + if parent ~= base then + table.insert(t, parent) + parent:GetBaseParent(t) + end + + return t[#t] + +end + +--[[--------------------------------------------------------- + - func: CheckHover() + - desc: checks to see if the object should be in a + hover state +--]]--------------------------------------------------------- +function newobject:CheckHover() + + local x = self.x + local y = self.y + local width = self.width + local height = self.height + local mx, my = love.mouse.getPosition() + local selfcol = loveframes.BoundingBox(mx, x, my, y, 1, width, 1, height) + local collisioncount = loveframes.collisioncount + local curstate = loveframes.state + local state = self.state + local visible = self.visible + local type = self.type + local hoverobject = loveframes.hoverobject + + -- check if the mouse is colliding with the object + if state == curstate and visible then + local collide = self.collide + if selfcol and collide then + loveframes.collisioncount = collisioncount + 1 + local clickbounds = self.clickbounds + if clickbounds then + local cx = clickbounds.x + local cy = clickbounds.y + local cwidth = clickbounds.width + local cheight = clickbounds.height + local clickcol = loveframes.BoundingBox(mx, cx, my, cy, 1, cwidth, 1, cheight) + if clickcol then + table.insert(loveframes.collisions, self) + end + else + table.insert(loveframes.collisions, self) + end + end + end + + -- check if the object is being hovered + if hoverobject == self and type ~= "base" then + self.hover = true + else + self.hover = false + end + + local hover = self.hover + local calledmousefunc = self.calledmousefunc + + -- check for mouse enter and exit events + if hover then + loveframes.hover = true + if not calledmousefunc then + local on_mouse_enter = self.OnMouseEnter + if on_mouse_enter then + on_mouse_enter(self) + self.calledmousefunc = true + else + self.calledmousefunc = true + end + end + else + if calledmousefunc then + local on_mouse_exit = self.OnMouseExit + if on_mouse_exit then + on_mouse_exit(self) + self.calledmousefunc = false + else + self.calledmousefunc = false + end + end + end + +end + +--[[--------------------------------------------------------- + - func: GetHover() + - desc: return if the object is in a hover state or not +--]]--------------------------------------------------------- +function newobject:GetHover() + + return self.hover + +end + +--[[--------------------------------------------------------- + - func: GetChildren() + - desc: returns the object's children +--]]--------------------------------------------------------- +function newobject:GetChildren() + + local children = self.children + + if children then + return children + end + +end + +--[[--------------------------------------------------------- + - func: GetInternals() + - desc: returns the object's internals +--]]--------------------------------------------------------- +function newobject:GetInternals() + + local internals = self.internals + + if internals then + return internals + end + +end + + +--[[--------------------------------------------------------- + - func: IsTopList() + - desc: returns true if the object is the top most list + object or false if not +--]]--------------------------------------------------------- +function newobject:IsTopList() + + local cols = loveframes.GetCollisions() + local children = self:GetChildren() + local order = self.draworder + local top = true + local found = false + + local function IsChild(object) + local parents = object:GetParents() + for k, v in ipairs(parents) do + if v == self then + return true + end + end + return false + end + + for k, v in ipairs(cols) do + if v == self then + found = true + else + if v.draworder > order then + if IsChild(v) ~= true then + top = false + break + end + end + end + end + + if found == false then + top = false + end + + return top + +end + +--[[--------------------------------------------------------- + - func: IsTopChild() + - desc: returns true if the object is the top most child + in its parent's children table or false if not +--]]--------------------------------------------------------- +function newobject:IsTopChild() + + local children = self.parent.children + local num = #children + + if children[num] == self then + return true + else + return false + end + +end + +--[[--------------------------------------------------------- + - func: MoveToTop() + - desc: moves the object to the top of its parent's + children table +--]]--------------------------------------------------------- +function newobject:MoveToTop() + + local pchildren = self.parent.children + local pinternals = self.parent.internals + + local internal = false + + if pinternals then + for k, v in ipairs(pinternals) do + if v == self then + internal = true + end + end + end + + self:Remove() + + if internal then + table.insert(pinternals, self) + else + table.insert(pchildren, self) + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetDrawFunc() + - desc: sets the objects skin based draw function +--]]--------------------------------------------------------- +function newobject:SetDrawFunc() + local skins = loveframes.skins + local activeskin = skins[loveframes.config["ACTIVESKIN"]] + --local defaultskin = skins[loveframes.config["DEFAULTSKIN"]] + + local funcname = self.type + self.drawfunc = activeskin[funcname] -- or defaultskin[funcname] + + funcname = self.type .. "_over" + self.drawoverfunc = activeskin[funcname] -- or defaultskin[funcname] +end + +--[[--------------------------------------------------------- + - func: SetSkin(name) + - desc: sets the object's skin +--]]--------------------------------------------------------- +function newobject:SetSkin(name) + + local children = self.children + local internals = self.internals + + self.skin = name + + local selfskin = loveframes.skins[name] + + local funcname = self.type + + self.drawfunc = selfskin[funcname] + self.drawoverfunc = selfskin[funcname.."_over"] + + if children then + for k, v in ipairs(children) do + v:SetSkin(name) + end + end + + if internals then + for k, v in ipairs(internals) do + v:SetSkin(name) + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetSkin() + - desc: gets the object's skin +--]]--------------------------------------------------------- +function newobject:GetSkin() + + local skins = loveframes.skins + local skinindex = loveframes.config["ACTIVESKIN"] + local defaultskin = loveframes.config["DEFAULTSKIN"] + local selfskin = self.skin + local skin = skins[selfskin] or skins[skinindex] + + return skin + +end + +--[[--------------------------------------------------------- + - func: GetSkinName() + - desc: gets the name of the object's skin +--]]--------------------------------------------------------- +function newobject:GetSkinName() + + return self.skin + +end + +--[[--------------------------------------------------------- + - func: SetAlwaysUpdate(bool) + - desc: sets the object's skin +--]]--------------------------------------------------------- +function newobject:SetAlwaysUpdate(bool) + + self.alwaysupdate = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetAlwaysUpdate() + - desc: gets whether or not the object will always update +--]]--------------------------------------------------------- +function newobject:GetAlwaysUpdate() + + return self.alwaysupdate + +end + +--[[--------------------------------------------------------- + - func: SetRetainSize(bool) + - desc: sets whether or not the object should retain its + size when another object tries to resize it +--]]--------------------------------------------------------- +function newobject:SetRetainSize(bool) + + self.retainsize = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetRetainSize() + - desc: gets whether or not the object should retain its + size when another object tries to resize it +--]]--------------------------------------------------------- +function newobject:GetRetainSize() + + return self.retainsize + +end + +--[[--------------------------------------------------------- + - func: IsActive() + - desc: gets whether or not the object is active within + its parent's child table +--]]--------------------------------------------------------- +function newobject:IsActive() + + local parent = self.parent + local pchildren = parent.children + local valid = false + + for k, v in ipairs(pchildren) do + if v == self then + valid = true + end + end + + return valid + +end + +--[[--------------------------------------------------------- + - func: GetParents() + - desc: returns a table of the object's parents and its + sub-parents +--]]--------------------------------------------------------- +function newobject:GetParents() + + local function GetParents(object, t) + local t = t or {} + local type = object.type + local parent = object.parent + if type ~= "base" then + table.insert(t, parent) + GetParents(parent, t) + end + return t + end + + local parents = GetParents(self) + return parents + +end + +--[[--------------------------------------------------------- + - func: IsTopInternal() + - desc: returns true if the object is the top most + internal in its parent's internals table or + false if not +--]]--------------------------------------------------------- +function newobject:IsTopInternal() + + local parent = self.parent + local internals = parent.internals + local topitem = internals[#internals] + + if topitem ~= self then + return false + else + return true + end + +end + +--[[--------------------------------------------------------- + - func: IsInternal() + - desc: returns true if the object is internal or + false if not +--]]--------------------------------------------------------- +function newobject:IsInternal() + + return self.internal + +end + +--[[--------------------------------------------------------- + - func: GetType() + - desc: gets the type of the object +--]]--------------------------------------------------------- +function newobject:GetType() + + return self.type + +end + +--[[--------------------------------------------------------- + - func: SetDrawOrder() + - desc: sets the object's draw order +--]]--------------------------------------------------------- +function newobject:SetDrawOrder() + + loveframes.drawcount = loveframes.drawcount + 1 + self.draworder = loveframes.drawcount + return self + +end + +--[[--------------------------------------------------------- + - func: GetDrawOrder() + - desc: sets the object's draw order +--]]--------------------------------------------------------- +function newobject:GetDrawOrder() + + return self.draworder + +end + +--[[--------------------------------------------------------- + - func: SetProperty(name, value) + - desc: sets a property on the object +--]]--------------------------------------------------------- +function newobject:SetProperty(name, value) + + self[name] = value + return self + +end + +--[[--------------------------------------------------------- + - func: GetProperty(name) + - desc: gets the value of an object's property +--]]--------------------------------------------------------- +function newobject:GetProperty(name) + + return self[name] + +end + +--[[--------------------------------------------------------- + - func: IsInList() + - desc: checks to see if an object is in a list +--]]--------------------------------------------------------- +function newobject:IsInList() + + local parents = self:GetParents() + + for k, v in ipairs(parents) do + if v.type == "list" then + return true, v + end + end + + return false, false + +end + +--[[--------------------------------------------------------- + - func: SetState(name) + - desc: sets the object's state +--]]--------------------------------------------------------- +function newobject:SetState(name) + + local children = self.children + local internals = self.internals + + self.state = name + + if children then + for k, v in ipairs(children) do + v:SetState(name) + end + end + + if internals then + for k, v in ipairs(internals) do + v:SetState(name) + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetState() + - desc: gets the object's state +--]]--------------------------------------------------------- +function newobject:GetState() + + return self.state + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/button.lua b/loveframes/objects/button.lua new file mode 100644 index 0000000..b3877a6 --- /dev/null +++ b/loveframes/objects/button.lua @@ -0,0 +1,298 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- button object +local newobject = loveframes.NewObject("button", "loveframes_object_button", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "button" + self.text = "Button" + self.width = 80 + self.height = 25 + self.internal = false + self.down = false + self.clickable = true + self.enabled = true + self.toggleable = false + self.toggle = false + self.OnClick = nil + self.groupIndex = 0 + self.checked = false + + self:SetDrawFunc() +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 + + self:CheckHover() + + local hover = self.hover + local down = self.down + local downobject = loveframes.downobject + local parent = self.parent + local base = loveframes.base + local update = self.Update + + if not hover then + self.down = false + if downobject == self then + self.hover = true + end + else + if downobject == self then + self.down = true + 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 + + if update then + update(self, dt) + 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 + + if hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + self.down = true + loveframes.downobject = self + 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 hover = self.hover + local down = self.down + local clickable = self.clickable + local enabled = self.enabled + local onclick = self.OnClick + + if hover and down and clickable and button == 1 then + if enabled then + if self.groupIndex ~= 0 then + local baseparent = self.parent + if baseparent then + for k, v in ipairs(baseparent.children) do + if v.groupIndex then + if v.groupIndex == self.groupIndex then + v.checked = false + end + end + end + end + self.checked = true + end + if onclick then + onclick(self, x, y) + end + if self.toggleable then + local ontoggle = self.OnToggle + self.toggle = not self.toggle + if ontoggle then + ontoggle(self, self.toggle) + end + end + end + end + + self.down = false + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the object's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + self.text = text + return self + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the object's text +--]]--------------------------------------------------------- +function newobject:GetText() + + return self.text + +end + +--[[--------------------------------------------------------- + - func: SetImage(image) + - desc: adds an image to the object +--]]--------------------------------------------------------- +function newobject:SetImage(image) + + if type(image) == "string" then + self.image = love.graphics.newImage(image) + self.image:setFilter("nearest", "nearest") + else + self.image = image + end +end + +--[[--------------------------------------------------------- + - func: GetImage() + - desc: gets the object's image +--]]--------------------------------------------------------- +function newobject:GetImage() + + return self.image + +end + +--[[--------------------------------------------------------- + - func: SetClickable(bool) + - desc: sets whether the object can be clicked or not +--]]--------------------------------------------------------- +function newobject:SetClickable(bool) + + self.clickable = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetClickable(bool) + - desc: gets whether the object can be clicked or not +--]]--------------------------------------------------------- +function newobject:GetClickable() + + return self.clickable + +end + +--[[--------------------------------------------------------- + - func: SetClickable(bool) + - desc: sets whether or not the object is enabled +--]]--------------------------------------------------------- +function newobject:SetEnabled(bool) + + self.enabled = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetEnabled() + - desc: gets whether or not the object is enabled +--]]--------------------------------------------------------- +function newobject:GetEnabled() + + return self.enabled + +end + +--[[--------------------------------------------------------- + - func: GetDown() + - desc: gets whether or not the object is currently + being pressed +--]]--------------------------------------------------------- +function newobject:GetDown() + + return self.down + +end + +--[[--------------------------------------------------------- + - func: SetToggleable(bool) + - desc: sets whether or not the object is toggleable +--]]--------------------------------------------------------- +function newobject:SetToggleable(bool) + + self.toggleable = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetToggleable() + - desc: gets whether or not the object is toggleable +--]]--------------------------------------------------------- +function newobject:GetToggleable() + + return self.toggleable + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/checkbox.lua b/loveframes/objects/checkbox.lua new file mode 100644 index 0000000..14d4477 --- /dev/null +++ b/loveframes/objects/checkbox.lua @@ -0,0 +1,429 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- checkbox object +local newobject = loveframes.NewObject("checkbox", "loveframes_object_checkbox", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + self.type = "checkbox" + self.width = 0 + self.height = 0 + self.boxwidth = 16 + self.boxheight = 16 + self.font = loveframes.basicfont + self.checked = false + self.lastvalue = false + self.internal = false + self.down = true + self.enabled = true + self.internals = {} + self.OnChanged = nil + self.groupIndex = 0 + + self:SetDrawFunc() +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 + + self:CheckHover() + + local hover = self.hover + local internals = self.internals + local boxwidth = self.boxwidth + local boxheight = self.boxheight + local parent = self.parent + local base = loveframes.base + local update = self.Update + + if not hover then + self.down = false + else + if loveframes.downobject == self then + self.down = true + end + end + + if not self.down and loveframes.downobject == self then + self.hover = true + end + + -- move to parent if there is a parent + if parent ~= base and parent.type ~= "list" then + self.x = self.parent.x + self.staticx + self.y = self.parent.y + self.staticy + end + + if internals[1] then + self.width = boxwidth + 5 + internals[1].width + if internals[1].height == boxheight then + self.height = boxheight + else + if internals[1].height > boxheight then + self.height = internals[1].height + else + self.height = boxheight + end + end + else + self.width = boxwidth + self.height = boxheight + end + + for k, v in ipairs(internals) do + v:update(dt) + end + + if update then + update(self, dt) + 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 + + if hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + self.down = true + loveframes.downobject = self + 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 hover = self.hover + local down = self.down + local enabled = self.enabled + local checked = self.checked + local onchanged = self.OnChanged + + if hover and down and enabled and button == 1 then + if checked then + if self.groupIndex == 0 then self.checked = false end + else + if self.groupIndex ~= 0 then + local baseparent = self.parent + if baseparent then + for k, v in ipairs(baseparent.children) do + if v.groupIndex then + if v.groupIndex == self.groupIndex then + v.checked = false + end + end + end + end + end + self.checked = true + end + if onchanged then + onchanged(self, self.checked) + end + end + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the object's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + local boxwidth = self.boxwidth + local boxheight = self.boxheight + + if text ~= "" then + self.internals = {} + local textobject = loveframes.Create("text") + local skin = loveframes.GetActiveSkin() + if not skin then + skin = loveframes.config["DEFAULTSKIN"] + end + local directives = skin.directives + if directives then + local default_color = directives.checkbox_text_default_color + local default_shadowcolor = directives.checkbox_text_default_shadowcolor + local default_font = directives.checkbox_text_default_font + if default_color then + textobject.defaultcolor = default_color + end + if default_shadowcolor then + textobject.shadowcolor = default_shadowcolor + end + if default_font then + self.font = default_font + end + end + textobject:Remove() + textobject.parent = self + textobject.state = self.state + textobject.collide = false + textobject:SetFont(self.font) + textobject:SetText(text) + textobject.Update = function(object, dt) + if object.height > boxheight then + object:SetPos(boxwidth + 5, 0) + else + object:SetPos(boxwidth + 5, boxheight/2 - object.height/2) + end + end + table.insert(self.internals, textobject) + else + self.width = boxwidth + self.height = boxheight + self.internals = {} + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the object's text +--]]--------------------------------------------------------- +function newobject:GetText() + + local internals = self.internals + local text = internals[1] + + if text then + return text.text + else + return false + end + +end + +--[[--------------------------------------------------------- + - func: SetSize(width, height, r1, r2) + - desc: sets the object's size +--]]--------------------------------------------------------- +function newobject:SetSize(width, height, r1, r2) + + if r1 then + self.boxwidth = self.parent.width * width + else + self.boxwidth = width + end + + if r2 then + self.boxheight = self.parent.height * height + else + self.boxheight = height + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetWidth(width, relative) + - desc: sets the object's width +--]]--------------------------------------------------------- +function newobject:SetWidth(width, relative) + + if relative then + self.boxwidth = self.parent.width * width + else + self.boxwidth = width + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetHeight(height, relative) + - desc: sets the object's height +--]]--------------------------------------------------------- +function newobject:SetHeight(height, relative) + + if relative then + self.boxheight = self.parent.height * height + else + self.boxheight = height + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetChecked(bool) + - desc: sets whether the object is checked or not +--]]--------------------------------------------------------- +function newobject:SetChecked(bool) + + local onchanged = self.OnChanged + + self.checked = bool + + if onchanged then + onchanged(self) + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetChecked() + - desc: gets whether the object is checked or not +--]]--------------------------------------------------------- +function newobject:GetChecked() + + return self.checked + +end + +--[[--------------------------------------------------------- + - func: SetFont(font) + - desc: sets the font of the object's text +--]]--------------------------------------------------------- +function newobject:SetFont(font) + + local internals = self.internals + local text = internals[1] + + self.font = font + + if text then + text:SetFont(font) + end + + return self + +end + +--[[--------------------------------------------------------- + - func: newobject:GetFont() + - desc: gets the font of the object's text +--]]--------------------------------------------------------- +function newobject:GetFont() + + return self.font + +end + +--[[--------------------------------------------------------- + - func: newobject:GetBoxHeight() + - desc: gets the object's box size +--]]--------------------------------------------------------- +function newobject:GetBoxSize() + + return self.boxwidth, self.boxheight + +end + +--[[--------------------------------------------------------- + - func: newobject:GetBoxWidth() + - desc: gets the object's box width +--]]--------------------------------------------------------- +function newobject:GetBoxWidth() + + return self.boxwidth + +end + +--[[--------------------------------------------------------- + - func: newobject:GetBoxHeight() + - desc: gets the object's box height +--]]--------------------------------------------------------- +function newobject:GetBoxHeight() + + return self.boxheight + +end + +--[[--------------------------------------------------------- + - func: SetClickable(bool) + - desc: sets whether or not the object is enabled +--]]--------------------------------------------------------- +function newobject:SetEnabled(bool) + + self.enabled = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetEnabled() + - desc: gets whether or not the object is enabled +--]]--------------------------------------------------------- +function newobject:GetEnabled() + + return self.enabled + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/collapsiblecategory.lua b/loveframes/objects/collapsiblecategory.lua new file mode 100644 index 0000000..fdd8d6e --- /dev/null +++ b/loveframes/objects/collapsiblecategory.lua @@ -0,0 +1,328 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- collapsiblecategory object +local newobject = loveframes.NewObject("collapsiblecategory", "loveframes_object_collapsiblecategory", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "collapsiblecategory" + self.text = "Category" + self.width = 200 + self.height = 25 + self.closedheight = 25 + self.padding = 5 + self.internal = false + self.open = false + self.down = false + self.children = {} + self.OnOpenedClosed = nil + + self:SetDrawFunc() +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 open = self.open + local children = self.children + local curobject = children[1] + local parent = self.parent + local base = loveframes.base + local update = self.Update + + self:CheckHover() + + -- move to parent if there is a parent + if parent ~= base and parent.type ~= "list" then + self.x = self.parent.x + self.staticx + self.y = self.parent.y + self.staticy + end + + if open and curobject then + curobject:SetWidth(self.width - self.padding * 2) + curobject:update(dt) + elseif not open and curobject then + if curobject:GetVisible() then + curobject:SetVisible(false) + end + end + + if update then + update(self, dt) + 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 open = self.open + local children = self.children + local curobject = children[1] + + if hover then + local col = loveframes.BoundingBox(self.x, x, self.y, y, self.width, 1, self.closedheight, 1) + if button == 1 and col then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + self.down = true + loveframes.downobject = self + end + end + + if open and curobject then + curobject: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 hover = self.hover + local down = self.down + local clickable = self.clickable + local enabled = self.enabled + local open = self.open + local col = loveframes.BoundingBox(self.x, x, self.y, y, self.width, 1, self.closedheight, 1) + local children = self.children + local curobject = children[1] + + if hover and col and down and button == 1 then + if open then + self:SetOpen(false) + else + self:SetOpen(true) + end + self.down = false + end + + if open and curobject then + curobject:mousereleased(x, y, button) + end + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the object's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + self.text = text + return self + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the object's text +--]]--------------------------------------------------------- +function newobject:GetText() + + return self.text + +end + +--[[--------------------------------------------------------- + - func: SetObject(object) + - desc: sets the category's object +--]]--------------------------------------------------------- +function newobject:SetObject(object) + + local children = self.children + local curobject = children[1] + + if curobject then + curobject:Remove() + self.children = {} + end + + object:Remove() + object.parent = self + object:SetState(self.state) + object:SetWidth(self.width - self.padding*2) + object:SetPos(self.padding, self.closedheight + self.padding) + table.insert(self.children, object) + + return self + +end + +--[[--------------------------------------------------------- + - func: SetObject(object) + - desc: sets the category's object +--]]--------------------------------------------------------- +function newobject:GetObject() + + local children = self.children + local curobject = children[1] + + if curobject then + return curobject + else + return false + end + +end + +--[[--------------------------------------------------------- + - func: SetSize(width, height, relative) + - desc: sets the object's size +--]]--------------------------------------------------------- +function newobject:SetSize(width, height, relative) + + if relative then + self.width = self.parent.width * width + else + self.width = width + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetHeight(height) + - desc: sets the object's height +--]]--------------------------------------------------------- +function newobject:SetHeight(height) + + return self + +end + +--[[--------------------------------------------------------- + - func: SetClosedHeight(height) + - desc: sets the object's closed height +--]]--------------------------------------------------------- +function newobject:SetClosedHeight(height) + + self.closedheight = height + return self + +end + +--[[--------------------------------------------------------- + - func: GetClosedHeight() + - desc: gets the object's closed height +--]]--------------------------------------------------------- +function newobject:GetClosedHeight() + + return self.closedheight + +end + +--[[--------------------------------------------------------- + - func: SetOpen(bool) + - desc: sets whether the object is opened or closed +--]]--------------------------------------------------------- +function newobject:SetOpen(bool) + + local children = self.children + local curobject = children[1] + local closedheight = self.closedheight + local padding = self.padding + local onopenedclosed = self.OnOpenedClosed + + self.open = bool + + if not bool then + self.height = closedheight + if curobject then + local curobjectheight = curobject.height + curobject:SetVisible(false) + end + else + if curobject then + local curobjectheight = curobject.height + self.height = closedheight + padding * 2 + curobjectheight + curobject:SetVisible(true) + end + end + + -- call the on opened closed callback if it exists + if onopenedclosed then + onopenedclosed(self) + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetOpen() + - desc: gets whether the object is opened or closed +--]]--------------------------------------------------------- +function newobject:GetOpen() + + return self.open + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/columnlist.lua b/loveframes/objects/columnlist.lua new file mode 100644 index 0000000..5872f92 --- /dev/null +++ b/loveframes/objects/columnlist.lua @@ -0,0 +1,984 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- columnlist object +local newobject = loveframes.NewObject("columnlist", "loveframes_object_columnlist", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: intializes the element +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "columnlist" + self.width = 300 + self.height = 100 + self.defaultcolumnwidth = 100 + self.columnheight = 16 + self.buttonscrollamount = 200 + self.mousewheelscrollamount = 1500 + self.autoscroll = false + self.dtscrolling = true + self.internal = false + self.selectionenabled = true + self.multiselect = false + self.startadjustment = false + self.canresizecolumns = true + self.children = {} + self.internals = {} + self.resizecolumn = nil + self.OnRowClicked = nil + self.OnRowRightClicked = nil + self.OnRowSelected = nil + self.OnScroll = nil + + local list = loveframes.objects["columnlistarea"]:new(self) + table.insert(self.internals, list) + + self:SetDrawFunc() +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 children = self.children + local internals = self.internals + local update = self.Update + + self:CheckHover() + + -- 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 + + for k, v in ipairs(internals) do + v:update(dt) + end + + for k, v in ipairs(children) do + v.columnid = k + v:update(dt) + end + + self.startadjustment = false + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: draw() + - desc: draws the object +--]]--------------------------------------------------------- +function newobject:draw() + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local vbody = self.internals[1]:GetVerticalScrollBody() + local hbody = self.internals[1]:GetHorizontalScrollBody() + local width = self.width + local height = self.height + + if vbody then + width = width - vbody.width + end + + if hbody then + height = height - hbody.height + end + + local stencilfunc = function() + love.graphics.rectangle("fill", self.x, self.y, width, height) + end + + -- set the object's draw order + self:SetDrawOrder() + + local drawfunc = self.Draw or self.drawfunc + if drawfunc then + drawfunc(self) + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:draw() + end + end + + love.graphics.stencil(stencilfunc) + love.graphics.setStencilTest("greater", 0) + + local children = self.children + if children then + for k, v in ipairs(children) do + v:draw() + end + end + + local drawfunc = self.DrawOver or self.drawoverfunc + if drawfunc then + drawfunc(self) + end + love.graphics.setStencilTest() + +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 children = self.children + local internals = self.internals + + if hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent: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 children = self.children + local internals = self.internals + + 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: PositionColumns() + - desc: positions the object's columns +--]]--------------------------------------------------------- +function newobject:PositionColumns() + + local x = 0 + + for k, v in ipairs(self.children) do + v:SetPos(x, 0) + x = x + v.width + end + + return self + +end + +--[[--------------------------------------------------------- + - func: AddColumn(name) + - desc: gives the object a new column with the specified + name +--]]--------------------------------------------------------- +function newobject:AddColumn(name) + + local internals = self.internals + local list = internals[1] + local width = self.width + local height = self.height + + loveframes.objects["columnlistheader"]:new(name, self) + self:PositionColumns() + + list:SetSize(width, height) + list:SetPos(0, 0) + + return self + +end + +--[[--------------------------------------------------------- + - func: AddRow(...) + - desc: adds a row of data to the object's list +--]]--------------------------------------------------------- +function newobject:AddRow(...) + + local arg = {...} + local internals = self.internals + local list = internals[1] + + list:AddRow(arg) + return self + +end + +--[[--------------------------------------------------------- + - func: Getchildrenize() + - desc: gets the size of the object's children +--]]--------------------------------------------------------- +function newobject:GetColumnSize() + + local children = self.children + local numchildren = #self.children + + if numchildren > 0 then + local column = self.children[1] + local colwidth = column.width + local colheight = column.height + return colwidth, colheight + else + return 0, 0 + end + +end + +--[[--------------------------------------------------------- + - func: SetSize(width, height, r1, r2) + - desc: sets the object's size +--]]--------------------------------------------------------- +function newobject:SetSize(width, height, r1, r2) + + local internals = self.internals + local list = internals[1] + + if r1 then + self.width = self.parent.width * width + else + self.width = width + end + + if r2 then + self.height = self.parent.height * height + else + self.height = height + end + + self:PositionColumns() + + list:SetSize(width, height) + list:SetPos(0, 0) + list:CalculateSize() + list:RedoLayout() + + return self + +end + +--[[--------------------------------------------------------- + - func: SetWidth(width, relative) + - desc: sets the object's width +--]]--------------------------------------------------------- +function newobject:SetWidth(width, relative) + + local internals = self.internals + local list = internals[1] + + if relative then + self.width = self.parent.width * width + else + self.width = width + end + + self:PositionColumns() + + list:SetSize(width) + list:SetPos(0, 0) + list:CalculateSize() + list:RedoLayout() + + return self + +end + +--[[--------------------------------------------------------- + - func: SetHeight(height, relative) + - desc: sets the object's height +--]]--------------------------------------------------------- +function newobject:SetHeight(height, relative) + + local internals = self.internals + local list = internals[1] + + if relative then + self.height = self.parent.height * height + else + self.height = height + end + + self:PositionColumns() + + list:SetSize(height) + list:SetPos(0, 0) + list:CalculateSize() + list:RedoLayout() + + return self + +end + +--[[--------------------------------------------------------- + - func: SetMaxColorIndex(num) + - desc: sets the object's max color index for + alternating row colors +--]]--------------------------------------------------------- +function newobject:SetMaxColorIndex(num) + + local internals = self.internals + local list = internals[1] + + list.colorindexmax = num + return self + +end + +--[[--------------------------------------------------------- + - func: Clear() + - desc: removes all items from the object's list +--]]--------------------------------------------------------- +function newobject:Clear() + + local internals = self.internals + local list = internals[1] + + list:Clear() + return self + +end + +--[[--------------------------------------------------------- + - func: SetAutoScroll(bool) + - desc: sets whether or not the list's scrollbar should + auto scroll to the bottom when a new object is + added to the list +--]]--------------------------------------------------------- +function newobject:SetAutoScroll(bool) + + local internals = self.internals + local list = internals[1] + local scrollbar = list:GetScrollBar() + + self.autoscroll = bool + + if list then + if scrollbar then + scrollbar.autoscroll = bool + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetButtonScrollAmount(speed) + - desc: sets the scroll amount of the object's scrollbar + buttons +--]]--------------------------------------------------------- +function newobject:SetButtonScrollAmount(amount) + + self.buttonscrollamount = amount + self.internals[1].buttonscrollamount = amount + return self + +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 + self.internals[1].mousewheelscrollamount = amount + return self + +end + +--[[--------------------------------------------------------- + - func: GetMouseWheelScrollAmount() + - desc: gets the scroll amount of the mouse wheel +--]]--------------------------------------------------------- +function newobject:GetButtonScrollAmount() + + return self.mousewheelscrollamount + +end + +--[[--------------------------------------------------------- + - func: SetColumnHeight(height) + - desc: sets the height of the object's columns +--]]--------------------------------------------------------- +function newobject:SetColumnHeight(height) + + local children = self.children + local internals = self.internals + local list = internals[1] + + self.columnheight = height + + for k, v in ipairs(children) do + v:SetHeight(height) + end + + list:CalculateSize() + list:RedoLayout() + return self + +end + +--[[--------------------------------------------------------- + - func: SetDTScrolling(bool) + - desc: sets whether or not the object should use delta + time when scrolling +--]]--------------------------------------------------------- +function newobject:SetDTScrolling(bool) + + self.dtscrolling = bool + self.internals[1].dtscrolling = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetDTScrolling() + - desc: gets whether or not the object should use delta + time when scrolling +--]]--------------------------------------------------------- +function newobject:GetDTScrolling() + + return self.dtscrolling + +end + +--[[--------------------------------------------------------- + - func: SelectRow(row, ctrl) + - desc: selects the specfied row in the object's list + of rows +--]]--------------------------------------------------------- +function newobject:SelectRow(row, ctrl) + + local selectionenabled = self.selectionenabled + + if not selectionenabled then + return + end + + local list = self.internals[1] + local children = list.children + local multiselect = self.multiselect + local onrowselected = self.OnRowSelected + + for k, v in ipairs(children) do + if v == row then + if v.selected and ctrl then + v.selected = false + else + v.selected = true + if onrowselected then + onrowselected(self, row, row:GetColumnData()) + end + end + elseif v ~= row then + if not (multiselect and ctrl) then + v.selected = false + end + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: DeselectRow(row) + - desc: deselects the specfied row in the object's list + of rows +--]]--------------------------------------------------------- +function newobject:DeselectRow(row) + + row.selected = false + return self + +end + +--[[--------------------------------------------------------- + - func: GetSelectedRows() + - desc: gets the object's selected rows +--]]--------------------------------------------------------- +function newobject:GetSelectedRows() + + local rows = {} + local list = self.internals[1] + local children = list.children + + for k, v in ipairs(children) do + if v.selected then + table.insert(rows, v) + end + end + + return rows + +end + +--[[--------------------------------------------------------- + - func: SetSelectionEnabled(bool) + - desc: sets whether or not the object's rows can be + selected +--]]--------------------------------------------------------- +function newobject:SetSelectionEnabled(bool) + + self.selectionenabled = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetSelectionEnabled() + - desc: gets whether or not the object's rows can be + selected +--]]--------------------------------------------------------- +function newobject:GetSelectionEnabled() + + return self.selectionenabled + +end + +--[[--------------------------------------------------------- + - func: SetMultiselectEnabled(bool) + - desc: sets whether or not the object can have more + than one row selected +--]]--------------------------------------------------------- +function newobject:SetMultiselectEnabled(bool) + + self.multiselect = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetMultiselectEnabled() + - desc: gets whether or not the object can have more + than one row selected +--]]--------------------------------------------------------- +function newobject:GetMultiselectEnabled() + + return self.multiselect + +end + +--[[--------------------------------------------------------- + - func: RemoveColumn(id) + - desc: removes a column +--]]--------------------------------------------------------- +function newobject:RemoveColumn(id) + + local children = self.children + + for k, v in ipairs(children) do + if k == id then + v:Remove() + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetColumnName(id, name) + - desc: sets a column's name +--]]--------------------------------------------------------- +function newobject:SetColumnName(id, name) + + local children = self.children + + for k, v in ipairs(children) do + if k == id then + v.name = name + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetColumnName(id) + - desc: gets a column's name +--]]--------------------------------------------------------- +function newobject:GetColumnName(id) + + local children = self.children + + for k, v in ipairs(children) do + if k == id then + return v.name + end + end + + return false + +end + +--[[--------------------------------------------------------- + - func: SizeToChildren(max) + - desc: sizes the object to match the combined height + of its children + - note: Credit to retupmoc258, the original author of + this method. This version has a few slight + modifications. +--]]--------------------------------------------------------- +function newobject:SizeToChildren(max) + + local oldheight = self.height + local list = self.internals[1] + local listchildren = list.children + local children = self.children + local width = self.width + local buf = children[1].height + local h = listchildren[1].height + local c = #listchildren + local height = buf + h*c + + if max then + height = math.min(max, oldheight) + end + + self:SetSize(width, height) + self:PositionColumns() + return self + +end + +--[[--------------------------------------------------------- + - func: RemoveRow(id) + - desc: removes a row from the object's list +--]]--------------------------------------------------------- +function newobject:RemoveRow(id) + + local list = self.internals[1] + local listchildren = list.children + local row = listchildren[id] + + if row then + row:Remove() + end + + list:CalculateSize() + list:RedoLayout() + return self + +end + +--[[--------------------------------------------------------- + - func: SetCellText(text, rowid, columnid) + - desc: sets a cell's text +--]]--------------------------------------------------------- +function newobject:SetCellText(text, rowid, columnid) + + local list = self.internals[1] + local listchildren = list.children + local row = listchildren[rowid] + + if row and row.columndata[columnid]then + row.columndata[columnid] = text + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetCellText(rowid, columnid) + - desc: gets a cell's text +--]]--------------------------------------------------------- +function newobject:GetCellText(rowid, columnid) + + local row = self.internals[1].children[rowid] + + if row and row.columndata[columnid] then + return row.columndata[columnid] + else + return false + end + +end + +--[[--------------------------------------------------------- + - func: SetRowColumnData(rowid, columndata) + - desc: sets the columndata of the specified row +--]]--------------------------------------------------------- +function newobject:SetRowColumnData(rowid, columndata) + + local list = self.internals[1] + local listchildren = list.children + local row = listchildren[rowid] + + if row then + for k, v in ipairs(columndata) do + row.columndata[k] = tostring(v) + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetTotalColumnWidth() + - desc: gets the combined width of the object's columns +--]]--------------------------------------------------------- +function newobject:GetTotalColumnWidth() + + local width = 0 + + for k, v in ipairs(self.children) do + width = width + v.width + end + + return width + +end + +--[[--------------------------------------------------------- + - func: SetColumnWidth(id, width) + - desc: sets the width of the specified column +--]]--------------------------------------------------------- +function newobject:SetColumnWidth(id, width) + + local column = self.children[id] + if column then + column.width = width + local x = 0 + for k, v in ipairs(self.children) do + v:SetPos(x) + x = x + v.width + end + self.internals[1]:CalculateSize() + self.internals[1]:RedoLayout() + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetColumnWidth(id) + - desc: gets the width of the specified column +--]]--------------------------------------------------------- +function newobject:GetColumnWidth(id) + + local column = self.children[id] + if column then + return column.width + end + + return false + +end + +--[[--------------------------------------------------------- + - func: ResizeColumns() + - desc: resizes the object's columns to fit within the + width of the object's list area +--]]--------------------------------------------------------- +function newobject:ResizeColumns() + + local children = self.children + local width = 0 + local vbody = self.internals[1]:GetVerticalScrollBody() + + if vbody then + width = (self:GetWidth() - vbody:GetWidth())/#children + else + width = self:GetWidth()/#children + end + + for k, v in ipairs(children) do + v:SetWidth(width) + self:PositionColumns() + self.internals[1]:CalculateSize() + self.internals[1]:RedoLayout() + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetDefaultColumnWidth(width) + - desc: sets the object's default column width +--]]--------------------------------------------------------- +function newobject:SetDefaultColumnWidth(width) + + self.defaultcolumnwidth = width + return self + +end + +--[[--------------------------------------------------------- + - func: GetDefaultColumnWidth() + - desc: gets the object's default column width +--]]--------------------------------------------------------- +function newobject:GetDefaultColumnWidth() + + return self.defaultcolumnwidth + +end + +--[[--------------------------------------------------------- + - func: SetColumnResizeEnabled(bool) + - desc: sets whether or not the object's columns can + be resized +--]]--------------------------------------------------------- +function newobject:SetColumnResizeEnabled(bool) + + self.canresizecolumns = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetColumnResizeEnabled() + - desc: gets whether or not the object's columns can + be resized +--]]--------------------------------------------------------- +function newobject:GetColumnResizeEnabled() + + return self.canresizecolumns + +end + +--[[--------------------------------------------------------- + - func: SizeColumnToData(columnid) + - desc: sizes a column to the width of its largest data + string +--]]--------------------------------------------------------- +function newobject:SizeColumnToData(columnid) + + local column = self.children[columnid] + local list = self.internals[1] + local largest = 0 + + for k, v in ipairs(list.children) do + local width = v:GetFont():getWidth(self:GetCellText(k, columnid)) + if width > largest then + largest = width + v.textx + end + end + + if largest <= 0 then + largest = 10 + end + + self:SetColumnWidth(columnid, largest) + return self + +end + +--[[--------------------------------------------------------- + - func: SetColumnOrder(curid, newid) + - desc: sets the order of the specified column +--]]--------------------------------------------------------- +function newobject:SetColumnOrder(curid, newid) + + local column = self.children[curid] + local totalcolumns = #self.children + + if column and totalcolumns > 1 and newid <= totalcolumns and newid >= 1 then + column:Remove() + table.insert(self.children, newid, column) + self:PositionColumns() + end + + return self + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/form.lua b/loveframes/objects/form.lua new file mode 100644 index 0000000..437de01 --- /dev/null +++ b/loveframes/objects/form.lua @@ -0,0 +1,303 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- form object +local newobject = loveframes.NewObject("form", "loveframes_object_form", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "form" + self.name = "Form" + self.layout = "vertical" + self.width = 200 + self.height = 50 + self.padding = 5 + self.spacing = 5 + self.topmargin = 12 + self.internal = false + self.children = {} + + self:SetDrawFunc() +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 children = self.children + local parent = self.parent + local base = loveframes.base + local update = self.Update + + -- move to parent if there is a parent + if parent ~= base and parent.type ~= "list" then + self.x = self.parent.x + self.staticx + self.y = self.parent.y + self.staticy + end + + self:CheckHover() + + for k, v in ipairs(children) do + v:update(dt) + end + + if update then + update(self, dt) + 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 children = self.children + local hover = self.hover + + if hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + 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 + local children = self.children + + if not visible then + return + end + + for k, v in ipairs(children) do + v:mousereleased(x, y, button) + end + +end + +--[[--------------------------------------------------------- + - func: AddItem(object) + - desc: adds an item to the object +--]]--------------------------------------------------------- +function newobject:AddItem(object) + + local objtype = object.type + if objtype == "frame" then + return + end + + local children = self.children + local state = self.state + + object:Remove() + object.parent = self + object:SetState(state) + + table.insert(children, object) + self:LayoutObjects() + + return self + +end + +--[[--------------------------------------------------------- + - func: RemoveItem(object or number) + - desc: removes an item from the object +--]]--------------------------------------------------------- +function newobject:RemoveItem(data) + + local dtype = type(data) + + if dtype == "number" then + local children = self.children + local item = children[data] + if item then + item:Remove() + end + else + data:Remove() + end + + self:LayoutObjects() + return self + +end + +--[[--------------------------------------------------------- + - func: LayoutObjects() + - desc: positions the object's children and calculates + a new size for the object +--]]--------------------------------------------------------- +function newobject:LayoutObjects() + + local layout = self.layout + local padding = self.padding + local spacing = self.spacing + local topmargin = self.topmargin + local children = self.children + local width = padding * 2 + local height = padding * 2 + topmargin + local x = padding + local y = padding + topmargin + + if layout == "vertical" then + local largest_width = 0 + for k, v in ipairs(children) do + v.staticx = x + v.staticy = y + y = y + v.height + spacing + height = height + v.height + spacing + if v.width > largest_width then + largest_width = v.width + end + end + height = height - spacing + self.width = width + largest_width + self.height = height + elseif layout == "horizontal" then + local largest_height = 0 + for k, v in ipairs(children) do + v.staticx = x + v.staticy = y + x = x + v.width + spacing + width = width + v.width + spacing + if v.height > largest_height then + largest_height = v.height + end + end + width = width - spacing + self.width = width + self.height = height + largest_height + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetLayoutType(ltype) + - desc: sets the object's layout type +--]]--------------------------------------------------------- +function newobject:SetLayoutType(ltype) + + self.layout = ltype + return self + +end + +--[[--------------------------------------------------------- + - func: GetLayoutType() + - desc: gets the object's layout type +--]]--------------------------------------------------------- +function newobject:GetLayoutType() + + return self.layout + +end + +--[[--------------------------------------------------------- + - func: SetTopMargin(margin) + - desc: sets the margin between the top of the object + and its children +--]]--------------------------------------------------------- +function newobject:SetTopMargin(margin) + + self.topmargin = margin + return self + +end + +--[[--------------------------------------------------------- + - func: GetTopMargin() + - desc: gets the margin between the top of the object + and its children +--]]--------------------------------------------------------- +function newobject:GetTopMargin() + + return self.topmargin + +end + +--[[--------------------------------------------------------- + - func: SetName(name) + - desc: sets the object's name +--]]--------------------------------------------------------- +function newobject:SetName(name) + + self.name = name + return self + +end + +--[[--------------------------------------------------------- + - func: GetName() + - desc: gets the object's name +--]]--------------------------------------------------------- +function newobject:GetName() + + return self.name + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/frame.lua b/loveframes/objects/frame.lua new file mode 100644 index 0000000..126cd7e --- /dev/null +++ b/loveframes/objects/frame.lua @@ -0,0 +1,1111 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- frame object +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.resizex = 0 + self.resizey = 0 + self.resizexmod = 0 + self.resizeymod = 0 + self.resizewidth = 0 + self.resizeheight = 0 + self.minwidth = 100 + self.minheight = 30 + self.maxwidth = 500 + self.maxheight = 500 + 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.resizing = false + self.canresize = false + self.alwaysontop = false + self.internals = {} + self.children = {} + self.icon = nil + self.OnClose = nil + self.OnDock = nil + self.OnResize = 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 + if onclose then + local ret = onclose(object.parent) + + if ret ~= false then + object.parent:Remove() + end + else + object.parent:Remove() + end + end + + table.insert(self.internals, close) + + self:SetDrawFunc() +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 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 + local width = self.width + local height = self.height + 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.RectangleCollisionCheck(self.dockzones.bottom, v.dockzones.top) + local botcol = loveframes.RectangleCollisionCheck(self.dockzones.top, v.dockzones.bottom) + local leftcol = loveframes.RectangleCollisionCheck(self.dockzones.right, v.dockzones.left) + local rightcol = loveframes.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 + -- FIXME: object? + -- 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 + elseif self.resizing then + local width = self.width + local height = self.height + if self.resize_mode == "top_left" then + local new_width = self.resizewidth + (self.resizex - mx) + local new_height = self.resizeheight + (self.resizey - my) + if new_width >= self.minwidth and new_width <= self.maxwidth then + self.width = new_width + self.x = mx - self.resizexmod + end + if new_height >= self.minheight and new_height <= self.maxheight then + self.height = new_height + self.y = my - self.resizeymod + end + elseif self.resize_mode == "bottom_right" then + local new_width = (mx - self.x) + self.resizexmod + local new_height = (my - self.y) + self.resizeymod + if new_width >= self.minwidth and new_width <= self.maxwidth then + self.width = new_width + end + if new_height >= self.minheight and new_height <= self.maxheight then + self.height = new_height + end + elseif self.resize_mode == "top_right" then + local new_width = (mx - self.x) + self.resizexmod + local new_height = self.resizeheight + (self.resizey - my) + if new_width >= self.minwidth and new_width <= self.maxwidth then + self.width = new_width + end + if new_height >= self.minheight and new_height <= self.maxheight then + self.height = new_height + self.y = my - self.resizeymod + end + elseif self.resize_mode == "bottom_left" then + local new_width = self.resizewidth + (self.resizex - mx) + local new_height = (my - self.y) + self.resizeymod + if new_width >= self.minwidth and new_width <= self.maxwidth then + self.width = new_width + self.x = mx - self.resizexmod + end + if new_height >= self.minheight and new_height <= self.maxheight then + self.height = new_height + end + elseif self.resize_mode == "top" then + local new_height = self.resizeheight + (self.resizey - my) + if new_height >= self.minheight and new_height <= self.maxheight then + self.height = new_height + self.y = my - self.resizeymod + end + elseif self.resize_mode == "bottom" then + local new_height = (my - self.y) + self.resizeymod + if new_height >= self.minheight and new_height <= self.maxheight then + self.height = new_height + end + elseif self.resize_mode == "left" then + local new_width = self.resizewidth + (self.resizex - mx) + if new_width >= self.minwidth and new_width <= self.maxwidth then + self.width = new_width + self.x = mx - self.resizexmod + end + elseif self.resize_mode == "right" then + local new_width = (mx - self.x) + self.resizexmod + if new_width >= self.minwidth and new_width <= self.maxwidth then + self.width = new_width + end + end + if self.width ~= width or self.height ~= height then + local onresize = self.OnResize + if onresize then + onresize(self, self.width, self.height) + end + end + end + + -- if screenlocked then keep within screen + if screenlocked then + local width = self.width + local height = self.height + 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 width = self.width + local height = self.height + 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 parent == base and self.alwaysontop and not self:IsTopChild() then + self:MakeTop() + 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 + if self.modalbackground.state ~= self.state then + self.modalbackground:SetState(self.state) + 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: 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 internals = self.internals + local children = self.children + local dragging = self.dragging + local parent = self.parent + local base = loveframes.base + + if button == 1 then + -- initiate dragging if not currently dragging + if not dragging and self.hover and self.draggable then + local topcol + if self.canresize then + topcol = loveframes.BoundingBox(x, self.x + 2, y, self.y + 2, 1, self.width - 4, 1, 21) + else + topcol = loveframes.BoundingBox(x, self.x, y, self.y, 1, self.width, 1, 25) + end + if topcol 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 + loveframes.dragobject = self + end + end + if not self.resizing and self.canresize and loveframes.hoverobject == self then + if loveframes.BoundingBox(self.x, x, self.y, y, 5, 1, 5, 1) then + self.resizing = true + self.dragging = false + loveframes.dragobject = false + self.resize_mode = "top_left" + self.resizex = x + self.resizey = y + self.resizewidth = self.width + self.resizeheight = self.height + loveframes.resizeobject = self + if x ~= self.x then + self.resizexmod = x - self.x + end + if y ~= self.y then + self.resizeymod = y - self.y + end + elseif loveframes.BoundingBox(self.x + self.width - 5, x, self.y + self.height - 5, y, 5, 1, 5, 1) then + self.resizing = true + self.resize_mode = "bottom_right" + self.resizex = x + self.resizey = y + self.resizewidth = self.width + self.resizeheight = self.height + loveframes.resizeobject = self + if x ~= self.x + self.width then + self.resizexmod = (self.x + self.width) - x + end + if y ~= self.y + self.height then + self.resizeymod = (self.y + self.height) - y + end + elseif loveframes.BoundingBox(self.x + self.width - 5, x, self.y, y, 5, 1, 5, 1) then + self.resizing = true + self.dragging = false + loveframes.dragobject = false + self.resize_mode = "top_right" + self.resizex = x + self.resizey = y + self.resizewidth = self.width + self.resizeheight = self.height + loveframes.resizeobject = self + if x ~= self.x + self.width then + self.resizexmod = (self.x + self.width) - x + end + if y ~= self.y then + self.resizeymod = y - self.y + end + elseif loveframes.BoundingBox(self.x, x, self.y + self.height - 5, y, 5, 1, 5, 1) then + self.resizing = true + self.dragging = false + loveframes.dragobject = false + self.resize_mode = "bottom_left" + self.resizex = x + self.resizey = y + self.resizewidth = self.width + self.resizeheight = self.height + loveframes.resizeobject = self + if x ~= self.x then + self.resizexmod = x - self.x + end + if y ~= self.y + self.height then + self.resizeymod = (self.y + self.height) - y + end + elseif loveframes.BoundingBox(self.x + 5, x, self.y, y, self.width - 10, 1, 2, 1) then + self.resizing = true + self.dragging = false + loveframes.dragobject = false + self.resize_mode = "top" + self.resizex = x + self.resizey = y + self.resizewidth = self.width + self.resizeheight = self.height + loveframes.resizeobject = self + if y ~= self.y then + self.resizeymod = y - self.y + end + elseif loveframes.BoundingBox(self.x + 5, x, self.y + self.height - 2, y, self.width - 10, 1, 2, 1) then + self.resizing = true + self.dragging = false + loveframes.dragobject = false + self.resize_mode = "bottom" + self.resizex = x + self.resizey = y + self.resizewidth = self.width + self.resizeheight = self.height + loveframes.resizeobject = self + if y ~= self.y then + self.resizeymod = (self.y + self.height) - y + end + elseif loveframes.BoundingBox(self.x, x, self.y + 5, y, 2, 1, self.height - 10, 1) then + self.resizing = true + self.dragging = false + loveframes.dragobject = false + self.resize_mode = "left" + self.resizex = x + self.resizey = y + self.resizewidth = self.width + self.resizeheight = self.height + loveframes.resizeobject = self + if x ~= self.x then + self.resizexmod = x - self.x + end + elseif loveframes.BoundingBox(self.x + self.width - 2, x, self.y + 5, y, 2, 1, self.height - 10, 1) then + self.resizing = true + self.dragging = false + loveframes.dragobject = false + self.resize_mode = "right" + self.resizex = x + self.resizey = y + self.resizewidth = self.width + self.resizeheight = self.height + loveframes.resizeobject = self + if x ~= self.x + self.width then + self.resizexmod = (self.x + self.width) - x + end + end + end + if self.hover and button == 1 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 children = self.children + local internals = self.internals + + self.dragging = false + loveframes.dragobject = false + + if self.resizing then + self.resizex = 0 + self.resizey = 0 + self.resizexmod = 0 + self.resizeymod = 0 + self.resizewidth = 0 + self.resizeheight = 0 + self.resizing = false + loveframes.resizeobject = 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 object's name +--]]--------------------------------------------------------- +function newobject:SetName(name) + + self.name = name + return self + +end + +--[[--------------------------------------------------------- + - func: GetName() + - desc: gets the object's name +--]]--------------------------------------------------------- +function newobject:GetName() + + return self.name + +end + +--[[--------------------------------------------------------- + - func: SetDraggable(true/false) + - desc: sets whether the object can be dragged or not +--]]--------------------------------------------------------- +function newobject:SetDraggable(bool) + + self.draggable = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetDraggable() + - desc: gets whether the object can be dragged ot not +--]]--------------------------------------------------------- +function newobject:GetDraggable() + + return self.draggable + +end + + +--[[--------------------------------------------------------- + - func: SetScreenLocked(bool) + - desc: sets whether the object can be moved passed the + boundaries of the window or not +--]]--------------------------------------------------------- +function newobject:SetScreenLocked(bool) + + self.screenlocked = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetScreenLocked() + - desc: gets whether the object can be moved passed the + boundaries of window or not +--]]--------------------------------------------------------- +function newobject:GetScreenLocked() + + return self.screenlocked + +end + +--[[--------------------------------------------------------- + - func: ShowCloseButton(bool) + - desc: sets whether the object's close button should + be drawn +--]]--------------------------------------------------------- +function newobject:ShowCloseButton(bool) + + local close = self.internals[1] + + close.visible = bool + self.showclose = bool + return self + +end + +--[[--------------------------------------------------------- + - func: MakeTop() + - desc: makes the object the top object in the drawing + order +--]]--------------------------------------------------------- +function newobject:MakeTop() + + local key = 0 + local base = loveframes.base + local basechildren = base.children + local numbasechildren = #basechildren + + -- check to see if the object's parent is not the base object + if self.parent ~= base then + local baseparent = self:GetBaseParent() + if baseparent.type == "frame" then + baseparent:MakeTop() + end + return self + end + + -- check to see if the object is the only child of the base object + if numbasechildren == 1 then + return self + end + + -- check to see if the object is already at the top + if basechildren[numbasechildren] == self then + return self + 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 + + return self + +end + +--[[--------------------------------------------------------- + - func: SetModal(bool) + - desc: sets whether or not the object is in a modal + state +--]]--------------------------------------------------------- +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 + + return self + +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 + + return self + +end + +--[[--------------------------------------------------------- + - func: SetParentLocked(bool) + - desc: sets whether the object can be moved passed the + boundaries of its parent or not +--]]--------------------------------------------------------- +function newobject:SetParentLocked(bool) + + self.parentlocked = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetParentLocked(bool) + - desc: gets whether the object 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) + self.icon:setFilter("nearest", "nearest") + else + self.icon = icon + end + + return self + +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 object of its type or be docked + by another object of its type +--]]--------------------------------------------------------- +function newobject:SetDockable(dockable) + + self.dockable = dockable + return self + +end + +--[[--------------------------------------------------------- + - func: GetDockable() + - desc: gets whether or not the object can dock onto + another object of its type or be docked + by another object of its type +--]]--------------------------------------------------------- +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 + return self + +end + +--[[--------------------------------------------------------- + - func: GetDockZoneSize(size) + - desc: gets the size of the object's docking zone +--]]--------------------------------------------------------- +function newobject:GetDockZoneSize() + + return self.dockzonesize + +end + +--[[--------------------------------------------------------- + - func: SetResizable(bool) + - desc: sets whether or not the object can be resized +--]]--------------------------------------------------------- +function newobject:SetResizable(bool) + + self.canresize = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetResizable() + - desc: gets whether or not the object can be resized +--]]--------------------------------------------------------- +function newobject:GetResizable() + + return self.canresize + +end + +--[[--------------------------------------------------------- + - func: SetMinWidth(width) + - desc: sets the object's minimum width +--]]--------------------------------------------------------- +function newobject:SetMinWidth(width) + + self.minwidth = width + return self + +end + +--[[--------------------------------------------------------- + - func: GetMinWidth() + - desc: gets the object's minimum width +--]]--------------------------------------------------------- +function newobject:GetMinWidth() + + return self.minwidth + +end + +--[[--------------------------------------------------------- + - func: SetMaxWidth(width) + - desc: sets the object's maximum width +--]]--------------------------------------------------------- +function newobject:SetMaxWidth(width) + + self.maxwidth = width + return self + +end + +--[[--------------------------------------------------------- + - func: GetMaxWidth() + - desc: gets the object's maximum width +--]]--------------------------------------------------------- +function newobject:GetMaxWidth() + + return self.maxwidth + +end + +--[[--------------------------------------------------------- + - func: SetMinHeight(height) + - desc: sets the object's minimum height +--]]--------------------------------------------------------- +function newobject:SetMinHeight(height) + + self.minheight = height + return self + +end + +--[[--------------------------------------------------------- + - func: GetMinHeight() + - desc: gets the object's minimum height +--]]--------------------------------------------------------- +function newobject:GetMinHeight() + + return self.minheight + +end + +--[[--------------------------------------------------------- + - func: SetMaxHeight(height) + - desc: sets the object's maximum height +--]]--------------------------------------------------------- +function newobject:SetMaxHeight(height) + + self.maxheight = height + return self + +end + +--[[--------------------------------------------------------- + - func: GetMaxHeight() + - desc: gets the object's maximum height +--]]--------------------------------------------------------- +function newobject:GetMaxHeight() + + return self.maxheight + +end + +--[[--------------------------------------------------------- + - func: SetMinSize(width, height) + - desc: sets the object's minimum size +--]]--------------------------------------------------------- +function newobject:SetMinSize(width, height) + + self.minwidth = width + self.minheight = height + return self + +end + +--[[--------------------------------------------------------- + - func: GetMinSize() + - desc: gets the object's minimum size +--]]--------------------------------------------------------- +function newobject:GetMinSize() + + return self.minwidth, self.maxwidth + +end + +--[[--------------------------------------------------------- + - func: SetMaxSize(width, height) + - desc: sets the object's maximum size +--]]--------------------------------------------------------- +function newobject:SetMaxSize(width, height) + + self.maxwidth = width + self.maxheight = height + return self + +end + +--[[--------------------------------------------------------- + - func: GetMaxSize() + - desc: gets the object's maximum size +--]]--------------------------------------------------------- +function newobject:GetMaxSize() + + return self.maxwidth, self.maxheight + +end + +--[[--------------------------------------------------------- + - func: SetAlwaysOnTop(bool) + - desc: sets whether or not a frame should always be + drawn on top of other objects +--]]--------------------------------------------------------- +function newobject:SetAlwaysOnTop(bool) + + self.alwaysontop = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetAlwaysOnTop() + - desc: gets whether or not a frame should always be + drawn on top of other objects +--]]--------------------------------------------------------- +function newobject:GetAlwaysOnTop() + + return self.alwaysontop + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/grid.lua b/loveframes/objects/grid.lua new file mode 100644 index 0000000..89c0e7a --- /dev/null +++ b/loveframes/objects/grid.lua @@ -0,0 +1,358 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- grid object +local newobject = loveframes.NewObject("grid", "loveframes_object_grid", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "grid" + self.width = 100 + self.height = 100 + self.prevwidth = 100 + self.prevheight = 100 + self.rows = 0 + self.columns = 0 + self.cellwidth = 25 + self.cellheight = 25 + self.cellpadding = 5 + self.itemautosize = false + self.children = {} + self.OnSizeChanged = nil + + self:SetDrawFunc() +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 + + self:CheckHover() + + local parent = self.parent + local children = self.children + local base = loveframes.base + + -- 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 cw = self.cellwidth + (self.cellpadding * 2) + local ch = self.cellheight + (self.cellpadding * 2) + local prevwidth = self.prevwidth + local prevheight = self.prevheight + + self.width = (self.columns * self.cellwidth) + (self.columns * (self.cellpadding * 2)) + self.height = (self.rows * self.cellheight) + (self.rows * (self.cellpadding * 2)) + + if self.width ~= prevwidth or self.height ~= prevheight then + local onsizechanged = self.OnSizeChanged + self.prevwidth = self.width + self.prevheight = self.height + if onsizechanged then + onsizechanged(self) + end + end + + for k, v in ipairs(children) do + local x = 0 + ((cw * v.gridcolumn) - cw ) + (cw/2 - v.width/2) + local y = 0 + ((ch * v.gridrow) - ch) + (ch/2 - v.height/2) + v.staticx = x + v.staticy = y + v:update(dt) + end + + local update = self.Update + if update then update(self, dt) 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 children = self.children + local hover = self.hover + + if hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + 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 + local children = self.children + + if not visible then + return + end + + for k, v in ipairs(children) do + v:mousereleased(x, y, button) + end + +end + +--[[--------------------------------------------------------- + - func: AddItem(object, row, column) + - desc: adds and item to the object +--]]--------------------------------------------------------- +function newobject:AddItem(object, row, column) + + local itemautosize = self.itemautosize + local children = self.children + + object:Remove() + + table.insert(children, object) + object.parent = self + object.gridrow = row + object.gridcolumn = column + + if itemautosize then + local cw = self.cellwidth + (self.cellpadding * 2) + local ch = self.cellheight + (self.cellpadding * 2) + object.width = cw - (self.cellpadding * 2) + object.height = ch - (self.cellpadding * 2) + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetItem(row, column) + - desc: gets an item from the object at the specified + row and column +--]]--------------------------------------------------------- +function newobject:GetItem(row, column) + + local children = self.children + + for k, v in ipairs(children) do + if v.gridrow == row and v.gridcolumn == column then + return v + end + end + + return false + +end + +--[[--------------------------------------------------------- + - func: SetItemAutoSize(bool) + - desc: sets whether or not the object should auto-size + its items +--]]--------------------------------------------------------- +function newobject:SetItemAutoSize(bool) + + self.itemautosize = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetItemAutoSize() + - desc: gets whether or not the object should auto-size + its items +--]]--------------------------------------------------------- +function newobject:GetItemAutoSize() + + return self.itemautosize + +end + +--[[--------------------------------------------------------- + - func: SetRows(rows) + - desc: sets the number of rows the object should have +--]]--------------------------------------------------------- +function newobject:SetRows(rows) + + self.rows = rows + return self + +end + +--[[--------------------------------------------------------- + - func: SetRows(rows) + - desc: gets the number of rows the object has +--]]--------------------------------------------------------- +function newobject:GetRows() + + return self.rows + +end + +--[[--------------------------------------------------------- + - func: SetColumns(columns) + - desc: sets the number of columns the object should + have +--]]--------------------------------------------------------- +function newobject:SetColumns(columns) + + self.columns = columns + return self + +end + +--[[--------------------------------------------------------- + - func: GetColumns() + - desc: gets the number of columns the object has +--]]--------------------------------------------------------- +function newobject:GetColumns() + + return self.columns + +end + +--[[--------------------------------------------------------- + - func: SetCellWidth(width) + - desc: sets the width of the object's cells +--]]--------------------------------------------------------- +function newobject:SetCellWidth(width) + + self.cellwidth = width + return self + +end + +--[[--------------------------------------------------------- + - func: GetCellWidth() + - desc: gets the width of the object's cells +--]]--------------------------------------------------------- +function newobject:GetCellWidth() + + return self.cellwidth + +end + +--[[--------------------------------------------------------- + - func: SetCellHeight(height) + - desc: sets the height of the object's cells +--]]--------------------------------------------------------- +function newobject:SetCellHeight(height) + + self.cellheight = height + return self + +end + +--[[--------------------------------------------------------- + - func: GetCellHeight() + - desc: gets the height of the object's cells +--]]--------------------------------------------------------- +function newobject:GetCellHeight() + + return self.cellheight + +end + +--[[--------------------------------------------------------- + - func: SetCellSize(width, height) + - desc: sets the size of the object's cells +--]]--------------------------------------------------------- +function newobject:SetCellSize(width, height) + + self.cellwidth = width + self.cellheight = height + return self + +end + +--[[--------------------------------------------------------- + - func: GetCellSize() + - desc: gets the size of the object's cells +--]]--------------------------------------------------------- +function newobject:GetCellSize() + + return self.cellwidth, self.cellheight + +end + +--[[--------------------------------------------------------- + - func: SetCellPadding(padding) + - desc: sets the padding of the object's cells +--]]--------------------------------------------------------- +function newobject:SetCellPadding(padding) + + self.cellpadding = padding + return self + +end + +--[[--------------------------------------------------------- + - func: GetCellPadding + - desc: gets the padding of the object's cells +--]]--------------------------------------------------------- +function newobject:GetCellPadding() + + return self.cellpadding + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/image.lua b/loveframes/objects/image.lua new file mode 100644 index 0000000..e8f4acc --- /dev/null +++ b/loveframes/objects/image.lua @@ -0,0 +1,385 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- image object +local newobject = loveframes.NewObject("image", "loveframes_object_image", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "image" + self.width = 0 + self.height = 0 + self.orientation = 0 + self.scalex = 1 + self.scaley = 1 + self.offsetx = 0 + self.offsety = 0 + self.shearx = 0 + self.sheary = 0 + self.internal = false + self.image = nil + self.imagecolor = {1, 1, 1, 1} + + self:SetDrawFunc() +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() + + -- 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: SetImage(image) + - desc: sets the object's image +--]]--------------------------------------------------------- +function newobject:SetImage(image) + + if type(image) == "string" then + self.image = love.graphics.newImage(image) + self.image:setFilter("nearest", "nearest") + else + self.image = image + end + + self.width = self.image:getWidth() + self.height = self.image:getHeight() + + return self + +end + +--[[--------------------------------------------------------- + - func: GetImage() + - desc: gets the object's image +--]]--------------------------------------------------------- +function newobject:GetImage() + + return self.image + +end + +--[[--------------------------------------------------------- + - func: SetColor(r, g, b, a) + - desc: sets the object's color +--]]--------------------------------------------------------- +function newobject:SetColor(r, g, b, a) + + self.imagecolor = {r, g, b, a} + return self + +end + +--[[--------------------------------------------------------- + - func: GetColor() + - desc: gets the object's color +--]]--------------------------------------------------------- +function newobject:GetColor() + + return unpack(self.imagecolor) + +end + +--[[--------------------------------------------------------- + - func: SetOrientation(orientation) + - desc: sets the object's orientation +--]]--------------------------------------------------------- +function newobject:SetOrientation(orientation) + + self.orientation = orientation + return self + +end + +--[[--------------------------------------------------------- + - func: GetOrientation() + - desc: gets the object's orientation +--]]--------------------------------------------------------- +function newobject:GetOrientation() + + return self.orientation + +end + +--[[--------------------------------------------------------- + - func: SetScaleX(scalex) + - desc: sets the object's x scale +--]]--------------------------------------------------------- +function newobject:SetScaleX(scalex) + + self.scalex = scalex + return self + +end + +--[[--------------------------------------------------------- + - func: GetScaleX() + - desc: gets the object's x scale +--]]--------------------------------------------------------- +function newobject:GetScaleX() + + return self.scalex + +end + +--[[--------------------------------------------------------- + - func: SetScaleY(scaley) + - desc: sets the object's y scale +--]]--------------------------------------------------------- +function newobject:SetScaleY(scaley) + + self.scaley = scaley + return self + +end + +--[[--------------------------------------------------------- + - func: GetScaleY() + - desc: gets the object's y scale +--]]--------------------------------------------------------- +function newobject:GetScaleY() + + return self.scaley + +end + +--[[--------------------------------------------------------- + - func: SetScale(scalex, scaley) + - desc: sets the object's x and y scale +--]]--------------------------------------------------------- +function newobject:SetScale(scalex, scaley) + + self.scalex = scalex + self.scaley = scaley + + return self + +end + +--[[--------------------------------------------------------- + - func: GetScale() + - desc: gets the object's x and y scale +--]]--------------------------------------------------------- +function newobject:GetScale() + + return self.scalex, self.scaley + +end + +--[[--------------------------------------------------------- + - func: SetOffsetX(x) + - desc: sets the object's x offset +--]]--------------------------------------------------------- +function newobject:SetOffsetX(x) + + self.offsetx = x + return self + +end + +--[[--------------------------------------------------------- + - func: GetOffsetX() + - desc: gets the object's x offset +--]]--------------------------------------------------------- +function newobject:GetOffsetX() + + return self.offsetx + +end + +--[[--------------------------------------------------------- + - func: SetOffsetY(y) + - desc: sets the object's y offset +--]]--------------------------------------------------------- +function newobject:SetOffsetY(y) + + self.offsety = y + return self + +end + +--[[--------------------------------------------------------- + - func: GetOffsetY() + - desc: gets the object's y offset +--]]--------------------------------------------------------- +function newobject:GetOffsetY() + + return self.offsety + +end + +--[[--------------------------------------------------------- + - func: SetOffset(x, y) + - desc: sets the object's x and y offset +--]]--------------------------------------------------------- +function newobject:SetOffset(x, y) + + self.offsetx = x + self.offsety = y + + return self + +end + +--[[--------------------------------------------------------- + - func: GetOffset() + - desc: gets the object's x and y offset +--]]--------------------------------------------------------- +function newobject:GetOffset() + + return self.offsetx, self.offsety + +end + +--[[--------------------------------------------------------- + - func: SetShearX(shearx) + - desc: sets the object's x shear +--]]--------------------------------------------------------- +function newobject:SetShearX(shearx) + + self.shearx = shearx + return self + +end + +--[[--------------------------------------------------------- + - func: GetShearX() + - desc: gets the object's x shear +--]]--------------------------------------------------------- +function newobject:GetShearX() + + return self.shearx + +end + +--[[--------------------------------------------------------- + - func: SetShearY(sheary) + - desc: sets the object's y shear +--]]--------------------------------------------------------- +function newobject:SetShearY(sheary) + + self.sheary = sheary + return self + +end + +--[[--------------------------------------------------------- + - func: GetShearY() + - desc: gets the object's y shear +--]]--------------------------------------------------------- +function newobject:GetShearY() + + return self.sheary + +end + +--[[--------------------------------------------------------- + - func: SetShear(shearx, sheary) + - desc: sets the object's x and y shear +--]]--------------------------------------------------------- +function newobject:SetShear(shearx, sheary) + + self.shearx = shearx + self.sheary = sheary + + return self + +end + +--[[--------------------------------------------------------- + - func: GetShear() + - desc: gets the object's x and y shear +--]]--------------------------------------------------------- +function newobject:GetShear() + + return self.shearx, self.sheary + +end + +--[[--------------------------------------------------------- + - func: GetImageSize() + - desc: gets the size of the object's image +--]]--------------------------------------------------------- +function newobject:GetImageSize() + + local image = self.image + + if image then + return image:getWidth(), image:getHeight() + end + +end + +--[[--------------------------------------------------------- + - func: GetImageWidth() + - desc: gets the width of the object's image +--]]--------------------------------------------------------- +function newobject:GetImageWidth() + + local image = self.image + + if image then + return image:getWidth() + end + +end + +--[[--------------------------------------------------------- + - func: GetImageWidth() + - desc: gets the height of the object's image +--]]--------------------------------------------------------- +function newobject:GetImageHeight() + + local image = self.image + + if image then + return image:getHeight() + end + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/imagebutton.lua b/loveframes/objects/imagebutton.lua new file mode 100644 index 0000000..786c7a5 --- /dev/null +++ b/loveframes/objects/imagebutton.lua @@ -0,0 +1,343 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- imagebutton object +local newobject = loveframes.NewObject("imagebutton", "loveframes_object_imagebutton", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "imagebutton" + self.text = "Image Button" + self.width = 50 + self.height = 50 + self.internal = false + self.down = false + self.clickable = true + self.enabled = true + self.image = nil + self.imagecolor = {1, 1, 1, 1} + self.OnClick = nil + self.groupIndex = 0 + self.checked = false + + self:SetDrawFunc() +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 + + self:CheckHover() + + local hover = self.hover + local downobject = loveframes.downobject + local down = self.down + local parent = self.parent + local base = loveframes.base + local update = self.Update + + if not hover then + self.down = false + else + if downobject == self then + self.down = true + end + end + + if not down and downobject == self then + self.hover = true + 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: 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 + self.down = true + loveframes.downobject = self + 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 hover = self.hover + local down = self.down + local clickable = self.clickable + local enabled = self.enabled + local onclick = self.OnClick + + if hover and down and clickable and button == 1 then + if enabled then + if self.groupIndex ~= 0 then + local baseparent = self.parent + if baseparent then + for k, v in ipairs(baseparent.children) do + if v.groupIndex then + if v.groupIndex == self.groupIndex then + v.checked = false + end + end + end + end + self.checked = true + end + if onclick then + onclick(self, x, y) + end + end + end + + self.down = false + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the object's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + self.text = text + return self + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the object's text +--]]--------------------------------------------------------- +function newobject:GetText() + + return self.text + +end + +--[[--------------------------------------------------------- + - func: SetClickable(bool) + - desc: sets whether the object can be clicked or not +--]]--------------------------------------------------------- +function newobject:SetClickable(bool) + + self.clickable = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetClickable(bool) + - desc: gets whether the object can be clicked or not +--]]--------------------------------------------------------- +function newobject:GetClickable() + + return self.clickable + +end + +--[[--------------------------------------------------------- + - func: SetClickable(bool) + - desc: sets whether the object is enabled or not +--]]--------------------------------------------------------- +function newobject:SetEnabled(bool) + + self.enabled = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetEnabled() + - desc: gets whether the object is enabled or not +--]]--------------------------------------------------------- +function newobject:GetEnabled() + + return self.enabled + +end + +--[[--------------------------------------------------------- + - func: SetImage(image) + - desc: sets the object's image +--]]--------------------------------------------------------- +function newobject:SetImage(image) + + if type(image) == "string" then + self.image = love.graphics.newImage(image) + self.image:setFilter("nearest", "nearest") + else + self.image = image + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetImage() + - desc: gets whether the object is enabled or not +--]]--------------------------------------------------------- +function newobject:GetImage() + + return self.image + +end + +--[[--------------------------------------------------------- + - func: SizeToImage() + - desc: makes the object the same size as its image +--]]--------------------------------------------------------- +function newobject:SizeToImage() + + local image = self.image + + if image then + self.width = image:getWidth() + self.height = image:getHeight() + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetImageSize() + - desc: gets the size of the object's image +--]]--------------------------------------------------------- +function newobject:GetImageSize() + + local image = self.image + + if image then + return image:getWidth(), image:getHeight() + end + +end + +--[[--------------------------------------------------------- + - func: GetImageWidth() + - desc: gets the width of the object's image +--]]--------------------------------------------------------- +function newobject:GetImageWidth() + + local image = self.image + + if image then + return image:getWidth() + end + +end + +--[[--------------------------------------------------------- + - func: GetImageWidth() + - desc: gets the height of the object's image +--]]--------------------------------------------------------- +function newobject:GetImageHeight() + + local image = self.image + + if image then + return image:getHeight() + end + +end + +--[[--------------------------------------------------------- + - func: SetColor(r, g, b, a) + - desc: sets the object's color +--]]--------------------------------------------------------- +function newobject:SetColor(r, g, b, a) + + self.imagecolor = {r, g, b, a} + return self + +end + +--[[--------------------------------------------------------- + - func: GetColor() + - desc: gets the object's color +--]]--------------------------------------------------------- +function newobject:GetColor() + + return unpack(self.imagecolor) + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/closebutton.lua b/loveframes/objects/internal/closebutton.lua new file mode 100644 index 0000000..ec2ac5f --- /dev/null +++ b/loveframes/objects/internal/closebutton.lua @@ -0,0 +1,159 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- closebutton class +local newobject = loveframes.NewObject("closebutton", "loveframes_object_closebutton", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "closebutton" + self.width = 16 + self.height = 16 + self.internal = true + self.hover = false + self.down = false + self.autoposition = true + self.OnClick = function() end + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + + local visible = self.visible + local alwaysupdate = self.alwaysupdate + + if not visible then + if not alwaysupdate then + return + end + end + + self:CheckHover() + + local hover = self.hover + local down = self.down + local downobject = loveframes.downobject + local parent = self.parent + local base = loveframes.base + local update = self.Update + + if not hover then + self.down = false + else + if loveframes.downobject == self then + self.down = true + end + end + + if not down and downobject == self then + self.hover = true + end + + if self.autoposition then + self.staticx = self.parent.width - self.width - 4 + self.staticy = 4 + end + + -- move to parent if there is a parent + if parent ~= base then + self.x = parent.x + self.staticx + self.y = parent.y + self.staticy + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + + 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 + self.down = true + loveframes.downobject = self + end + +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function newobject:mousereleased(x, y, button) + + local visible = self.visible + + if not visible then + return + end + + local hover = self.hover + local onclick = self.OnClick + + if hover and self.down then + if button == 1 then + onclick(x, y, self) + end + end + + self.down = false + +end + +--[[--------------------------------------------------------- + - func: SetAutoPosition(bool) + - desc: sets whether or not the object should be + positioned automatically +--]]--------------------------------------------------------- +function newobject:SetAutoPosition(bool) + + self.autoposition = bool + +end + +--[[--------------------------------------------------------- + - func: GetAutoPosition() + - desc: gets whether or not the object should be + positioned automatically +--]]--------------------------------------------------------- +function newobject:GetAutoPosition() + + return self.autoposition + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/columnlist/columnlistarea.lua b/loveframes/objects/internal/columnlist/columnlistarea.lua new file mode 100644 index 0000000..f4d3458 --- /dev/null +++ b/loveframes/objects/internal/columnlist/columnlistarea.lua @@ -0,0 +1,469 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- columnlistarea class +local newobject = loveframes.NewObject("columnlistarea", "loveframes_object_columnlistarea", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: intializes the element +--]]--------------------------------------------------------- +function newobject:initialize(parent) + + self.type = "columnlistarea" + self.display = "vertical" + self.parent = parent + self.width = 80 + self.height = 25 + self.clickx = 0 + self.clicky = 0 + self.offsety = 0 + self.offsetx = 0 + self.extrawidth = 0 + self.extraheight = 0 + self.rowcolorindex = 1 + self.rowcolorindexmax = 2 + self.buttonscrollamount = parent.buttonscrollamount + self.mousewheelscrollamount = parent.mousewheelscrollamount + self.vbar = false + self.hbar = false + self.dtscrolling = parent.dtscrolling + self.internal = true + self.internals = {} + self.children = {} + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + + if not self.visible then + if not self.alwaysupdate then + return + end + end + + local cwidth, cheight = self.parent:GetColumnSize() + local parent = self.parent + local update = self.Update + local internals = self.internals + + self:CheckHover() + + -- move to parent if there is a parent + if parent ~= loveframes.base then + self.x = parent.x + self.staticx + self.y = parent.y + self.staticy + end + + for k, v in ipairs(self.children) do + local col = loveframes.BoundingBox(self.x, v.x, self.y, v.y, self.width, v.width, self.height, v.height) + if col then + v:update(dt) + end + v:SetClickBounds(self.x, self.y, self.width, self.height) + v.y = (v.parent.y + v.staticy) - self.offsety + cheight + v.x = (v.parent.x + v.staticx) - self.offsetx + end + + for k, v in ipairs(self.internals) do + v:update(dt) + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: draw() + - desc: draws the object +--]]--------------------------------------------------------- +function newobject:draw() + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local x = self.x + local y = self.y + local width = self.width + local height = self.height + local swidth = width + local sheight = height + + if self.vbar then + swidth = swidth - self:GetVerticalScrollBody():GetWidth() + end + + if self.hbar then + sheight = sheight - self:GetHorizontalScrollBody():GetHeight() + end + + local stencilfunc = function() love.graphics.rectangle("fill", x, y, swidth, sheight) end + + self:SetDrawOrder() + + local drawfunc = self.Draw or self.drawfunc + if drawfunc then + drawfunc(self) + end + + love.graphics.stencil(stencilfunc) + love.graphics.setStencilTest("greater", 0) + + local children = self.children + if children then + for k, v in ipairs(self.children) do + local col = loveframes.BoundingBox(self.x, v.x, self.y, v.y, width, v.width, height, v.height) + if col then + v:draw() + end + end + end + + love.graphics.setStencilTest() + + drawfunc = self.DrawOver or self.drawoverfunc + if drawfunc then + drawfunc(self) + end + + local internals = self.internals + if internals then + for k, v in ipairs(self.internals) do + v:draw() + end + end +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + + local scrollamount = self.mousewheelscrollamount + + if self.hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + end + + for k, v in ipairs(self.internals) do + v:mousepressed(x, y, button) + end + + for k, v in ipairs(self.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 internals = self.internals + local children = self.children + + 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: wheelmoved(x, y) + - desc: called when the player moves a mouse wheel +--]]--------------------------------------------------------- +function newobject:wheelmoved(x, y) + + local scrollamount = self.mousewheelscrollamount + + -- FIXME: button is nil + -- if self.hover and button == 1 then + if self.hover then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + end + + local bar = false + if self.vbar and self.hbar then + bar = self:GetVerticalScrollBody():GetScrollBar() + elseif self.vbar and not self.hbar then + bar = self:GetVerticalScrollBody():GetScrollBar() + elseif not self.vbar and self.hbar then + bar = self:GetHorizontalScrollBody():GetScrollBar() + end + + if self:IsTopList() and bar then + if self.dtscrolling then + local dt = love.timer.getDelta() + bar:Scroll(-y * scrollamount * dt) + else + bar:Scroll(-y * scrollamount) + end + end + +end + +--[[--------------------------------------------------------- + - func: CalculateSize() + - desc: calculates the size of the object's children +--]]--------------------------------------------------------- +function newobject:CalculateSize() + + local height = self.height + local width = self.width + local parent = self.parent + local itemheight = parent.columnheight + + for k, v in ipairs(self.children) do + itemheight = itemheight + v.height + end + + self.itemheight = itemheight + self.itemwidth = parent:GetTotalColumnWidth() + + local hbarheight = 0 + local hbody = self:GetHorizontalScrollBody() + if hbody then + hbarheight = hbody.height + end + + if self.itemheight > (height - hbarheight) then + if hbody then + self.itemheight = self.itemheight + hbarheight + end + self.extraheight = self.itemheight - height + if not self.vbar then + local newbar = loveframes.objects["scrollbody"]:new(self, "vertical") + table.insert(self.internals, newbar) + self.vbar = true + newbar:GetScrollBar().autoscroll = parent.autoscroll + self.itemwidth = self.itemwidth + newbar.width + self.extrawidth = self.itemwidth - width + end + else + if self.vbar then + self:GetVerticalScrollBody():Remove() + self.vbar = false + self.offsety = 0 + end + end + + local vbarwidth = 0 + local vbody = self:GetVerticalScrollBody() + if vbody then + vbarwidth = vbody.width + end + + if self.itemwidth > (width - vbarwidth) then + if vbody then + self.itemwidth = self.itemwidth + vbarwidth + end + self.extrawidth = self.itemwidth - width + if not self.hbar then + local newbar = loveframes.objects["scrollbody"]:new(self, "horizontal") + table.insert(self.internals, newbar) + self.hbar = true + self.itemheight = self.itemheight + newbar.height + self.extraheight = self.itemheight - height + end + else + if self.hbar then + local hbar = self:GetHorizontalScrollBody() + hbar:Remove() + self.itemheight = self.itemheight - hbar.height + self.extraheight = self.itemheight - height + self.hbar = false + self.offsetx = 0 + end + end + +end + +--[[--------------------------------------------------------- + - func: RedoLayout() + - desc: used to redo the layour of the object +--]]--------------------------------------------------------- +function newobject:RedoLayout() + + local starty = 0 + self.rowcolorindex = 1 + + for k, v in ipairs(self.children) do + v:SetWidth(self.parent:GetTotalColumnWidth()) + v.staticx = 0 + v.staticy = starty + if self.vbar then + local vbody = self:GetVerticalScrollBody() + vbody.staticx = self.width - vbody.width + if self.hbar then + vbody.height = self.height - self:GetHorizontalScrollBody().height + else + vbody.height = self.height + end + end + if self.hbar then + local hbody = self:GetHorizontalScrollBody() + hbody.staticy = self.height - hbody.height + if self.vbar then + hbody.width = self.width - self:GetVerticalScrollBody().width + else + hbody.width = self.width + end + end + starty = starty + v.height + v.lastheight = v.height + v.colorindex = self.rowcolorindex + if self.rowcolorindex == self.rowcolorindexmax then + self.rowcolorindex = 1 + else + self.rowcolorindex = self.rowcolorindex + 1 + end + end + +end + +--[[--------------------------------------------------------- + - func: AddRow(data) + - desc: adds a row to the object +--]]--------------------------------------------------------- +function newobject:AddRow(data) + + local colorindex = self.rowcolorindex + + if colorindex == self.rowcolorindexmax then + self.rowcolorindex = 1 + else + self.rowcolorindex = colorindex + 1 + end + + table.insert(self.children, loveframes.objects["columnlistrow"]:new(self, data)) + self:CalculateSize() + self:RedoLayout() + self.parent:PositionColumns() + +end + +--[[--------------------------------------------------------- + - func: GetScrollBar() + - desc: gets the object's scroll bar +--]]--------------------------------------------------------- +function newobject:GetScrollBar() + + if self.bar then + return self.internals[1].internals[1].internals[1] + else + return false + end + +end + +--[[--------------------------------------------------------- + - func: Sort() + - desc: sorts the object's children +--]]--------------------------------------------------------- +function newobject:Sort(column, desc) + + local children = self.children + self.rowcolorindex = 1 + + table.sort(children, function(a, b) + if desc then + return (tostring(a.columndata[column]) or a.columndata[column]) < (tostring(b.columndata[column]) or b.columndata[column]) + else + return (tostring(a.columndata[column]) or a.columndata[column]) > (tostring(b.columndata[column]) or b.columndata[column]) + end + end) + + for k, v in ipairs(children) do + local colorindex = self.rowcolorindex + v.colorindex = colorindex + if colorindex == self.rowcolorindexmax then + self.rowcolorindex = 1 + else + self.rowcolorindex = colorindex + 1 + end + end + + self:CalculateSize() + self:RedoLayout() + +end + +--[[--------------------------------------------------------- + - func: Clear() + - desc: removes all items from the object's list +--]]--------------------------------------------------------- +function newobject:Clear() + + self.children = {} + self:CalculateSize() + self:RedoLayout() + self.parent:PositionColumns() + self.rowcolorindex = 1 + +end + +--[[--------------------------------------------------------- + - func: GetVerticalScrollBody() + - desc: gets the object's vertical scroll body +--]]--------------------------------------------------------- +function newobject:GetVerticalScrollBody() + + for k, v in ipairs(self.internals) do + if v.bartype == "vertical" then + return v + end + end + + return false + +end + +--[[--------------------------------------------------------- + - func: GetHorizontalScrollBody() + - desc: gets the object's horizontal scroll body +--]]--------------------------------------------------------- +function newobject:GetHorizontalScrollBody() + + for k, v in ipairs(self.internals) do + if v.bartype == "horizontal" then + return v + end + end + + return false + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/columnlist/columnlistheader.lua b/loveframes/objects/internal/columnlist/columnlistheader.lua new file mode 100644 index 0000000..d4f5be0 --- /dev/null +++ b/loveframes/objects/internal/columnlist/columnlistheader.lua @@ -0,0 +1,203 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- columnlistheader class +local newobject = loveframes.NewObject("columnlistheader", "loveframes_object_columnlistheader", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: intializes the element +--]]--------------------------------------------------------- +function newobject:initialize(name, parent) + + self.type = "columnlistheader" + self.parent = parent + self.name = name + self.state = parent.state + self.width = parent.defaultcolumnwidth + self.height = parent.columnheight + self.columnid = 0 + self.hover = false + self.down = false + self.clickable = true + self.enabled = true + self.descending = true + self.resizebox = nil + self.internal = true + + table.insert(parent.children, self) + + local key = 0 + + for k, v in ipairs(parent.children) do + if v == self then + key = k + end + end + + self.OnClick = function(object) + local descending = object.descending + local parent = object.parent + local pinternals = parent.internals + local list = pinternals[1] + if descending then + object.descending = false + else + object.descending = true + end + list:Sort(key, object.descending) + end + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + + if not self.visible then + if not self.alwaysupdate then + return + end + end + + local update = self.Update + local parent = self.parent + local list = parent.internals[1] + local vbody = list:GetVerticalScrollBody() + local width = list.width + if vbody then + width = width - vbody.width + end + + self.clickbounds = {x = list.x, y = list.y, width = width, height = list.height} + self:CheckHover() + + if not self.hover then + self.down = false + else + if loveframes.downobject == self then + self.down = true + end + end + + -- move to parent if there is a parent + if parent ~= loveframes.base then + self.x = (parent.x + self.staticx) - parent.internals[1].offsetx + self.y = parent.y + self.staticy + end + + local resizecolumn = parent.resizecolumn + + if resizecolumn and resizecolumn == self then + local x, y = love.mouse.getPosition() + local start = false + self.width = x - self.x + if self.width < 20 then + self.width = 20 + end + parent.startadjustment = true + parent.internals[1]:CalculateSize() + parent.internals[1]:RedoLayout() + elseif resizecolumn and parent.startadjustment then + local header = parent.children[self.columnid - 1] + self.staticx = header.staticx + header.width + end + + self.resizebox = {x = self.x + (self.width - 2), y = self.y, width = 4, height = self.height} + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + + if not self.parent.resizecolumn and self.parent.canresizecolumns then + local box = self.resizebox + local col = loveframes.BoundingBox(x, box.x, y, box.y, 1, box.width, 1, box.height) + if col then + self.resizing = true + self.parent.resizecolumn = self + end + end + + if self.hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" and button == 1 then + baseparent:MakeTop() + end + self.down = true + loveframes.downobject = self + end + +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function newobject:mousereleased(x, y, button) + + if not self.visible then + return + end + + local hover = self.hover + local down = self.down + local clickable = self.clickable + local enabled = self.enabled + local onclick = self.OnClick + + if hover and down and clickable and button == 1 then + if enabled then + onclick(self, x, y) + end + end + + local resizecolumn = self.parent.resizecolumn + if resizecolumn and resizecolumn == self then + self.parent.resizecolumn = nil + end + + self.down = false + +end + +--[[--------------------------------------------------------- + - func: SetName(name) + - desc: sets the object's name +--]]--------------------------------------------------------- +function newobject:SetName(name) + + self.name = name + return self + +end + +--[[--------------------------------------------------------- + - func: GetName() + - desc: gets the object's name +--]]--------------------------------------------------------- +function newobject:GetName() + + return self.name + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/columnlist/columnlistrow.lua b/loveframes/objects/internal/columnlist/columnlistrow.lua new file mode 100644 index 0000000..242f840 --- /dev/null +++ b/loveframes/objects/internal/columnlist/columnlistrow.lua @@ -0,0 +1,218 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- columnlistrow class +local newobject = loveframes.NewObject("columnlistrow", "loveframes_object_columnlistrow", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: intializes the element +--]]--------------------------------------------------------- +function newobject:initialize(parent, data) + + self.type = "columnlistrow" + self.parent = parent + self.state = parent.state + self.colorindex = self.parent.rowcolorindex + self.font = loveframes.basicfontsmall + self.width = 80 + self.height = 25 + self.textx = 5 + self.texty = 5 + self.selected = false + self.internal = true + self.columndata = {} + + for k, v in ipairs(data) do + self.columndata[k] = tostring(v) + end + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + + if not self.visible then + if not self.alwaysupdate then + return + end + end + + local parent = self.parent + local update = self.Update + + self:CheckHover() + + -- move to parent if there is a parent + if parent ~= loveframes.base then + self.x = parent.x + self.staticx + self.y = parent.y + self.staticy + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + + if not self.visible then + return + end + + if self.hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + self:GetParent():GetParent():SelectRow(self, loveframes.IsCtrlDown()) + end + +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function newobject:mousereleased(x, y, button) + + if not self.visible then + return + end + + if self.hover then + local parent = self:GetParent():GetParent() + if button == 1 then + local onrowclicked = parent.OnRowClicked + if onrowclicked then + onrowclicked(parent, self, self.columndata) + end + elseif button == 2 then + local onrowrightclicked = parent.OnRowRightClicked + if onrowrightclicked then + onrowrightclicked(parent, self, self.columndata) + end + end + end + +end + +--[[--------------------------------------------------------- + - func: SetTextPos(x, y) + - desc: sets the positions of the object's text +--]]--------------------------------------------------------- +function newobject:SetTextPos(x, y) + + self.textx = x + self.texty = y + +end + +--[[--------------------------------------------------------- + - func: GetTextX() + - desc: gets the object's text x position +--]]--------------------------------------------------------- +function newobject:GetTextX() + + return self.textx + +end + +--[[--------------------------------------------------------- + - func: GetTextY() + - desc: gets the object's text y position +--]]--------------------------------------------------------- +function newobject:GetTextY() + + return self.texty + +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: GetColorIndex() + - desc: gets the object's color index +--]]--------------------------------------------------------- +function newobject:GetColorIndex() + + return self.colorindex + +end + +--[[--------------------------------------------------------- + - func: SetColumnData(data) + - desc: sets the object's column data +--]]--------------------------------------------------------- +function newobject:SetColumnData(data) + + self.columndata = data + +end + +--[[--------------------------------------------------------- + - func: GetColumnData() + - desc: gets the object's column data +--]]--------------------------------------------------------- +function newobject:GetColumnData() + + return self.columndata + +end + +--[[--------------------------------------------------------- + - func: SetSelected(selected) + - desc: sets whether or not the object is selected +--]]--------------------------------------------------------- +function newobject:SetSelected(selected) + + self.selected = true + +end + +--[[--------------------------------------------------------- + - func: GetSelected() + - desc: gets whether or not the object is selected +--]]--------------------------------------------------------- +function newobject:GetSelected() + + return self.selected + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/linenumberspanel.lua b/loveframes/objects/internal/linenumberspanel.lua new file mode 100644 index 0000000..ed4600b --- /dev/null +++ b/loveframes/objects/internal/linenumberspanel.lua @@ -0,0 +1,161 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- linenumberspanel class +local newobject = loveframes.NewObject("linenumberspanel", "loveframes_object_linenumberspanel", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(parent) + + self.parent = parent + self.type = "linenumberspanel" + self.width = 5 + self.height = 5 + self.offsety = 0 + self.staticx = 0 + self.staticy = 0 + self.internal = true + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the element +--]]--------------------------------------------------------- +function newobject:update(dt) + + 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 + local height = self.parent.height + local parentinternals = parent.internals + + self.height = height + self.offsety = self.parent.offsety - self.parent.textoffsety + + -- 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 parentinternals[1] ~= self then + self:Remove() + table.insert(parentinternals, 1, self) + return + end + + self:CheckHover() + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: draw() + - desc: draws the object +--]]--------------------------------------------------------- +function newobject:draw() + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local x = self.x + local y = self.y + local width = self.width + local height = self.height + local stencilfunc = function() love.graphics.rectangle("fill", self.parent.x, self.parent.y, width, height) end + + if self.parent.hbar then + stencilfunc = function() love.graphics.rectangle("fill", self.parent.x, self.parent.y, self.width, self.parent.height - 16) end + end + + self:SetDrawOrder() + + love.graphics.stencil(stencilfunc) + love.graphics.setStencilTest("greater", 0) + + local drawfunc = self.Draw or self.drawfunc + if drawfunc then + drawfunc(self) + end + + love.graphics.setStencilTest() + +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + + 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 + end + +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function newobject:mousereleased(x, y, button) + + local visible = self.visible + + if not visible then + return + end + +end + +--[[--------------------------------------------------------- + - func: GetOffsetY() + - desc: gets the object's y offset +--]]--------------------------------------------------------- +function newobject:GetOffsetY() + + return self.offsety + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/menuoption.lua b/loveframes/objects/internal/menuoption.lua new file mode 100644 index 0000000..37e9b1e --- /dev/null +++ b/loveframes/objects/internal/menuoption.lua @@ -0,0 +1,218 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- menuoption object +local newobject = loveframes.NewObject("menuoption", "loveframes_object_menuoption", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(parent, option_type, menu) + + self.type = "menuoption" + self.text = "Option" + self.width = 100 + self.height = 25 + self.contentwidth = 0 + self.contentheight = 0 + self.parent = parent + self.option_type = option_type or "option" + self.menu = menu + self.activated = false + self.internal = true + self.icon = false + self.func = nil + self:SetDrawFunc() +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 + + self:CheckHover() + + local hover = self.hover + local parent = self.parent + local option_type = self.option_type + local activated = self.activated + local base = loveframes.base + local update = self.Update + + if option_type == "submenu_activator" then + if hover and not activated then + self.menu:SetVisible(true) + self.menu:MoveToTop() + self.activated = true + elseif not hover and activated then + local hoverobject = loveframes.hoverobject + if hoverobject and hoverobject:GetBaseParent() == self.parent then + self.menu:SetVisible(false) + self.activated = false + end + elseif activated then + local screen_width = love.graphics.getWidth() + local screen_height = love.graphics.getHeight() + local sx = self.x + local sy = self.y + local width = self.width + local height = self.height + local x1 = sx + width + if x1 + self.menu.width <= screen_width then + self.menu.x = x1 + else + self.menu.x = sx - self.menu.width + end + if sy + self.menu.height <= screen_height then + self.menu.y = sy + else + self.menu.y = (sy + height) - self.menu.height + 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 + + if update then + update(self, dt) + 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 + +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 hover = self.hover + local option_type = self.option_type + if hover and option_type ~= "divider" and button == 1 then + local func = self.func + if func then + local text = self.text + func(self, text) + end + local basemenu = self.parent:GetBaseMenu() + basemenu:SetVisible(false) + end + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the object's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + self.text = text + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the object's text +--]]--------------------------------------------------------- +function newobject:GetText() + + return self.text + +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) + self.icon:setFilter("nearest", "nearest") + elseif type(icon) == "userdata" then + self.icon = icon + end +end + +--[[--------------------------------------------------------- + - func: GetIcon() + - desc: gets the object's icon +--]]--------------------------------------------------------- +function newobject:GetIcon() + + return self.icon + +end + +--[[--------------------------------------------------------- + - func: SetFunction(func) + - desc: sets the object's function +--]]--------------------------------------------------------- +function newobject:SetFunction(func) + + self.func = func + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/modalbackground.lua b/loveframes/objects/internal/modalbackground.lua new file mode 100644 index 0000000..b28d825 --- /dev/null +++ b/loveframes/objects/internal/modalbackground.lua @@ -0,0 +1,79 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- modalbackground class +local newobject = loveframes.NewObject("modalbackground", "loveframes_object_modalbackground", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(object) + + self.type = "modalbackground" + self.width = love.graphics.getWidth() + self.height = love.graphics.getHeight() + self.x = 0 + self.y = 0 + self.internal = true + self.parent = loveframes.base + self.object = object + + table.insert(loveframes.base.children, self) + + if self.object.type ~= "frame" then + self:Remove() + end + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the element +--]]--------------------------------------------------------- +function newobject:update(dt) + + local visible = self.visible + local alwaysupdate = self.alwaysupdate + + if not visible then + if not alwaysupdate then + return + end + end + + local object = self.object + local update = self.Update + local base = loveframes.base + local basechildren = base.children + + self:CheckHover() + + if #basechildren > 1 then + if basechildren[#basechildren - 1] ~= self then + self:Remove() + table.insert(basechildren, self) + end + end + + if not object:IsActive() then + self:Remove() + loveframes.modalobject = false + end + + if update then + update(self, dt) + end + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/multichoice/multichoicelist.lua b/loveframes/objects/internal/multichoice/multichoicelist.lua new file mode 100644 index 0000000..670290d --- /dev/null +++ b/loveframes/objects/internal/multichoice/multichoicelist.lua @@ -0,0 +1,420 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- multichoicelist class +local newobject = loveframes.NewObject("multichoicelist", "loveframes_object_multichoicelist", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(object) + + self.type = "multichoicelist" + self.parent = loveframes.base + self.list = object + self.x = object.x + self.y = object.y + self.list.height + self.width = self.list.width + self.height = 0 + self.clickx = 0 + self.clicky = 0 + self.padding = self.list.listpadding + self.spacing = self.list.listspacing + self.buttonscrollamount = object.buttonscrollamount + self.mousewheelscrollamount = object.mousewheelscrollamount + self.offsety = 0 + self.offsetx = 0 + self.extrawidth = 0 + self.extraheight = 0 + self.canremove = false + self.dtscrolling = self.list.dtscrolling + self.internal = true + self.vbar = false + self.children = {} + self.internals = {} + + for k, v in ipairs(object.choices) do + local row = loveframes.objects["multichoicerow"]:new() + row:SetText(v) + self:AddItem(row) + end + + table.insert(loveframes.base.internals, self) + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +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 width = love.graphics.getWidth() + local height = love.graphics.getHeight() + local x, y = love.mouse.getPosition() + local selfcol = loveframes.BoundingBox(x, self.x, y, self.y, 1, self.width, 1, self.height) + local parent = self.parent + local base = loveframes.base + local upadte = self.Update + local internals = self.internals + local children = self.children + + -- move to parent if there is a parent + if parent ~= base then + self.x = parent.x + self.staticx + self.y = parent.y + self.staticy + end + + if self.x < 0 then + self.x = 0 + end + + if self.x + self.width > width then + self.x = width - self.width + end + + if self.y < 0 then + self.y = 0 + end + + if self.y + self.height > height then + self.y = height - self.height + end + + for k, v in ipairs(internals) do + v:update(dt) + end + + for k, v in ipairs(children) do + v:update(dt) + v:SetClickBounds(self.x, self.y, self.width, self.height) + v.y = (v.parent.y + v.staticy) - self.offsety + v.x = (v.parent.x + v.staticx) - self.offsetx + end + + if upadte then + upadte(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: draw() + - desc: draws the object +--]]--------------------------------------------------------- +--[[ +function newobject:draw() + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + self:SetDrawOrder() + + local drawfunc = self.Draw or self.drawfunc + if drawfunc then + drawfunc(self) + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:draw() + end + end + + love.graphics.stencil(stencilfunc) + love.graphics.setStencilTest("greater", 0) + + local children = self.children + if children then + for k, v in ipairs(children) do + local col = loveframes.BoundingBox(self.x, v.x, self.y, v.y, self.width, v.width, self.height, v.height) + if col then + v:draw() + end + end + end + + love.graphics.setStencilTest() + + drawfunc = self.DrawOver or self.drawoverfunc + if drawfunc then + drawfunc(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 selfcol = loveframes.BoundingBox(x, self.x, y, self.y, 1, self.width, 1, self.height) + local internals = self.internals + local children = self.children + + if not selfcol and self.canremove and button == 1 then + self:Close() + 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 internals = self.internals + local children = self.children + + self.canremove = true + + 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: wheelmoved(x, y) + - desc: called when the player moves a mouse wheel +--]]--------------------------------------------------------- +function newobject:wheelmoved(x, y) + + local toplist = self:IsTopList() + local internals = self.internals + local scrollamount = self.mousewheelscrollamount + + if self.vbar and toplist then + local bar = internals[1].internals[1].internals[1] + local dtscrolling = self.dtscrolling + if dtscrolling then + local dt = love.timer.getDelta() + bar:Scroll(-y * scrollamount * dt) + else + bar:Scroll(-y * scrollamount) + end + end + +end + +--[[--------------------------------------------------------- + - func: AddItem(object) + - desc: adds an item to the object +--]]--------------------------------------------------------- +function newobject:AddItem(object) + + if object.type ~= "multichoicerow" then + return + end + + object.parent = self + table.insert(self.children, object) + + self:CalculateSize() + self:RedoLayout() + +end + +--[[--------------------------------------------------------- + - func: RemoveItem(object) + - desc: removes an item from the object +--]]--------------------------------------------------------- +function newobject:RemoveItem(object) + + local children = self.children + + for k, v in ipairs(children) do + if v == object then + table.remove(children, k) + end + end + + self:CalculateSize() + self:RedoLayout() + +end + +--[[--------------------------------------------------------- + - func: CalculateSize() + - desc: calculates the size of the object's children +--]]--------------------------------------------------------- +function newobject:CalculateSize() + + self.height = self.padding + + if self.list.listheight then + self.height = self.list.listheight + else + for k, v in ipairs(self.children) do + self.height = self.height + (v.height + self.spacing) + end + end + + if self.height > love.graphics.getHeight() then + self.height = love.graphics.getHeight() + end + + local numitems = #self.children + local height = self.height + local padding = self.padding + local spacing = self.spacing + local itemheight = self.padding + local vbar = self.vbar + local children = self.children + + for k, v in ipairs(children) do + itemheight = itemheight + v.height + spacing + end + + self.itemheight = (itemheight - spacing) + padding + + if self.itemheight > height then + self.extraheight = self.itemheight - height + if not vbar then + local scroll = loveframes.objects["scrollbody"]:new(self, "vertical") + table.insert(self.internals, scroll) + self.vbar = true + end + else + if vbar then + self.internals[1]:Remove() + self.vbar = false + self.offsety = 0 + end + end + +end + +--[[--------------------------------------------------------- + - func: RedoLayout() + - desc: used to redo the layour of the object +--]]--------------------------------------------------------- +function newobject:RedoLayout() + + local children = self.children + local padding = self.padding + local spacing = self.spacing + local starty = padding + local vbar = self.vbar + + if #children > 0 then + for k, v in ipairs(children) do + v.staticx = padding + v.staticy = starty + if vbar then + v.width = (self.width - self.internals[1].width) - padding * 2 + self.internals[1].staticx = self.width - self.internals[1].width + self.internals[1].height = self.height + else + v.width = self.width - padding * 2 + end + starty = starty + v.height + starty = starty + spacing + end + end + +end + +--[[--------------------------------------------------------- + - func: SetPadding(amount) + - desc: sets the object's padding +--]]--------------------------------------------------------- +function newobject:SetPadding(amount) + + self.padding = amount + +end + +--[[--------------------------------------------------------- + - func: SetSpacing(amount) + - desc: sets the object's spacing +--]]--------------------------------------------------------- +function newobject:SetSpacing(amount) + + self.spacing = amount + +end + +--[[--------------------------------------------------------- + - func: Close() + - desc: closes the object +--]]--------------------------------------------------------- +function newobject:Close() + + self:Remove() + self.list.haslist = false + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/multichoice/multichoicerow.lua b/loveframes/objects/internal/multichoice/multichoicerow.lua new file mode 100644 index 0000000..d19404c --- /dev/null +++ b/loveframes/objects/internal/multichoice/multichoicerow.lua @@ -0,0 +1,157 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- multichoicerow class +local newobject = loveframes.NewObject("multichoicerow", "loveframes_object_multichoicerow", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "multichoicerow" + self.text = "" + self.width = 50 + self.height = 25 + self.hover = false + self.internal = true + self.down = false + self.canclick = false + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + + 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() + + if not self.hover then + self.down = false + else + if loveframes.downobject == self then + self.down = true + end + end + + if not self.down and loveframes.downobject == self then + self.hover = true + end + + -- move to parent if there is a parent + if parent ~= base then + self.x = parent.x + self.staticx + self.y = parent.y + self.staticy + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + + local visible = self.visible + + if not visible then + return + end + + local hover = self.hover + + if hover and button == 1 then + self.down = true + loveframes.downobject = self + end + +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function newobject:mousereleased(x, y, button) + + local visible = self.visible + + if not visible then + return + end + + local text = self.text + + if self.hover and self.down and self.canclick and button == 1 then + self.parent.list:SelectChoice(text) + end + + self.down = false + self.canclick = true + +end + +--[[--------------------------------------------------------- + - func: keypressed(key, isrepeat) + - desc: called when the player presses a key +--]]--------------------------------------------------------- +function newobject:keypressed(key, isrepeat) + + local text = self.text + local selectedobject = loveframes.selectedobject + + if key == "return" and selectedobject == self then + self.parent.list:SelectChoice(text) + end + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the object's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + self.text = text + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the object's text +--]]--------------------------------------------------------- +function newobject:GetText() + + return self.text + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/scrollable/scrollarea.lua b/loveframes/objects/internal/scrollable/scrollarea.lua new file mode 100644 index 0000000..12f6ea8 --- /dev/null +++ b/loveframes/objects/internal/scrollable/scrollarea.lua @@ -0,0 +1,204 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- scrollarea class +local newobject = loveframes.NewObject("scrollarea", "loveframes_object_scrollarea", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(parent, bartype) + + self.type = "scroll-area" + self.bartype = bartype + self.parent = parent + self.x = 0 + self.y = 0 + self.scrolldelay = 0 + self.delayamount = 0.05 + self.down = false + self.internal = true + self.internals = {} + + table.insert(self.internals, loveframes.objects["scrollbar"]:new(self, bartype)) + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + + local visible = self.visible + local alwaysupdate = self.alwaysupdate + + if not visible then + if not alwaysupdate then + return + end + end + + self:CheckHover() + + local base = loveframes.base + local parent = self.parent + local pinternals = parent.internals + local button = pinternals[2] + local bartype = self.bartype + local time = love.timer.getTime() + local x, y = love.mouse.getPosition() + local listo = parent.parent + local down = self.down + local scrolldelay = self.scrolldelay + local delayamount = self.delayamount + local internals = self.internals + local bar = internals[1] + local hover = self.hover + local update = self.Update + + if button then + if bartype == "vertical" then + self.staticx = 0 + self.staticy = button.height - 1 + self.width = parent.width + self.height = parent.height - button.height*2 + 2 + elseif bartype == "horizontal" then + self.staticx = button.width - 1 + self.staticy = 0 + self.width = parent.width - button.width*2 + 2 + self.height = parent.height + end + end + + -- move to parent if there is a parent + if parent ~= base then + self.x = parent.x + self.staticx + self.y = parent.y + self.staticy + end + + if down then + if scrolldelay < time then + self.scrolldelay = time + delayamount + if self.bartype == "vertical" then + if y > bar.y then + bar:Scroll(bar.height) + else + bar:Scroll(-bar.height) + end + elseif self.bartype == "horizontal" then + if x > bar.x then + bar:Scroll(bar.width) + else + bar:Scroll(-bar.width) + end + end + end + if not hover then + self.down = false + end + end + + for k, v in ipairs(internals) do + v:update(dt) + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + + local visible = self.visible + + if not visible then + return + end + + local listo = self.parent.parent + local time = love.timer.getTime() + local internals = self.internals + local bar = internals[1] + local hover = self.hover + local delayamount = self.delayamount + + if hover and button == 1 then + self.down = true + self.scrolldelay = time + delayamount + 0.5 + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + if self.bartype == "vertical" then + if y > self.internals[1].y then + bar:Scroll(bar.height) + else + bar:Scroll(-bar.height) + end + elseif self.bartype == "horizontal" then + if x > bar.x then + bar:Scroll(bar.width) + else + bar:Scroll(-bar.width) + end + end + loveframes.downobject = self + 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 visible = self.visible + + if not visible then + return + end + + local internals = self.internals + + if button == 1 then + self.down = false + end + + for k, v in ipairs(internals) do + v:mousereleased(x, y, button) + end + +end + +--[[--------------------------------------------------------- + - func: GetBarType() + - desc: gets the object's bar type +--]]--------------------------------------------------------- +function newobject:GetBarType() + + return self.bartype + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/scrollable/scrollbar.lua b/loveframes/objects/internal/scrollable/scrollbar.lua new file mode 100644 index 0000000..846d1ea --- /dev/null +++ b/loveframes/objects/internal/scrollable/scrollbar.lua @@ -0,0 +1,358 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- scrollbar class +local newobject = loveframes.NewObject("scrollbar", "loveframes_object_scrollbar", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(parent, bartype) + + self.type = "scrollbar" + self.bartype = bartype + self.parent = parent + self.x = 0 + self.y = 0 + self.staticx = 0 + self.staticy = 0 + self.maxx = 0 + self.maxy = 0 + self.clickx = 0 + self.clicky = 0 + self.starty = 0 + self.lastwidth = 0 + self.lastheight = 0 + self.lastx = 0 + self.lasty = 0 + self.internal = true + self.hover = false + self.dragging = false + self.autoscroll = false + self.internal = true + + if self.bartype == "vertical" then + self.width = self.parent.width + self.height = 5 + elseif self.bartype == "horizontal" then + self.width = 5 + self.height = self.parent.height + end + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + + local visible = self.visible + local alwaysupdate = self.alwaysupdate + + if not visible then + if not alwaysupdate then + return + end + end + + self:CheckHover() + + local x, y = love.mouse.getPosition() + local bartype = self.bartype + local cols = {} + local basecols = {} + local dragging = self.dragging + + if bartype == "vertical" then + self.width = self.parent.width + elseif bartype == "horizontal" then + self.height = self.parent.height + end + + if bartype == "vertical" then + local parent = self.parent + local listo = parent.parent.parent + local height = parent.height * (listo.height/listo.itemheight) + if height < 20 then + self.height = 20 + else + self.height = height + end + self.maxy = parent.y + (parent.height - self.height) + self.x = parent.x + parent.width - self.width + self.y = parent.y + self.staticy + if dragging then + if self.staticy ~= self.lasty then + if listo.OnScroll then + listo.OnScroll(listo) + end + self.lasty = self.staticy + end + self.staticy = self.starty + (y - self.clicky) + end + local space = (self.maxy - parent.y) + local remaining = (0 + self.staticy) + local percent = remaining/space + local extra = listo.extraheight * percent + local autoscroll = self.autoscroll + local lastheight = self.lastheight + listo.offsety = 0 + extra + if self.staticy > space then + self.staticy = space + listo.offsety = listo.extraheight + end + if self.staticy < 0 then + self.staticy = 0 + listo.offsety = 0 + end + if autoscroll then + if listo.itemheight > lastheight then + local type = listo.type + self.lastheight = listo.itemheight + if type == "textinput" then + local indicatory = listo.indicatory + local font = listo.font + local theight = font:getHeight("a") + local y = listo.y + local height = listo.height + local linecount = #listo.lines + local parentheight = self.parent.height + if (indicatory + theight) > (y + height) then + self:Scroll(parentheight/linecount) + end + else + local maxy = self.maxy + self:Scroll(maxy) + end + end + end + elseif bartype == "horizontal" then + local parent = self.parent + local listo = self.parent.parent.parent + local width = self.parent.width * (listo.width/listo.itemwidth) + if width < 20 then + self.width = 20 + else + self.width = width + end + self.maxx = parent.x + (parent.width) - self.width + self.x = parent.x + self.staticx + self.y = parent.y + self.staticy + if dragging then + if self.staticx ~= self.lastx then + if listo.OnScroll then + listo.OnScroll(listo) + end + self.lastx = self.staticx + end + self.staticx = self.startx + (x - self.clickx) + end + local space = (self.maxx - parent.x) + local remaining = (0 + self.staticx) + local percent = remaining/space + local extra = listo.extrawidth * percent + local autoscroll = self.autoscroll + local lastwidth = self.lastwidth + listo.offsetx = 0 + extra + if self.staticx > space then + self.staticx = space + listo.offsetx = listo.extrawidth + end + + if self.staticx < 0 then + self.staticx = 0 + listo.offsetx = 0 + end + if autoscroll then + if listo.itemwidth > lastwidth then + self.lastwidth = listo.itemwidth + self:Scroll(self.maxx) + end + end + end + + local update = self.Update + if update then update(self, dt) end + +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + + local visible = self.visible + local hover = self.hover + + if not visible then + return + end + + if not hover then + return + end + + local baseparent = self:GetBaseParent() + + if baseparent.type == "frame" then + baseparent:MakeTop() + end + + local dragging = self.dragging + + if not dragging then + if button == 1 then + self.starty = self.staticy + self.startx = self.staticx + self.clickx = x + self.clicky = y + self.dragging = true + loveframes.downobject = self + end + end + +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function newobject:mousereleased(x, y, button) + + local visible = self.visible + + if not visible then + return + end + + if self.dragging then + self.dragging = false + end + +end + +--[[--------------------------------------------------------- + - func: SetMaxX(x) + - desc: sets the object's max x position +--]]--------------------------------------------------------- +function newobject:SetMaxX(x) + + self.maxx = x + +end + +--[[--------------------------------------------------------- + - func: SetMaxY(y) + - desc: sets the object's max y position +--]]--------------------------------------------------------- +function newobject:SetMaxY(y) + + self.maxy = y + +end + +--[[--------------------------------------------------------- + - func: Scroll(amount) + - desc: scrolls the object +--]]--------------------------------------------------------- +function newobject:Scroll(amount) + + local bartype = self.bartype + local listo = self.parent.parent.parent + local onscroll = listo.OnScroll + + if bartype == "vertical" then + local newy = (self.y + amount) + if newy > self.maxy then + self.staticy = self.maxy - self.parent.y + elseif newy < self.parent.y then + self.staticy = 0 + else + self.staticy = self.staticy + amount + end + elseif bartype == "horizontal" then + local newx = (self.x + amount) + if newx > self.maxx then + self.staticx = self.maxx - self.parent.x + elseif newx < self.parent.x then + self.staticx = 0 + else + self.staticx = self.staticx + amount + end + end + + if onscroll then + onscroll(listo) + end + +end + +--[[--------------------------------------------------------- + - func: ScrollTo(position) + - desc: scrolls the object +--]]--------------------------------------------------------- +function newobject:ScrollTo(position) + + local bartype = self.bartype + local listo = self.parent.parent.parent + local onscroll = listo.OnScroll + + if bartype == "vertical" then + local maxRealPos = self.parent.height - self.height + if position > 1 then + self.staticy = maxRealPos + elseif position < 0 then + self.staticy = 0 + else + self.staticy = position * maxRealPos + end + elseif bartype == "horizontal" then + local maxRealPos = self.parent.width - self.width + if position > 1 then + self.staticx = maxRealPos + elseif position < 0 then + self.staticx = 0 + else + self.staticx = position * maxRealPos + end + end + + if onscroll then + onscroll(listo) + end + +end + +--[[--------------------------------------------------------- + - func: IsDragging() + - desc: gets whether the object is being dragged or not +--]]--------------------------------------------------------- +function newobject:IsDragging() + + return self.dragging + +end + +--[[--------------------------------------------------------- + - func: GetBarType() + - desc: gets the object's bartype +--]]--------------------------------------------------------- +function newobject:GetBarType() + + return self.bartype + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/scrollable/scrollbody.lua b/loveframes/objects/internal/scrollable/scrollbody.lua new file mode 100644 index 0000000..6d2b172 --- /dev/null +++ b/loveframes/objects/internal/scrollable/scrollbody.lua @@ -0,0 +1,192 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- scrollbar class +local newobject = loveframes.NewObject("scrollbody", "loveframes_object_scrollbody", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(parent, bartype) + + self.type = "scrollbody" + self.bartype = bartype + self.parent = parent + self.x = 0 + self.y = 0 + self.internal = true + self.internals = {} + + if self.bartype == "vertical" then + self.width = 16 + self.height = self.parent.height + self.staticx = self.parent.width - self.width + self.staticy = 0 + elseif self.bartype == "horizontal" then + self.width = self.parent.width + self.height = 16 + self.staticx = 0 + self.staticy = self.parent.height - self.height + end + + table.insert(self.internals, loveframes.objects["scrollarea"]:new(self, bartype)) + + local bar = self.internals[1].internals[1] + + if self.bartype == "vertical" then + local upbutton = loveframes.objects["scrollbutton"]:new("up") + upbutton.staticx = 0 + self.width - upbutton.width + upbutton.staticy = 0 + upbutton.parent = self + upbutton.Update = function(object, dt) + upbutton.staticx = 0 + self.width - upbutton.width + upbutton.staticy = 0 + if object.down and object.hover then + local dtscrolling = self.parent.dtscrolling + if dtscrolling then + local dt = love.timer.getDelta() + bar:Scroll(-self.parent.buttonscrollamount * dt) + else + bar:Scroll(-self.parent.buttonscrollamount) + end + end + end + local downbutton = loveframes.objects["scrollbutton"]:new("down") + downbutton.parent = self + downbutton.staticx = 0 + self.width - downbutton.width + downbutton.staticy = 0 + self.height - downbutton.height + downbutton.Update = function(object, dt) + downbutton.staticx = 0 + self.width - downbutton.width + downbutton.staticy = 0 + self.height - downbutton.height + downbutton.x = downbutton.parent.x + downbutton.staticx + downbutton.y = downbutton.parent.y + downbutton.staticy + if object.down and object.hover then + local dtscrolling = self.parent.dtscrolling + if dtscrolling then + local dt = love.timer.getDelta() + bar:Scroll(self.parent.buttonscrollamount * dt) + else + bar:Scroll(self.parent.buttonscrollamount) + end + end + end + table.insert(self.internals, upbutton) + table.insert(self.internals, downbutton) + elseif self.bartype == "horizontal" then + local leftbutton = loveframes.objects["scrollbutton"]:new("left") + leftbutton.parent = self + leftbutton.staticx = 0 + leftbutton.staticy = 0 + leftbutton.Update = function(object, dt) + leftbutton.staticx = 0 + leftbutton.staticy = 0 + if object.down and object.hover then + local dtscrolling = self.parent.dtscrolling + if dtscrolling then + local dt = love.timer.getDelta() + bar:Scroll(-self.parent.buttonscrollamount * dt) + else + bar:Scroll(-self.parent.buttonscrollamount) + end + end + end + local rightbutton = loveframes.objects["scrollbutton"]:new("right") + rightbutton.parent = self + rightbutton.staticx = 0 + self.width - rightbutton.width + rightbutton.staticy = 0 + rightbutton.Update = function(object, dt) + rightbutton.staticx = 0 + self.width - rightbutton.width + rightbutton.staticy = 0 + rightbutton.x = rightbutton.parent.x + rightbutton.staticx + rightbutton.y = rightbutton.parent.y + rightbutton.staticy + if object.down and object.hover then + local dtscrolling = self.parent.dtscrolling + if dtscrolling then + local dt = love.timer.getDelta() + bar:Scroll(self.parent.buttonscrollamount * dt) + else + bar:Scroll(self.parent.buttonscrollamount) + end + end + end + table.insert(self.internals, leftbutton) + table.insert(self.internals, rightbutton) + end + + local parentstate = parent.state + self:SetState(parentstate) + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + + local visible = self.visible + local alwaysupdate = self.alwaysupdate + + if not visible then + if not alwaysupdate then + return + end + end + + self:CheckHover() + + local parent = self.parent + local base = loveframes.base + local update = self.Update + local internals = self.internals + + -- move to parent if there is a parent + if parent ~= base then + self.x = parent.x + self.staticx + self.y = parent.y + self.staticy + end + + -- resize to parent + if parent ~= base then + if self.bartype == "vertical" then + self.height = self.parent.height + self.staticx = self.parent.width - self.width + if parent.hbar then self.height = self.height - parent:GetHorizontalScrollBody().height end + elseif self.bartype == "horizontal" then + self.width = self.parent.width + self.staticy = self.parent.height - self.height + if parent.vbar then self.width = self.width - parent:GetVerticalScrollBody().width end + end + end + + for k, v in ipairs(internals) do + v:update(dt) + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: GetScrollBar() + - desc: gets the object's scroll bar +--]]--------------------------------------------------------- +function newobject:GetScrollBar() + + return self.internals[1].internals[1] + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/scrollable/scrollbutton.lua b/loveframes/objects/internal/scrollable/scrollbutton.lua new file mode 100644 index 0000000..7cde625 --- /dev/null +++ b/loveframes/objects/internal/scrollable/scrollbutton.lua @@ -0,0 +1,139 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- scrollbutton clas +local newobject = loveframes.NewObject("scrollbutton", "loveframes_object_scrollbutton", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(scrolltype) + + self.type = "scrollbutton" + self.scrolltype = scrolltype + self.width = 16 + self.height = 16 + self.down = false + self.internal = true + self.OnClick = function() end + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + + local visible = self.visible + local alwaysupdate = self.alwaysupdate + + if not visible then + if not alwaysupdate then + return + end + end + + self:CheckHover() + + local hover = self.hover + local parent = self.parent + local base = loveframes.base + local update = self.Update + + if not hover then + self.down = false + else + if loveframes.downobject == self then + self.down = true + end + end + + if not self.down and loveframes.downobject == self then + self.hover = true + end + + -- move to parent if there is a parent + if parent ~= base then + self.x = parent.x + self.staticx + self.y = parent.y + self.staticy + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + + 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.type == "frame" then + baseparent:MakeTop() + end + self.down = true + loveframes.downobject = self + end + +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function newobject:mousereleased(x, y, button) + + local visible = self.visible + + if not visible then + return + end + + local hover = self.hover + local down = self.down + local onclick = self.OnClick + + if hover and down then + if button == 1 then + onclick(x, y, self) + end + end + + self.down = false + +end + +--[[--------------------------------------------------------- + - func: GetScrollType() + - desc: gets the object's scroll type +--]]--------------------------------------------------------- +function newobject:GetScrollType() + + return self.scrolltype + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/sliderbutton.lua b/loveframes/objects/internal/sliderbutton.lua new file mode 100644 index 0000000..f22b276 --- /dev/null +++ b/loveframes/objects/internal/sliderbutton.lua @@ -0,0 +1,228 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- sliderbutton class +local newobject = loveframes.NewObject("sliderbutton", "loveframes_object_sliderbutton", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(parent) + + self.type = "sliderbutton" + self.width = 10 + self.height = 20 + self.staticx = 0 + self.staticy = 0 + self.startx = 0 + self.clickx = 0 + self.starty = 0 + self.clicky = 0 + self.intervals = true + self.internal = true + self.down = false + self.dragging = false + self.parent = parent + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + + local visible = self.visible + local alwaysupdate = self.alwaysupdate + + if not visible then + if not alwaysupdate then + return + end + end + + self:CheckHover() + + local x, y = love.mouse.getPosition() + local intervals = self.intervals + local progress = 0 + local nvalue = 0 + local pvalue = self.parent.value + local hover = self.hover + local down = self.down + local downobject = loveframes.downobject + local parent = self.parent + local slidetype = parent.slidetype + local dragging = self.dragging + local base = loveframes.base + local update = self.Update + + if not hover then + self.down = false + if downobject == self then + self.hover = true + end + else + if downobject == self then + self.down = true + end + end + + if not down and downobject == self then + self.hover = true + end + + -- move to parent if there is a parent + if parent ~= base then + self.x = parent.x + self.staticx + self.y = parent.y + self.staticy + end + + -- start calculations if the button is being dragged + if dragging then + -- calculations for horizontal sliders + if slidetype == "horizontal" then + self.staticx = self.startx + (x - self.clickx) + progress = self.staticx/(self.parent.width - self.width) + nvalue = self.parent.min + (self.parent.max - self.parent.min) * progress + nvalue = loveframes.Round(nvalue, self.parent.decimals) + -- calculations for vertical sliders + elseif slidetype == "vertical" then + self.staticy = self.starty + (y - self.clicky) + local space = self.parent.height - self.height + local remaining = (self.parent.height - self.height) - self.staticy + local percent = remaining/space + nvalue = self.parent.min + (self.parent.max - self.parent.min) * percent + nvalue = loveframes.Round(nvalue, self.parent.decimals) + end + if nvalue > self.parent.max then + nvalue = self.parent.max + end + if nvalue < self.parent.min then + nvalue = self.parent.min + end + self.parent.value = nvalue + if self.parent.value == -0 then + self.parent.value = math.abs(self.parent.value) + end + if nvalue ~= pvalue and nvalue >= self.parent.min and nvalue <= self.parent.max then + if self.parent.OnValueChanged then + self.parent.OnValueChanged(self.parent, self.parent.value) + end + end + loveframes.downobject = self + end + + if slidetype == "horizontal" then + if (self.staticx + self.width) > self.parent.width then + self.staticx = self.parent.width - self.width + end + if self.staticx < 0 then + self.staticx = 0 + end + end + + if slidetype == "vertical" then + if (self.staticy + self.height) > self.parent.height then + self.staticy = self.parent.height - self.height + end + if self.staticy < 0 then + self.staticy = 0 + end + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + + 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 + self.down = true + self.dragging = true + self.startx = self.staticx + self.clickx = x + self.starty = self.staticy + self.clicky = y + loveframes.downobject = self + end + +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function newobject:mousereleased(x, y, button) + + local visible = self.visible + + if not visible then + return + end + + local down = self.down + local dragging = self.dragging + + if dragging then + local parent = self.parent + local onrelease = parent.OnRelease + if onrelease then + onrelease(parent) + end + end + + self.down = false + self.dragging = false + +end + +--[[--------------------------------------------------------- + - func: MoveToX(x) + - desc: moves the object to the specified x position +--]]--------------------------------------------------------- +function newobject:MoveToX(x) + + self.staticx = x + +end + +--[[--------------------------------------------------------- + - func: MoveToY(y) + - desc: moves the object to the specified y position +--]]--------------------------------------------------------- +function newobject:MoveToY(y) + + self.staticy = y + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/tabbutton.lua b/loveframes/objects/internal/tabbutton.lua new file mode 100644 index 0000000..5816f90 --- /dev/null +++ b/loveframes/objects/internal/tabbutton.lua @@ -0,0 +1,205 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- tabbutton class +local newobject = loveframes.NewObject("tabbutton", "loveframes_object_tabbutton", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(parent, text, tabnumber, tip, image, onopened, onclosed) + + self.type = "tabbutton" + self.font = loveframes.smallfont + self.text = text + self.tabnumber = tabnumber + self.parent = parent + self.state = parent.state + self.staticx = 0 + self.staticy = 0 + self.width = 50 + self.height = 25 + self.internal = true + self.down = false + self.image = nil + self.OnOpened = nil + self.OnClosed = nil + + if tip then + self.tooltip = loveframes.objects["tooltip"]:new(self, tip) + self.tooltip:SetFollowCursor(false) + self.tooltip:SetFollowObject(true) + self.tooltip:SetOffsets(0, -(self.tooltip.internals[1]:GetHeight() + 12)) + end + + if image then + self:SetImage(image) + end + + if onopened then + self.OnOpened = onopened + end + + if onclosed then + self.OnClosed = onclosed + end + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + + self:SetDrawFunc() +end + +--[[--------------------------------------------------------- + - func: update(deltatime) + - desc: updates the object +--]]--------------------------------------------------------- +function newobject:update(dt) + + 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() + self:SetClickBounds(parent.x, parent.y, parent.width, parent.height) + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: mousepressed(x, y, button) + - desc: called when the player presses a mouse button +--]]--------------------------------------------------------- +function newobject:mousepressed(x, y, button) + + local visible = self.visible + + if not visible then + return + end + + local hover = self.hover + local internals = self.internals + + if hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + self.down = true + loveframes.downobject = self + end + +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function newobject:mousereleased(x, y, button) + + local visible = self.visible + + if not visible then + return + end + + local hover = self.hover + local parent = self.parent + local tabnumber = self.tabnumber + + if hover and button == 1 then + if button == 1 then + local tab = parent.tab + local internals = parent.internals + local onopened = self.OnOpened + local prevtab = internals[tab] + local onclosed = prevtab.OnClosed + parent:SwitchToTab(tabnumber) + if onopened then + onopened(self) + end + if onclosed then + onclosed(prevtab) + end + end + end + + self.down = false + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the object's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + self.text = text + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the object's text +--]]--------------------------------------------------------- +function newobject:GetText() + + return self.text + +end + +--[[--------------------------------------------------------- + - func: SetImage(image) + - desc: adds an image to the object +--]]--------------------------------------------------------- +function newobject:SetImage(image) + + if type(image) == "string" then + self.image = love.graphics.newImage(image) + self.image:setFilter("nearest", "nearest") + else + self.image = image + end +end + +--[[--------------------------------------------------------- + - func: GetImage() + - desc: gets the object's image +--]]--------------------------------------------------------- +function newobject:GetImage() + + return self.image + +end + +--[[--------------------------------------------------------- + - func: GetTabNumber() + - desc: gets the object's tab number +--]]--------------------------------------------------------- +function newobject:GetTabNumber() + + return self.tabnumber + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/tooltip.lua b/loveframes/objects/internal/tooltip.lua new file mode 100644 index 0000000..8c9095e --- /dev/null +++ b/loveframes/objects/internal/tooltip.lua @@ -0,0 +1,353 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- tooltip class +local newobject = loveframes.NewObject("tooltip", "loveframes_object_tooltip", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(object, text) + + self.type = "tooltip" + self.parent = loveframes.base + self.object = object or nil + self.width = 0 + self.height = 0 + self.padding = 5 + self.xoffset = 10 + self.yoffset = -10 + self.internal = true + self.followcursor = true + self.followobject = false + self.alwaysupdate = true + self.internals = {} + + -- create the object's text + local textobject = loveframes.Create("text") + textobject:Remove() + textobject.parent = self + textobject:SetText(text or "") + textobject:SetPos(10000, 0) -- textobject interferes with hover detection + table.insert(self.internals, textobject) + + -- apply template properties to the object + loveframes.ApplyTemplatesToObject(self) + table.insert(loveframes.base.internals, self) + + self:SetDrawFunc() +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 + local internals = self.internals + local textobject = internals[1] + + if not visible then + textobject:SetPos(10000, 0) -- textobject interferes with hover detection + if not alwaysupdate then + return + end + end + + local padding = self.padding + local object = self.object + local draworder = self.draworder + local update = self.Update + + self.width = textobject.width + padding * 2 + self.height = textobject.height + padding * 2 + + if object then + if object == loveframes.base then + self:Remove() + return + end + --local ovisible = object.visible + local ohover = object.hover + local ostate = object.state + if ostate ~= state then + self.visible = false + return + end + self.visible = ohover + if ohover then + local top = self:IsTopInternal() + local followcursor = self.followcursor + local followobject = self.followobject + local xoffset = self.xoffset + local yoffset = self.yoffset + if followcursor then + local height = self.height + local mx, my = love.mouse.getPosition() + self.x = mx + xoffset + self.y = my - height + yoffset + elseif followobject then + local ox = object.x + local oy = object.y + self.x = ox + xoffset + self.y = oy + yoffset + end + if not top then + self:MoveToTop() + end + textobject:SetPos(padding, padding) + end + end + + --textobject:SetVisible(self.show) + textobject:SetState(selfstate) + textobject:update(dt) + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: SetFollowCursor(bool) + - desc: sets whether or not the tooltip should follow the + cursor +--]]--------------------------------------------------------- +function newobject:SetFollowCursor(bool) + + self.followcursor = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetFollowCursor() + - desc: gets whether or not the tooltip should follow the + cursor +--]]--------------------------------------------------------- +function newobject:GetFollowCursor() + + return self.followcursor + +end + +--[[--------------------------------------------------------- + - func: SetObject(object) + - desc: sets the tooltip's object +--]]--------------------------------------------------------- +function newobject:SetObject(object) + + self.object = object + self.x = object.x + self.y = object.y + + return self + +end + +--[[--------------------------------------------------------- + - func: GetObject() + - desc: gets the tooltip's object +--]]--------------------------------------------------------- +function newobject:GetObject() + + return self.object + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the tooltip's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + local internals = self.internals + local textobject = internals[1] + + textobject:SetText(text) + return self + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the tooltip's text +--]]--------------------------------------------------------- +function newobject:GetText() + + local internals = self.internals + local textobject = internals[1] + local text = textobject:GetText() + + return text + +end + +--[[--------------------------------------------------------- + - func: SetTextMaxWidth(text) + - desc: sets the tooltip's text max width +--]]--------------------------------------------------------- +function newobject:SetTextMaxWidth(width) + + local internals = self.internals + local textobject = internals[1] + + textobject:SetMaxWidth(width) + return self + +end + +--[[--------------------------------------------------------- + - func: SetOffsetX(xoffset) + - desc: sets the tooltip's x offset +--]]--------------------------------------------------------- +function newobject:SetOffsetX(xoffset) + + self.xoffset = xoffset + return self + +end + +--[[--------------------------------------------------------- + - func: GetOffsetX() + - desc: gets the tooltip's x offset +--]]--------------------------------------------------------- +function newobject:GetOffsetX() + + return self.xoffset + +end + +--[[--------------------------------------------------------- + - func: SetOffsetY(yoffset) + - desc: sets the tooltip's y offset +--]]--------------------------------------------------------- +function newobject:SetOffsetY(yoffset) + + self.yoffset = yoffset + return self + +end + +--[[--------------------------------------------------------- + - func: GetOffsetY() + - desc: gets the tooltip's y offset +--]]--------------------------------------------------------- +function newobject:GetOffsetY() + + return self.yoffset + +end + +--[[--------------------------------------------------------- + - func: SetOffsets(xoffset, yoffset) + - desc: sets the tooltip's x and y offset +--]]--------------------------------------------------------- +function newobject:SetOffsets(xoffset, yoffset) + + self.xoffset = xoffset + self.yoffset = yoffset + + return self + +end + +--[[--------------------------------------------------------- + - func: GetOffsets() + - desc: gets the tooltip's x and y offset +--]]--------------------------------------------------------- +function newobject:GetOffsets() + + return self.xoffset, self.yoffset + +end + +--[[--------------------------------------------------------- + - func: SetPadding(padding) + - desc: sets the tooltip's padding +--]]--------------------------------------------------------- +function newobject:SetPadding(padding) + + self.padding = padding + return self + +end + +--[[--------------------------------------------------------- + - func: GetPadding() + - desc: gets the tooltip's padding +--]]--------------------------------------------------------- +function newobject:GetPadding() + + return self.padding + +end + +--[[--------------------------------------------------------- + - func: SetFont(font) + - desc: sets the tooltip's font +--]]--------------------------------------------------------- +function newobject:SetFont(font) + + local internals = self.internals + local textobject = internals[1] + + textobject:SetFont(font) + return self + +end + +--[[--------------------------------------------------------- + - func: GetFont() + - desc: gets the tooltip's font +--]]--------------------------------------------------------- +function newobject:GetFont() + + local internals = self.internals + local textobject = internals[1] + + return textobject:GetFont() + +end + +--[[--------------------------------------------------------- + - func: SetFollowObject(bool) + - desc: sets whether or not the tooltip should follow + its assigned object +--]]--------------------------------------------------------- +function newobject:SetFollowObject(bool) + + self.followobject = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetFollowObject() + - desc: gets whether or not the tooltip should follow + its assigned object +--]]--------------------------------------------------------- +function newobject:GetFollowObject() + + return self.followobject + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/treenode.lua b/loveframes/objects/internal/treenode.lua new file mode 100644 index 0000000..5ec4e29 --- /dev/null +++ b/loveframes/objects/internal/treenode.lua @@ -0,0 +1,296 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- button object +local newobject = loveframes.NewObject("treenode", "loveframes_object_treenode", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "treenode" + self.text = "Node" + self.width = 250 + self.height = 16 + self.level = 0 + self.leftpadding = 0 + self.lastclick = 0 + self.open = false + self.internal = true + self.internals = {} + self.icon = nil + self.OnOpen = nil + self.OnClose = nil + + self:SetDrawFunc() +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 + + self:CheckHover() + + local parent = self.parent + local base = loveframes.base + local update = self.Update + + local tree = self.tree + self:SetClickBounds(tree.x, tree.y, tree.width, tree.height) + + for k, v in ipairs(self.internals) do + if v.type == "treenode" then + if self.open then + v.x = v.tree.x - v.tree.offsetx + v.y = (v.tree.y + self.tree.itemheight) - v.tree.offsety + if v.width > self.tree.itemwidth then + self.tree.itemwidth = v.width + end + self.tree.itemheight = self.tree.itemheight + v.height + v:update(dt) + end + else + v:update(dt) + end + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: draw() + - desc: draws the object +--]]--------------------------------------------------------- +function newobject:draw() + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + -- set the object's draw order + self:SetDrawOrder() + + local drawfunc = self.Draw or self.drawfunc + if drawfunc then + drawfunc(self) + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + if v.type == "treenode" then + if self.open then + v:draw() + end + else + v:draw() + end + end + 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 + + for k, v in ipairs(self.internals) do + v:mousepressed(x, y, button) + end + + if self.hover and button == 1 then + local time = os.time() + if self.lastclick + 0.40 > time then + self.open = not self.open + end + self.lastclick = time + local onselectnode = self.tree.OnSelectNode + self.tree.selectednode = self + if onselectnode then + onselectnode(self.parent, self) + end + 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 + + for k, v in ipairs(self.internals) do + v:mousereleased(x, y, button) + end + +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) + self.icon:setFilter("nearest", "nearest") + else + self.icon = icon + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetIcon() + - desc: gets the object's icon +--]]--------------------------------------------------------- +function newobject:GetIcon() + + return self.icon + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the object's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + self.text = text + return self + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the object's text +--]]--------------------------------------------------------- +function newobject:GetText() + + return self.text + +end + +--[[--------------------------------------------------------- + - func: AddNode(text) + - desc: adds a new node to the object +--]]--------------------------------------------------------- +function newobject:AddNode(text) + + if not self.internals[1] then + local openbutton = loveframes.objects["treenodebutton"]:new() + openbutton.parent = self + openbutton.staticx = 2 + openbutton.staticy = 2 + table.insert(self.internals, openbutton) + end + + local node = loveframes.objects["treenode"]:new() + node.parent = self + node.tree = self.tree + node.text = text + node.level = self.level + 1 + table.insert(self.internals, node) + return node + +end + +--[[--------------------------------------------------------- + - func: RemoveNode(id) + - desc: removes a node from the object +--]]--------------------------------------------------------- +function newobject:RemoveNode(id) + + id = id + 1 + for k, v in ipairs(self.internals) do + if k == id then + v:Remove() + break + end + end + +end + +--[[--------------------------------------------------------- + - func: SetOpen(bool) + - desc: sets whether or not the object is open +--]]--------------------------------------------------------- +function newobject:SetOpen(bool) + + self.open = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetOpen() + - desc: gets whether or not the object is open +--]]--------------------------------------------------------- +function newobject:GetOpen() + + return self.open + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/internal/treenodebutton.lua b/loveframes/objects/internal/treenodebutton.lua new file mode 100644 index 0000000..4e00033 --- /dev/null +++ b/loveframes/objects/internal/treenodebutton.lua @@ -0,0 +1,108 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- button object +local newobject = loveframes.NewObject("treenodebutton", "loveframes_object_treenodebutton", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "treenodebutton" + self.width = 16 + self.height = 16 + self.internal = true + + self:SetDrawFunc() +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 + + self:CheckHover() + + local parent = self.parent + local base = loveframes.base + local update = self.Update + + -- 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: 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 bool = not self.parent.open + if bool then + local onopen = self.parent.OnOpen + if onopen then + onopen(self.parent) + end + else + local onclose = self.parent.OnClose + if onclose then + onclose(self.parent) + end + end + self.parent:SetOpen(bool) + print("!") + print(self.parent.level) + end + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/list.lua b/loveframes/objects/list.lua new file mode 100644 index 0000000..dec8997 --- /dev/null +++ b/loveframes/objects/list.lua @@ -0,0 +1,807 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- list object +local newobject = loveframes.NewObject("list", "loveframes_object_list", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "list" + self.display = "vertical" + self.width = 300 + self.height = 150 + self.clickx = 0 + self.clicky = 0 + self.padding = 0 + self.spacing = 0 + self.offsety = 0 + self.offsetx = 0 + self.extrawidth = 0 + self.extraheight = 0 + self.buttonscrollamount = 0.10 + self.mousewheelscrollamount = 10 + self.internal = false + self.hbar = false + self.vbar = false + self.autoscroll = false + self.horizontalstacking = false + self.dtscrolling = false + self.internals = {} + self.children = {} + self.OnScroll = nil + + self:SetDrawFunc() +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 + + self:CheckHover() + + local internals = self.internals + local children = self.children + local display = self.display + local parent = self.parent + local horizontalstacking = self.horizontalstacking + local base = loveframes.base + local update = self.Update + + -- 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 + + for k, v in ipairs(internals) do + v:update(dt) + for _, p in pairs(self:GetParents()) do + v.x = v.x - (p.offsetx or 0) + v.y = v.y - (p.offsety or 0) + end + end + + local x = self.x + local y = self.y + local width = self.width + local height = self.height + local offsetx = self.offsetx + local offsety = self.offsety + + for k, v in ipairs(children) do + v:update(dt) + v:SetClickBounds(x, y, width, height) + v.x = (v.parent.x + v.staticx) - offsetx + v.y = (v.parent.y + v.staticy) - offsety + for _, p in pairs(self:GetParents()) do + v.x = v.x - (p.offsetx or 0) + v.y = v.y - (p.offsety or 0) + end + if display == "vertical" then + if v.lastheight ~= v.height then + self:CalculateSize() + self:RedoLayout() + end + end + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: draw() + - desc: draws the object +--]]--------------------------------------------------------- +function newobject:draw() + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local x = self.x + local y = self.y + local width = self.width + local height = self.height + local stencilfunc = function() + love.graphics.rectangle("fill", x, y, width, height) + end + + self:SetDrawOrder() + + local drawfunc = self.Draw or self.drawfunc + if drawfunc then + drawfunc(self) + end + + love.graphics.stencil(stencilfunc) + love.graphics.setStencilTest("greater", 0) + + local children = self.children + if children then + for k, v in ipairs(children) do + local col = loveframes.BoundingBox(x, v.x, y, v.y, width, v.width, height, v.height) + if col then + v:draw() + end + end + end + + love.graphics.setStencilTest() + + local drawfunc = self.DrawOver or self.drawoverfunc + if drawfunc then + drawfunc(self) + end + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:draw() + end + 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 children = self.children + local internals = self.internals + + if hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent: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: wheelmoved(x, y) + - desc: called when the player moves a mouse wheel +--]]--------------------------------------------------------- +function newobject:wheelmoved(x, y) + + local toplist = self:IsTopList() + local vbar = self.vbar + local hbar = self.hbar + local scrollamount = self.mousewheelscrollamount + + if (vbar or hbar) and toplist then + local bar = self:GetScrollBar() + local dtscrolling = self.dtscrolling + if dtscrolling then + local dt = love.timer.getDelta() + bar:Scroll(-y * scrollamount * dt) + else + bar:Scroll(-y * scrollamount) + end + end + +end + +--[[--------------------------------------------------------- + - func: AddItem(object) + - desc: adds an item to the object +--]]--------------------------------------------------------- +function newobject:AddItem(object) + + local objtype = object.type + if objtype == "frame" then + return + end + + local children = self.children + local state = self.state + + -- remove the item object from its current parent and make its new parent the list object + object:Remove() + object.parent = self + object:SetState(state) + + -- insert the item object into the list object's children table + table.insert(children, object) + + -- resize the list and redo its layout + self:CalculateSize() + self:RedoLayout() + + return self + +end + +--[[--------------------------------------------------------- + - func: RemoveItem(object or number) + - desc: removes an item from the object +--]]--------------------------------------------------------- +function newobject:RemoveItem(data) + + local dtype = type(data) + + if dtype == "number" then + local children = self.children + local item = children[data] + if item then + item:Remove() + end + else + data:Remove() + end + + self:CalculateSize() + self:RedoLayout() + + return self + +end + +--[[--------------------------------------------------------- + - func: CalculateSize() + - desc: calculates the size of the object's children +--]]--------------------------------------------------------- +function newobject:CalculateSize() + + local numitems = #self.children + local height = self.height + local width = self.width + local padding = self.padding + local spacing = self.spacing + local itemheight = self.padding + local itemwidth = self.padding + local display = self.display + local vbar = self.vbar + local hbar = self.hbar + local internals = self.internals + local children = self.children + local horizontalstacking = self.horizontalstacking + + if display == "vertical" then + if horizontalstacking then + local curwidth = 0 + local maxwidth = width - padding * 2 + local prevheight = 0 + local scrollbar = self:GetScrollBar() + if scrollbar then + maxwidth = maxwidth - scrollbar.width + end + for k, v in ipairs(children) do + if v.height > prevheight then + prevheight = v.height + end + curwidth = curwidth + v.width + spacing + if children[k + 1] then + if curwidth + children[k + 1].width > maxwidth then + curwidth = padding + itemheight = itemheight + prevheight + spacing + prevheight = 0 + end + else + itemheight = itemheight + prevheight + padding + end + end + self.itemheight = itemheight + else + for k, v in ipairs(children) do + itemheight = itemheight + v.height + spacing + end + self.itemheight = (itemheight - spacing) + padding + end + local itemheight = self.itemheight + if itemheight > height then + self.extraheight = itemheight - height + if not vbar then + local scrollbar = loveframes.objects["scrollbody"]:new(self, display) + table.insert(internals, scrollbar) + self.vbar = true + self:GetScrollBar().autoscroll = self.autoscroll + end + else + if vbar then + local bar = internals[1] + bar:Remove() + self.vbar = false + self.offsety = 0 + end + end + elseif display == "horizontal" then + for k, v in ipairs(children) do + itemwidth = itemwidth + v.width + spacing + end + self.itemwidth = (itemwidth - spacing) + padding + local itemwidth = self.itemwidth + if itemwidth > width then + self.extrawidth = itemwidth - width + if not hbar then + local scrollbar = loveframes.objects["scrollbody"]:new(self, display) + table.insert(internals, scrollbar) + self.hbar = true + self:GetScrollBar().autoscroll = self.autoscroll + end + else + if hbar then + local bar = internals[1] + bar:Remove() + self.hbar = false + self.offsetx = 0 + end + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: RedoLayout() + - desc: used to redo the layout of the object +--]]--------------------------------------------------------- +function newobject:RedoLayout() + + local width = self.width + local height = self.height + local children = self.children + local internals = self.internals + local padding = self.padding + local spacing = self.spacing + local starty = padding + local startx = padding + local vbar = self.vbar + local hbar = self.hbar + local display = self.display + local horizontalstacking = self.horizontalstacking + local scrollbody, scrollbodywidth, scrollbodyheight + + if vbar or hbar then + scrollbody = internals[1] + scrollbodywidth = scrollbody.width + scrollbodyheight = scrollbody.height + end + + if #children > 0 then + if display == "vertical" then + if horizontalstacking then + local curwidth = padding + local curheight = padding + local maxwidth = self.width - padding * 2 + local prevheight = 0 + local scrollbar = self:GetScrollBar() + if scrollbar then + maxwidth = maxwidth - scrollbar.width + end + for k, v in ipairs(children) do + local itemheight = v.height + v.lastheight = itemheight + v.staticx = curwidth + v.staticy = curheight + if v.height > prevheight then + prevheight = v.height + end + if children[k + 1] then + curwidth = curwidth + v.width + spacing + if curwidth + (children[k + 1].width) > maxwidth then + curwidth = padding + curheight = curheight + prevheight + spacing + prevheight = 0 + end + end + end + else + for k, v in ipairs(children) do + local itemwidth = v.width + local itemheight = v.height + local retainsize = v.retainsize + v.staticx = padding + v.staticy = starty + v.lastheight = itemheight + if vbar then + if itemwidth + padding > (width - scrollbodywidth) then + v:SetWidth((width - scrollbodywidth) - (padding * 2)) + end + if not retainsize then + v:SetWidth((width - scrollbodywidth) - (padding * 2)) + end + scrollbody.staticx = width - scrollbodywidth + scrollbody.height = height + else + if not retainsize then + v:SetWidth(width - (padding * 2)) + end + end + starty = starty + itemheight + starty = starty + spacing + end + end + elseif display == "horizontal" then + for k, v in ipairs(children) do + local itemwidth = v.width + local itemheight = v.height + local retainsize = v.retainsize + v.staticx = startx + v.staticy = padding + if hbar then + if itemheight + padding > (height - scrollbodyheight) then + v:SetHeight((height - scrollbodyheight) - (padding * 2)) + end + if not retainsize then + v:SetHeight((height - scrollbodyheight) - (padding * 2)) + end + scrollbody.staticy = height - scrollbodyheight + scrollbody.width = width + else + if not retainsize then + v:SetHeight(height - (padding * 2)) + end + end + startx = startx + itemwidth + startx = startx + spacing + end + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetDisplayType(type) + - desc: sets the object's display type +--]]--------------------------------------------------------- +function newobject:SetDisplayType(type) + + local children = self.children + local numchildren = #children + + self.display = type + self.vbar = false + self.hbar = false + self.offsetx = 0 + self.offsety = 0 + self.internals = {} + + if numchildren > 0 then + self:CalculateSize() + self:RedoLayout() + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetDisplayType() + - desc: gets the object's display type +--]]--------------------------------------------------------- +function newobject:GetDisplayType() + + return self.display + +end + +--[[--------------------------------------------------------- + - func: SetPadding(amount) + - desc: sets the object's padding +--]]--------------------------------------------------------- +function newobject:SetPadding(amount) + + local children = self.children + local numchildren = #children + + self.padding = amount + + if numchildren > 0 then + self:CalculateSize() + self:RedoLayout() + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetSpacing(amount) + - desc: sets the object's spacing +--]]--------------------------------------------------------- +function newobject:SetSpacing(amount) + + local children = self.children + local numchildren = #children + + self.spacing = amount + + if numchildren > 0 then + self:CalculateSize() + self:RedoLayout() + end + + return self + +end + +--[[--------------------------------------------------------- + - func: Clear() + - desc: removes all of the object's children +--]]--------------------------------------------------------- +function newobject:Clear() + + self.children = {} + self:CalculateSize() + self:RedoLayout() + + return self + +end + +--[[--------------------------------------------------------- + - func: SetWidth(width, relative) + - desc: sets the object's width +--]]--------------------------------------------------------- +function newobject:SetWidth(width, relative) + + if relative then + self.width = self.parent.width * width + else + self.width = width + end + + self:CalculateSize() + self:RedoLayout() + + return self + +end + +--[[--------------------------------------------------------- + - func: SetHeight(height, relative) + - desc: sets the object's height +--]]--------------------------------------------------------- +function newobject:SetHeight(height, relative) + + if relative then + self.height = self.parent.height * height + else + self.height = height + end + + self:CalculateSize() + self:RedoLayout() + + return self + +end + +--[[--------------------------------------------------------- + - func: SetSize(width, height, r1, r2) + - desc: sets the object's size +--]]--------------------------------------------------------- +function newobject:SetSize(width, height, r1, r2) + + if r1 then + self.width = self.parent.width * width + else + self.width = width + end + + if r2 then + self.height = self.parent.height * height + else + self.height = height + end + + self:CalculateSize() + self:RedoLayout() + + return self + +end + +--[[--------------------------------------------------------- + - func: GetScrollBar() + - desc: gets the object's scroll bar +--]]--------------------------------------------------------- +function newobject:GetScrollBar() + + local vbar = self.vbar + local hbar = self.hbar + local internals = self.internals + + if vbar or hbar then + local scrollbody = internals[1] + local scrollarea = scrollbody.internals[1] + local scrollbar = scrollarea.internals[1] + return scrollbar + else + return false + end + +end + +--[[--------------------------------------------------------- + - func: SetAutoScroll(bool) + - desc: sets whether or not the list's scrollbar should + auto scroll to the bottom when a new object is + added to the list +--]]--------------------------------------------------------- +function newobject:SetAutoScroll(bool) + + local scrollbar = self:GetScrollBar() + + self.autoscroll = bool + + if scrollbar then + scrollbar.autoscroll = bool + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetAutoScroll() + - desc: gets whether or not the list's scrollbar should + auto scroll to the bottom when a new object is + added to the list +--]]--------------------------------------------------------- +function newobject:GetAutoScroll() + + return self.autoscroll + +end + +--[[--------------------------------------------------------- + - func: SetButtonScrollAmount(speed) + - desc: sets the scroll amount of the object's scrollbar + buttons +--]]--------------------------------------------------------- +function newobject:SetButtonScrollAmount(amount) + + self.buttonscrollamount = amount + return self + +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 + return self + +end + +--[[--------------------------------------------------------- + - func: GetMouseWheelScrollAmount() + - desc: gets the scroll amount of the mouse wheel +--]]--------------------------------------------------------- +function newobject:GetButtonScrollAmount() + + return self.mousewheelscrollamount + +end + +--[[--------------------------------------------------------- + - func: EnableHorizontalStacking(bool) + - desc: enables or disables horizontal stacking +--]]--------------------------------------------------------- +function newobject:EnableHorizontalStacking(bool) + + local children = self.children + local numchildren = #children + + self.horizontalstacking = bool + + if numchildren > 0 then + self:CalculateSize() + self:RedoLayout() + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetHorizontalStacking() + - desc: gets whether or not the object allows horizontal + stacking +--]]--------------------------------------------------------- +function newobject:GetHorizontalStacking() + + return self.horizontalstacking + +end + +--[[--------------------------------------------------------- + - func: SetDTScrolling(bool) + - desc: sets whether or not the object should use delta + time when scrolling +--]]--------------------------------------------------------- +function newobject:SetDTScrolling(bool) + + self.dtscrolling = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetDTScrolling() + - desc: gets whether or not the object should use delta + time when scrolling +--]]--------------------------------------------------------- +function newobject:GetDTScrolling() + + return self.dtscrolling + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/menu.lua b/loveframes/objects/menu.lua new file mode 100644 index 0000000..bbc47b2 --- /dev/null +++ b/loveframes/objects/menu.lua @@ -0,0 +1,287 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- menu object +local newobject = loveframes.NewObject("menu", "loveframes_object_menu", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize(menu) + + self.type = "menu" + self.width = 80 + self.height = 25 + self.largest_item_width = 0 + self.largest_item_height = 0 + self.is_sub_menu = false + self.internal = false + self.parentmenu = nil + self.options = {} + self.internals = {} + + self:SetDrawFunc() +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 + + self:CheckHover() + + local hover = self.hover + local parent = self.parent + local base = loveframes.base + local update = self.Update + + -- 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 + + for k, v in ipairs(self.internals) do + local width = v.contentwidth + local height = v.contentheight + if width > self.largest_item_width then + self.largest_item_width = width + end + if height > self.largest_item_height then + self.largest_item_height = height + end + end + + local y = 0 + self.height = 0 + + for k, v in ipairs(self.internals) do + v:SetWidth(self.largest_item_width) + if v.option_type ~= "divider" then + v:SetHeight(self.largest_item_height) + else + v:SetHeight(5) + end + v:SetY(y) + self.height = self.height + v.height + y = y + v.height + v:update(dt) + end + + self.width = self.largest_item_width + self.largest_item_width = 0 + self.largest_item_height = 0 + + local screen_width = love.graphics.getWidth() + local screen_height = love.graphics.getHeight() + local sx = self.x + local sy = self.y + local width = self.width + local height = self.height + local x1 = sx + width + if sx + width > screen_width then + self.x = screen_width - width + end + if sy + self.height > screen_height then + self.y = screen_height - self.height + end + + if update then + update(self, dt) + 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 + +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: AddOption(text, icon, func) + - desc: adds an option to the object +--]]--------------------------------------------------------- +function newobject:AddOption(text, icon, func) + + local menuoption = loveframes.objects["menuoption"]:new(self) + menuoption:SetText(text) + menuoption:SetIcon(icon) + menuoption:SetFunction(func) + + table.insert(self.internals, menuoption) + return self + +end + +--[[--------------------------------------------------------- + - func: RemoveOption(id) + - desc: removes an option +--]]--------------------------------------------------------- +function newobject:RemoveOption(id) + + for k, v in ipairs(self.internals) do + if k == id then + table.remove(self.internals, k) + return + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: AddSubMenu(text, icon, menu) + - desc: adds a submenu to the object +--]]--------------------------------------------------------- +function newobject:AddSubMenu(text, icon, menu) + + local function activatorFunc(object) + if menu:GetVisible() then + local hoverobject = loveframes.hoverobject + if hoverobject ~= object and hoverobject:GetBaseParent() ~= menu then + menu:SetVisible(false) + end + else + menu:SetVisible(true) + menu:SetPos(object:GetX() + object:GetWidth(), object:GetY()) + end + end + + menu:SetVisible(false) + + local menuoption = loveframes.objects["menuoption"]:new(self, "submenu_activator", menu) + menuoption:SetText(text) + menuoption:SetIcon(icon) + + if menu then + menu.is_sub_menu = true + menu.parentmenu = self + end + + table.insert(self.internals, menuoption) + return self + +end + +--[[--------------------------------------------------------- + - func: AddDivider() + - desc: adds a divider to the object +--]]--------------------------------------------------------- +function newobject:AddDivider() + + local menuoption = loveframes.objects["menuoption"]:new(self, "divider") + + table.insert(self.internals, menuoption) + return self + +end + +--[[--------------------------------------------------------- + - func: GetBaseMenu(t) + - desc: gets the object's base menu +--]]--------------------------------------------------------- +function newobject:GetBaseMenu(t) + + local t = t or {} + + if self.parentmenu then + table.insert(t, self.parentmenu) + self.parentmenu:GetBaseMenu(t) + else + return self + end + + return t[#t] + +end + +--[[--------------------------------------------------------- + - func: SetVisible(bool) + - desc: sets the object's visibility +--]]--------------------------------------------------------- +function newobject:SetVisible(bool) + + self.visible = bool + + if not bool then + local internals = self.internals + for k, v in ipairs(internals) do + if v.menu then + v.activated = false + v.menu:SetVisible(bool) + end + end + end + + return self + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/multichoice.lua b/loveframes/objects/multichoice.lua new file mode 100644 index 0000000..4323fc2 --- /dev/null +++ b/loveframes/objects/multichoice.lua @@ -0,0 +1,418 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- multichoice object +local newobject = loveframes.NewObject("multichoice", "loveframes_object_multichoice", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "multichoice" + self.choice = "" + self.text = "Select an option" + self.width = 200 + self.height = 25 + self.listpadding = 0 + self.listspacing = 0 + self.buttonscrollamount = 200 + self.mousewheelscrollamount = 1500 + self.sortfunc = function(a, b) return a < b end + self.haslist = false + self.dtscrolling = true + self.enabled = true + self.internal = false + self.choices = {} + self.listheight = nil + + self:SetDrawFunc() +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() + + -- 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: 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 haslist = self.haslist + local enabled = self.enabled + + if hover and not haslist and enabled and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + self.haslist = true + self.list = loveframes.objects["multichoicelist"]:new(self) + self.list:SetState(self.state) + loveframes.downobject = self + 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 + +end + +--[[--------------------------------------------------------- + - func: AddChoice(choice) + - desc: adds a choice to the current list of choices +--]]--------------------------------------------------------- +function newobject:AddChoice(choice) + + local choices = self.choices + table.insert(choices, choice) + + return self + +end + +--[[--------------------------------------------------------- + - func: RemoveChoice(choice) + - desc: removes the specified choice from the object's + list of choices +--]]--------------------------------------------------------- +function newobject:RemoveChoice(choice) + + local choices = self.choices + + for k, v in ipairs(choices) do + if v == choice then + table.remove(choices, k) + break + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetChoice(choice) + - desc: sets the current choice +--]]--------------------------------------------------------- +function newobject:SetChoice(choice) + + self.choice = choice + return self + +end + +--[[--------------------------------------------------------- + - func: SelectChoice(choice) + - desc: selects a choice +--]]--------------------------------------------------------- +function newobject:SelectChoice(choice) + + local onchoiceselected = self.OnChoiceSelected + + self.choice = choice + + if self.list then + self.list:Close() + end + + if onchoiceselected then + onchoiceselected(self, choice) + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetListHeight(height) + - desc: sets the height of the list of choices +--]]--------------------------------------------------------- +function newobject:SetListHeight(height) + + self.listheight = height + return self + +end + +--[[--------------------------------------------------------- + - func: SetPadding(padding) + - desc: sets the padding of the list of choices +--]]--------------------------------------------------------- +function newobject:SetPadding(padding) + + self.listpadding = padding + return self + +end + +--[[--------------------------------------------------------- + - func: SetSpacing(spacing) + - desc: sets the spacing of the list of choices +--]]--------------------------------------------------------- +function newobject:SetSpacing(spacing) + + self.listspacing = spacing + return self + +end + +--[[--------------------------------------------------------- + - func: GetValue() + - desc: gets the value (choice) of the object +--]]--------------------------------------------------------- +function newobject:GetValue() + + return self.choice + +end + +--[[--------------------------------------------------------- + - func: GetChoice() + - desc: gets the current choice (same as get value) +--]]--------------------------------------------------------- +function newobject:GetChoice() + + return self.choice + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the object's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + self.text = text + return self + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the object's text +--]]--------------------------------------------------------- +function newobject:GetText() + + return self.text + +end + +--[[--------------------------------------------------------- + - func: SetButtonScrollAmount(speed) + - desc: sets the scroll amount of the object's scrollbar + buttons +--]]--------------------------------------------------------- +function newobject:SetButtonScrollAmount(amount) + + self.buttonscrollamount = amount + return self + +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 + return self + +end + +--[[--------------------------------------------------------- + - func: GetMouseWheelScrollAmount() + - desc: gets the scroll amount of the mouse wheel +--]]--------------------------------------------------------- +function newobject:GetButtonScrollAmount() + + return self.mousewheelscrollamount + +end + +--[[--------------------------------------------------------- + - func: SetDTScrolling(bool) + - desc: sets whether or not the object should use delta + time when scrolling +--]]--------------------------------------------------------- +function newobject:SetDTScrolling(bool) + + self.dtscrolling = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetDTScrolling() + - desc: gets whether or not the object should use delta + time when scrolling +--]]--------------------------------------------------------- +function newobject:GetDTScrolling() + + return self.dtscrolling + +end + +--[[--------------------------------------------------------- + - func: Sort(func) + - desc: sorts the object's choices +--]]--------------------------------------------------------- +function newobject:Sort(func) + + local default = self.sortfunc + + if func then + table.sort(self.choices, func) + else + table.sort(self.choices, default) + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetSortFunction(func) + - desc: sets the object's default sort function +--]]--------------------------------------------------------- +function newobject:SetSortFunction(func) + + self.sortfunc = func + return self + +end + +--[[--------------------------------------------------------- + - func: GetSortFunction(func) + - desc: gets the object's default sort function +--]]--------------------------------------------------------- +function newobject:GetSortFunction() + + return self.sortfunc + +end + +--[[--------------------------------------------------------- + - func: Clear() + - desc: removes all choices from the object's list + of choices +--]]--------------------------------------------------------- +function newobject:Clear() + + self.choices = {} + self.choice = "" + self.text = "Select an option" + + return self + +end + +--[[--------------------------------------------------------- + - func: SetClickable(bool) + - desc: sets whether or not the object is enabled +--]]--------------------------------------------------------- +function newobject:SetEnabled(bool) + + self.enabled = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetEnabled() + - desc: gets whether or not the object is enabled +--]]--------------------------------------------------------- +function newobject:GetEnabled() + + return self.enabled + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/numberbox.lua b/loveframes/objects/numberbox.lua new file mode 100644 index 0000000..d857155 --- /dev/null +++ b/loveframes/objects/numberbox.lua @@ -0,0 +1,485 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- numberbox object +local newobject = loveframes.NewObject("numberbox", "loveframes_object_numberbox", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "numberbox" + self.width = 80 + self.height = 20 + self.value = 0 + self.increaseamount = 1 + self.decreaseamount = 1 + self.min = -100 + self.max = 100 + self.delay = 0 + self.decimals = 0 + self.internal = false + self.canmodify = false + self.lastbuttonclicked = false + self.internals = {} + self.OnValueChanged = nil + + local input = loveframes.objects["textinput"]:new() + input.parent = self + input:SetSize(50, 20) + input:SetUsable({"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ".", "-"}) + input:SetTabReplacement("") + input:SetText(self.value) + input.OnTextChanged = function(object) + local value = self.value + local newvalue = tonumber(object.lines[1]) + if not newvalue then + self.value = value + input:SetText(value) + return + end + self.value = newvalue + if self.value > self.max then + self.value = self.max + object:SetText(self.value) + end + if self.value < self.min then + self.value = self.min + object:SetText(self.value) + end + if value ~= self.value then + if self.OnValueChanged then + self.OnValueChanged(self, self.value) + end + end + end + input.Update = function(object) + object:SetSize(object.parent.width - 20, object.parent.height) + end + + local increasebutton = loveframes.objects["button"]:new() + increasebutton.parent = self + increasebutton:SetWidth(21) + increasebutton:SetText("+") + increasebutton.OnClick = function() + local canmodify = self.canmodify + if not canmodify then + self:ModifyValue("add") + else + self.canmodify = false + end + end + increasebutton.Update = function(object) + local time = 0 + time = love.timer.getTime() + local delay = self.delay + local down = object.down + local canmodify = self.canmodify + local lastbuttonclicked = self.lastbuttonclicked + object:SetPos(object.parent.width - 21, 0) + object:SetHeight(object.parent.height/2 + 1) + if down and not canmodify then + self:ModifyValue("add") + self.canmodify = true + self.delay = time + 0.80 + self.lastbuttonclicked = object + elseif down and canmodify and delay < time then + self:ModifyValue("add") + self.delay = time + 0.02 + elseif not down and canmodify and lastbuttonclicked == object then + self.canmodify = false + self.delay = time + 0.80 + end + end + + local decreasesbutton = loveframes.objects["button"]:new() + decreasesbutton.parent = self + decreasesbutton:SetWidth(21) + decreasesbutton:SetText("-") + decreasesbutton.OnClick = function() + local canmodify = self.canmodify + if not canmodify then + self:ModifyValue("subtract") + else + self.canmodify = false + end + end + decreasesbutton.Update = function(object) + local time = 0 + time = love.timer.getTime() + local delay = self.delay + local down = object.down + local canmodify = self.canmodify + local lastbuttonclicked = self.lastbuttonclicked + object:SetPos(object.parent.width - 21, object.parent.height/2) + object:SetHeight(object.parent.height/2) + if down and not canmodify then + self:ModifyValue("subtract") + self.canmodify = true + self.delay = time + 0.80 + self.lastbuttonclicked = object + elseif down and canmodify and delay < time then + self:ModifyValue("subtract") + self.delay = time + 0.02 + elseif not down and canmodify and lastbuttonclicked == object then + self.canmodify = false + self.delay = time + 0.80 + end + end + + table.insert(self.internals, input) + table.insert(self.internals, increasebutton) + table.insert(self.internals, decreasesbutton) + + self:SetDrawFunc() +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 internals = self.internals + local parent = self.parent + local base = loveframes.base + local update = self.Update + + -- move to parent if there is a parent + if parent ~= base and parent.type ~= "list" then + self.x = self.parent.x + self.staticx + self.y = self.parent.y + self.staticy + end + + self:CheckHover() + + for k, v in ipairs(internals) do + v:update(dt) + end + + if update then + update(self, dt) + 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 internals = self.internals + local hover = self.hover + + if hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + end + + for k, v in ipairs(internals) do + v:mousepressed(x, y, button) + end + +end + +--[[--------------------------------------------------------- + - func: SetValue(value) + - desc: sets the object's value +--]]--------------------------------------------------------- +function newobject:SetValue(value) + + local min = self.min + local curvalue = self.value + local value = tonumber(value) or min + local internals = self.internals + local input = internals[1] + local onvaluechanged = self.OnValueChanged + + self.value = value + input:SetText(value) + + if value ~= curvalue and onvaluechanged then + onvaluechanged(self, value) + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetValue() + - desc: gets the object's value +--]]--------------------------------------------------------- +function newobject:GetValue() + + return self.value + +end + +--[[--------------------------------------------------------- + - func: SetIncreaseAmount(amount) + - desc: sets the object's increase amount +--]]--------------------------------------------------------- +function newobject:SetIncreaseAmount(amount) + + self.increaseamount = amount + return self + +end + +--[[--------------------------------------------------------- + - func: GetIncreaseAmount() + - desc: gets the object's increase amount +--]]--------------------------------------------------------- +function newobject:GetIncreaseAmount() + + return self.increaseamount + +end + +--[[--------------------------------------------------------- + - func: SetDecreaseAmount(amount) + - desc: sets the object's decrease amount +--]]--------------------------------------------------------- +function newobject:SetDecreaseAmount(amount) + + self.decreaseamount = amount + return self + +end + +--[[--------------------------------------------------------- + - func: GetDecreaseAmount() + - desc: gets the object's decrease amount +--]]--------------------------------------------------------- +function newobject:GetDecreaseAmount() + + return self.decreaseamount + +end + +--[[--------------------------------------------------------- + - func: SetMax(max) + - desc: sets the object's maximum value +--]]--------------------------------------------------------- +function newobject:SetMax(max) + + local internals = self.internals + local input = internals[1] + local onvaluechanged = self.OnValueChanged + + self.max = max + + if self.value > max then + self.value = max + input:SetValue(max) + if onvaluechanged then + onvaluechanged(self, max) + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetMax() + - desc: gets the object's maximum value +--]]--------------------------------------------------------- +function newobject:GetMax() + + return self.max + +end + +--[[--------------------------------------------------------- + - func: SetMin(min) + - desc: sets the object's minimum value +--]]--------------------------------------------------------- +function newobject:SetMin(min) + + local internals = self.internals + local input = internals[1] + local onvaluechanged = self.OnValueChanged + + self.min = min + + if self.value < min then + self.value = min + input:SetValue(min) + if onvaluechanged then + onvaluechanged(self, min) + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetMin() + - desc: gets the object's minimum value +--]]--------------------------------------------------------- +function newobject:GetMin() + + return self.min + +end + +--[[--------------------------------------------------------- + - func: SetMinMax() + - desc: sets the object's minimum and maximum values +--]]--------------------------------------------------------- +function newobject:SetMinMax(min, max) + + local internals = self.internals + local input = internals[1] + local onvaluechanged = self.OnValueChanged + + self.min = min + self.max = max + + if self.value > max then + self.value = max + input:SetValue(max) + if onvaluechanged then + onvaluechanged(self, max) + end + end + + if self.value < min then + self.value = min + input:SetValue(min) + if onvaluechanged then + onvaluechanged(self, min) + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetMinMax() + - desc: gets the object's minimum and maximum values +--]]--------------------------------------------------------- +function newobject:GetMinMax() + + return self.min, self.max + +end + +--[[--------------------------------------------------------- + - func: ModifyValue(type) + - desc: modifies the object's value +--]]--------------------------------------------------------- +function newobject:ModifyValue(type) + + local value = self.value + local internals = self.internals + local input = internals[1] + local decimals = self.decimals + local onvaluechanged = self.OnValueChanged + + if not value then + return + end + + if type == "add" then + local increaseamount = self.increaseamount + local max = self.max + self.value = value + increaseamount + if self.value > max then + self.value = max + end + self.value = loveframes.Round(self.value, decimals) + input:SetText(self.value) + if value ~= self.value then + if onvaluechanged then + onvaluechanged(self, self.value) + end + end + elseif type == "subtract" then + local decreaseamount = self.decreaseamount + local min = self.min + self.value = value - decreaseamount + if self.value < min then + self.value = min + end + self.value = loveframes.Round(self.value, decimals) + input:SetText(self.value) + if value ~= self.value then + if onvaluechanged then + onvaluechanged(self, self.value) + end + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetDecimals(decimals) + - desc: sets how many decimals the object's value + can have +--]]--------------------------------------------------------- +function newobject:SetDecimals(decimals) + + self.decimals = decimals + return self + +end + +--[[--------------------------------------------------------- + - func: GetDecimals() + - desc: gets how many decimals the object's value + can have +--]]--------------------------------------------------------- +function newobject:GetDecimals() + + return self.decimals + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/panel.lua b/loveframes/objects/panel.lua new file mode 100644 index 0000000..bb822e3 --- /dev/null +++ b/loveframes/objects/panel.lua @@ -0,0 +1,134 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- panel object +local newobject = loveframes.NewObject("panel", "loveframes_object_panel", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "panel" + self.width = 200 + self.height = 50 + self.internal = false + self.children = {} + + self:SetDrawFunc() +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 children = self.children + local parent = self.parent + local base = loveframes.base + local update = self.Update + + -- move to parent if there is a parent + if parent ~= base and parent.type ~= "list" then + self.x = self.parent.x + self.staticx + self.y = self.parent.y + self.staticy + end + + self:CheckHover() + + for k, v in ipairs(children) do + v:update(dt) + end + + if update then + update(self, dt) + 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 children = self.children + local hover = self.hover + + if hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + 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 + local children = self.children + + if not visible then + return + end + + for k, v in ipairs(children) do + v:mousereleased(x, y, button) + end + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/progressbar.lua b/loveframes/objects/progressbar.lua new file mode 100644 index 0000000..99c25dd --- /dev/null +++ b/loveframes/objects/progressbar.lua @@ -0,0 +1,318 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- progressbar object +local newobject = loveframes.NewObject("progressbar", "loveframes_object_progressbar", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "progressbar" + self.text = "" + self.width = 100 + self.height = 25 + self.min = 0 + self.max = 10 + self.value = 0 + self.barwidth = 0 + self.lerprate = 1000 + self.lerpvalue = 0 + self.lerpto = 0 + self.lerpfrom = 0 + self.completed = false + self.lerp = false + self.internal = false + self.OnComplete = nil + + self:SetDrawFunc() +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 lerp = self.lerp + local lerprate = self.lerprate + local lerpvalue = self.lerpvalue + local lerpto = self.lerpto + local lerpfrom = self.lerpfrom + local value = self.value + local completed = self.completed + local parent = self.parent + local base = loveframes.base + local update = self.Update + local oncomplete = self.OnComplete + + self:CheckHover() + + -- caclulate barwidth + if lerp then + if lerpfrom < lerpto then + if lerpvalue < lerpto then + self.lerpvalue = lerpvalue + lerprate*dt + elseif lerpvalue > lerpto then + self.lerpvalue = lerpto + end + elseif lerpfrom > lerpto then + if lerpvalue > lerpto then + self.lerpvalue = lerpvalue - lerprate*dt + elseif lerpvalue < lerpto then + self.lerpvalue = lerpto + end + elseif lerpfrom == lerpto then + self.lerpvalue = lerpto + end + self.barwidth = self.lerpvalue/self.max * self.width + -- min check + if self.lerpvalue < self.min then + self.lerpvalue = self.min + end + -- max check + if self.lerpvalue > self.max then + self.lerpvalue = self.max + end + else + self.barwidth = value/self.max * self.width + -- min max check + if value < self.min then + self.value = self.min + elseif value > self.max then + self.value = self.max + 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 + + -- completion check + if not completed then + if self.value >= self.max then + self.completed = true + if oncomplete then + oncomplete(self) + end + end + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: SetMax(max) + - desc: sets the object's maximum value +--]]--------------------------------------------------------- +function newobject:SetMax(max) + + self.max = max + return self + +end + +--[[--------------------------------------------------------- + - func: GetMax() + - desc: gets the object's maximum value +--]]--------------------------------------------------------- +function newobject:GetMax() + + return self.max + +end + +--[[--------------------------------------------------------- + - func: SetMin(min) + - desc: sets the object's minimum value +--]]--------------------------------------------------------- +function newobject:SetMin(min) + + self.min = min + return self + +end + +--[[--------------------------------------------------------- + - func: GetMin() + - desc: gets the object's minimum value +--]]--------------------------------------------------------- +function newobject:GetMin() + + return self.min + +end + +--[[--------------------------------------------------------- + - func: SetMinMax() + - desc: sets the object's minimum and maximum values +--]]--------------------------------------------------------- +function newobject:SetMinMax(min, max) + + self.min = min + self.max = max + + return self + +end + +--[[--------------------------------------------------------- + - func: GetMinMax() + - desc: gets the object's minimum and maximum values +--]]--------------------------------------------------------- +function newobject:GetMinMax() + + return self.min, self.max + +end + +--[[--------------------------------------------------------- + - func: SetValue(value) + - desc: sets the object's value +--]]--------------------------------------------------------- +function newobject:SetValue(value) + + local lerp = self.lerp + + if lerp then + self.lerpvalue = self.lerpvalue + self.lerpto = value + self.lerpfrom = self.lerpvalue + self.value = value + else + self.value = value + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetValue() + - desc: gets the object's value +--]]--------------------------------------------------------- +function newobject:GetValue() + + return self.value + +end + +--[[--------------------------------------------------------- + - func: SetLerp(bool) + - desc: sets whether or not the object should lerp + when changing between values +--]]--------------------------------------------------------- +function newobject:SetLerp(bool) + + self.lerp = bool + self.lerpto = self:GetValue() + self.lerpvalue = self:GetValue() + + return self + +end + +--[[--------------------------------------------------------- + - func: GetLerp() + - desc: gets whether or not the object should lerp + when changing between values +--]]--------------------------------------------------------- +function newobject:GetLerp() + + return self.lerp + +end + +--[[--------------------------------------------------------- + - func: SetLerpRate(rate) + - desc: sets the object's lerp rate +--]]--------------------------------------------------------- +function newobject:SetLerpRate(rate) + + self.lerprate = rate + return self + +end + +--[[--------------------------------------------------------- + - func: GetLerpRate() + - desc: gets the object's lerp rate +--]]--------------------------------------------------------- +function newobject:GetLerpRate() + + return self.lerprate + +end + +--[[--------------------------------------------------------- + - func: GetCompleted() + - desc: gets whether or not the object has reached its + maximum value +--]]--------------------------------------------------------- +function newobject:GetCompleted() + + return self.completed + +end + +--[[--------------------------------------------------------- + - func: GetBarWidth() + - desc: gets the object's bar width +--]]--------------------------------------------------------- +function newobject:GetBarWidth() + + return self.barwidth + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the object's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + self.text = text + return self + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the object's text +--]]--------------------------------------------------------- +function newobject:GetText() + + return self.text + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/radiobutton.lua b/loveframes/objects/radiobutton.lua new file mode 100644 index 0000000..3f362d5 --- /dev/null +++ b/loveframes/objects/radiobutton.lua @@ -0,0 +1,429 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- radiobutton object +local newobject = loveframes.NewObject("radiobutton", "loveframes_object_radiobutton", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "radiobutton" + self.width = 0 + self.height = 0 + self.boxwidth = 20 + self.boxheight = 20 + self.font = loveframes.basicfont + self.checked = false + self.lastvalue = false + self.internal = false + self.down = true + self.enabled = true + self.internals = {} + self.OnChanged = function () end + self.group = {} + + self:SetDrawFunc() +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 + + self:CheckHover() + + local hover = self.hover + local internals = self.internals + local boxwidth = self.boxwidth + local boxheight = self.boxheight + local parent = self.parent + local base = loveframes.base + local update = self.Update + + if not hover then + self.down = false + else + if loveframes.downobject == self then + self.down = true + end + end + + if not self.down and loveframes.downobject == self then + self.hover = true + end + + -- move to parent if there is a parent + if parent ~= base and parent.type ~= "list" then + self.x = self.parent.x + self.staticx + self.y = self.parent.y + self.staticy + end + + if internals[1] then + self.width = boxwidth + 5 + internals[1].width + if internals[1].height == boxheight then + self.height = boxheight + else + if internals[1].height > boxheight then + self.height = internals[1].height + else + self.height = boxheight + end + end + else + self.width = boxwidth + self.height = boxheight + end + + for k, v in ipairs(internals) do + v:update(dt) + end + + if update then + update(self, dt) + 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 + + if hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + self.down = true + loveframes.downobject = self + end + +end + +--[[--------------------------------------------------------- + - func: mousereleased(x, y, button) + - desc: called when the player releases a mouse button +--]]--------------------------------------------------------- +function newobject:mousereleased(x, y, button) + + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + if self.hover and self.down and self.enabled and button == 1 then + if not self.checked then + -- a radio button can only be unchecked by checking another radio button + self:SetChecked(true) + end + end + +end + +--[[--------------------------------------------------------- + - func: SetText(text) + - desc: sets the object's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + local boxwidth = self.boxwidth + local boxheight = self.boxheight + + if text ~= "" then + self.internals = {} + local textobject = loveframes.Create("text") + local skin = loveframes.GetActiveSkin() + if not skin then + skin = loveframes.config["DEFAULTSKIN"] + end + local directives = skin.directives + if directives then + local default_color = directives.radiobutton_text_default_color + local default_shadowcolor = directives.radiobutton_text_default_shadowcolor + local default_font = directives.radiobutton_text_default_font + if default_color then + textobject.defaultcolor = default_color + end + if default_shadowcolor then + textobject.shadowcolor = default_shadowcolor + end + if default_font then + self.font = default_font + end + end + textobject:Remove() + textobject.parent = self + textobject.state = self.state + textobject.collide = false + textobject:SetFont(self.font) + textobject:SetText(text) + textobject.Update = function(object, dt) + if object.height > boxheight then + object:SetPos(boxwidth + 5, 0) + else + object:SetPos(boxwidth + 5, boxheight/2 - object.height/2) + end + end + table.insert(self.internals, textobject) + else + self.width = boxwidth + self.height = boxheight + self.internals = {} + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the object's text +--]]--------------------------------------------------------- +function newobject:GetText() + + local internals = self.internals + local text = internals[1] + + if text then + return text.text + else + return false + end + +end + +--[[--------------------------------------------------------- + - func: SetSize(width, height, r1, r2) + - desc: sets the object's size +--]]--------------------------------------------------------- +function newobject:SetSize(width, height, r1, r2) + + if r1 then + self.boxwidth = self.parent.width * width + else + self.boxwidth = width + end + + if r2 then + self.boxheight = self.parent.height * height + else + self.boxheight = height + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetWidth(width, relative) + - desc: sets the object's width +--]]--------------------------------------------------------- +function newobject:SetWidth(width, relative) + + if relative then + self.boxwidth = self.parent.width * width + else + self.boxwidth = width + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetHeight(height, relative) + - desc: sets the object's height +--]]--------------------------------------------------------- +function newobject:SetHeight(height, relative) + + if relative then + self.boxheight = self.parent.height * height + else + self.boxheight = height + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetChecked(bool) + - desc: sets whether the object is checked or not +--]]--------------------------------------------------------- +function newobject:SetChecked(checked) + + if self.checked ~= checked then + self.checked = checked + self:OnChanged(checked) + end + + if checked then + for _, button in pairs(self.group) do + if button ~= self and button.checked then + button:SetChecked(false) + end + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetChecked() + - desc: gets whether the object is checked or not +--]]--------------------------------------------------------- +function newobject:GetChecked() + + return self.checked + +end + +--[[--------------------------------------------------------- + - func: SetGroup() + - desc: set the object's group. only one radio button in a + group is checked at a time. +--]]--------------------------------------------------------- +function newobject:SetGroup(group) + + self.group = group + self.group[self] = self + +end + +--[[--------------------------------------------------------- + - func: GetGroup() + - desc: gets the object's group +--]]--------------------------------------------------------- +function newobject:GetGroup(group) + + return self.group + +end + +--[[--------------------------------------------------------- + - func: SetFont(font) + - desc: sets the font of the object's text +--]]--------------------------------------------------------- +function newobject:SetFont(font) + + local internals = self.internals + local text = internals[1] + + self.font = font + + if text then + text:SetFont(font) + end + + return self + +end + +--[[--------------------------------------------------------- + - func: newobject:GetFont() + - desc: gets the font of the object's text +--]]--------------------------------------------------------- +function newobject:GetFont() + + return self.font + +end + +--[[--------------------------------------------------------- + - func: newobject:GetBoxHeight() + - desc: gets the object's box size +--]]--------------------------------------------------------- +function newobject:GetBoxSize() + + return self.boxwidth, self.boxheight + +end + +--[[--------------------------------------------------------- + - func: newobject:GetBoxWidth() + - desc: gets the object's box width +--]]--------------------------------------------------------- +function newobject:GetBoxWidth() + + return self.boxwidth + +end + +--[[--------------------------------------------------------- + - func: newobject:GetBoxHeight() + - desc: gets the object's box height +--]]--------------------------------------------------------- +function newobject:GetBoxHeight() + + return self.boxheight + +end + +--[[--------------------------------------------------------- + - func: SetClickable(bool) + - desc: sets whether or not the object is enabled +--]]--------------------------------------------------------- +function newobject:SetEnabled(bool) + + self.enabled = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetEnabled() + - desc: gets whether or not the object is enabled +--]]--------------------------------------------------------- +function newobject:GetEnabled() + + return self.enabled + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/slider.lua b/loveframes/objects/slider.lua new file mode 100644 index 0000000..75b157f --- /dev/null +++ b/loveframes/objects/slider.lua @@ -0,0 +1,514 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- slider object +local newobject = loveframes.NewObject("slider", "loveframes_object_slider", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "slider" + self.text = "Slider" + self.slidetype = "horizontal" + self.width = 5 + self.height = 5 + self.max = 10 + self.min = 0 + self.value = 0 + self.decimals = 5 + self.scrollincrease = 1 + self.scrolldecrease = 1 + self.scrollable = true + self.enabled = true + self.internal = false + self.internals = {} + self.OnValueChanged = nil + self.OnRelease = nil + + -- create the slider button + local sliderbutton = loveframes.objects["sliderbutton"]:new(self) + sliderbutton.state = self.state + table.insert(self.internals, sliderbutton) + + -- set initial value to minimum + self:SetValue(self.min) + + self:SetDrawFunc() +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 internals = self.internals + local sliderbutton = internals[1] + local parent = self.parent + local base = loveframes.base + local update = self.Update + + self:CheckHover() + + -- move to parent if there is a parent + if parent ~= base and parent.type ~= "list" then + self.x = self.parent.x + self.staticx + self.y = self.parent.y + self.staticy + end + + if sliderbutton then + local slidetype = self.slidetype + local buttonwidth = sliderbutton.width + local buttonheight = sliderbutton.height + if slidetype == "horizontal" then + self.height = buttonheight + elseif slidetype == "vertical" then + self.width = buttonwidth + end + end + + -- update internals + for k, v in ipairs(self.internals) do + v:update(dt) + end + + if update then + update(self, dt) + 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 enabled = self.enabled + + if not enabled then + return + end + + local internals = self.internals + local hover = self.hover + local slidetype = self.slidetype + local scrollable = self.scrollable + + if hover and button == 1 then + if slidetype == "horizontal" then + local xpos = x - self.x + local button = internals[1] + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + button:MoveToX(xpos) + button.down = true + button.dragging = true + button.startx = button.staticx + button.clickx = x + elseif slidetype == "vertical" then + local ypos = y - self.y + local button = internals[1] + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + button:MoveToY(ypos) + button.down = true + button.dragging = true + button.starty = button.staticy + button.clicky = y + end + elseif hover and scrollable and button == "wu" then + local value = self.value + local increase = self.scrollincrease + local newvalue = value + increase + self:SetValue(newvalue) + elseif hover and scrollable and button == "wd" then + local value = self.value + local decrease = self.scrolldecrease + local newvalue = value - decrease + self:SetValue(newvalue) + end + + for k, v in ipairs(internals) do + v:mousepressed(x, y, button) + end + +end + +--[[--------------------------------------------------------- + - func: SetValue(value) + - desc: sets the object's value +--]]--------------------------------------------------------- +function newobject:SetValue(value) + + if value > self.max then + return + end + + if value < self.min then + return + end + + local decimals = self.decimals + local newval = loveframes.Round(value, decimals) + local internals = self.internals + local onvaluechanged = self.OnValueChanged + + -- set the new value + self.value = newval + + -- slider button object + local sliderbutton = internals[1] + local slidetype = self.slidetype + local width = self.width + local height = self.height + local min = self.min + local max = self.max + + -- move the slider button to the new position + if slidetype == "horizontal" then + local xpos = width * ((newval - min) / (max - min)) + sliderbutton:MoveToX(xpos) + elseif slidetype == "vertical" then + local ypos = height - height * ((newval - min) / (max - min)) + sliderbutton:MoveToY(ypos) + end + + -- call OnValueChanged + if onvaluechanged then + onvaluechanged(self, newval) + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetValue() + - desc: gets the object's value +--]]--------------------------------------------------------- +function newobject:GetValue() + + return self.value + +end + +--[[--------------------------------------------------------- + - func: SetMax(max) + - desc: sets the object's maximum value +--]]--------------------------------------------------------- +function newobject:SetMax(max) + + self.max = max + + if self.value > self.max then + self.value = self.max + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetMax() + - desc: gets the object's maximum value +--]]--------------------------------------------------------- +function newobject:GetMax() + + return self.max + +end + +--[[--------------------------------------------------------- + - func: SetMin(min) + - desc: sets the object's minimum value +--]]--------------------------------------------------------- +function newobject:SetMin(min) + + self.min = min + + if self.value < self.min then + self.value = self.min + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetMin() + - desc: gets the object's minimum value +--]]--------------------------------------------------------- +function newobject:GetMin() + + return self.min + +end + +--[[--------------------------------------------------------- + - func: SetMinMax() + - desc: sets the object's minimum and maximum values +--]]--------------------------------------------------------- +function newobject:SetMinMax(min, max) + + self.min = min + self.max = max + + if self.value > self.max then + self.value = self.max + end + + if self.value < self.min then + self.value = self.min + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetMinMax() + - desc: gets the object's minimum and maximum values +--]]--------------------------------------------------------- +function newobject:GetMinMax() + + return self.min, self.max + +end + +--[[--------------------------------------------------------- + - func: SetText(name) + - desc: sets the objects's text +--]]--------------------------------------------------------- +function newobject:SetText(text) + + self.text = text + return self + +end + +--[[--------------------------------------------------------- + - func: GetText() + - desc: gets the objects's text +--]]--------------------------------------------------------- +function newobject:GetText() + + return self.text + +end + +--[[--------------------------------------------------------- + - func: SetDecimals(decimals) + - desc: sets how many decimals the object's value + can have +--]]--------------------------------------------------------- +function newobject:SetDecimals(decimals) + + self.decimals = decimals + return self + +end + +--[[--------------------------------------------------------- + - func: GetDecimals() + - desc: gets how many decimals the object's value + can have +--]]--------------------------------------------------------- +function newobject:GetDecimals() + + return self.decimals + +end + +--[[--------------------------------------------------------- + - func: SetButtonSize(width, height) + - desc: sets the objects's button size +--]]--------------------------------------------------------- +function newobject:SetButtonSize(width, height) + + local internals = self.internals + local sliderbutton = internals[1] + + if sliderbutton then + sliderbutton.width = width + sliderbutton.height = height + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetButtonSize() + - desc: gets the objects's button size +--]]--------------------------------------------------------- +function newobject:GetButtonSize() + + local internals = self.internals + local sliderbutton = internals[1] + + if sliderbutton then + return sliderbutton.width, sliderbutton.height + end + +end + +--[[--------------------------------------------------------- + - func: SetSlideType(slidetype) + - desc: sets the objects's slide type +--]]--------------------------------------------------------- +function newobject:SetSlideType(slidetype) + + self.slidetype = slidetype + + if slidetype == "vertical" then + self:SetValue(self.min) + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetSlideType() + - desc: gets the objects's slide type +--]]--------------------------------------------------------- +function newobject:GetSlideType() + + return self.slidetype + +end + +--[[--------------------------------------------------------- + - func: SetScrollable(bool) + - desc: sets whether or not the object can be scrolled + via the mouse wheel +--]]--------------------------------------------------------- +function newobject:SetScrollable(bool) + + self.scrollable = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetScrollable() + - desc: gets whether or not the object can be scrolled + via the mouse wheel +--]]--------------------------------------------------------- +function newobject:GetScrollable() + + return self.scrollable + +end + +--[[--------------------------------------------------------- + - func: SetScrollIncrease(increase) + - desc: sets the amount to increase the object's value + by when scrolling with the mouse wheel +--]]--------------------------------------------------------- +function newobject:SetScrollIncrease(increase) + + self.scrollincrease = increase + return self + +end + +--[[--------------------------------------------------------- + - func: GetScrollIncrease() + - desc: gets the amount to increase the object's value + by when scrolling with the mouse wheel +--]]--------------------------------------------------------- +function newobject:GetScrollIncrease() + + return self.scrollincrease + +end + +--[[--------------------------------------------------------- + - func: SetScrollDecrease(decrease) + - desc: sets the amount to decrease the object's value + by when scrolling with the mouse wheel +--]]--------------------------------------------------------- +function newobject:SetScrollDecrease(decrease) + + self.scrolldecrease = decrease + return self + +end + +--[[--------------------------------------------------------- + - func: GetScrollDecrease() + - desc: gets the amount to decrease the object's value + by when scrolling with the mouse wheel +--]]--------------------------------------------------------- +function newobject:GetScrollDecrease() + + return self.scrolldecrease + +end + +--[[--------------------------------------------------------- + - func: SetEnabled(bool) + - desc: sets whether or not the object is enabled +--]]--------------------------------------------------------- +function newobject:SetEnabled(bool) + + self.enabled = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetEnabled() + - desc: gets whether or not the object is enabled +--]]--------------------------------------------------------- +function newobject:GetEnabled() + + return self.enabled + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/tabs.lua b/loveframes/objects/tabs.lua new file mode 100644 index 0000000..fff0b72 --- /dev/null +++ b/loveframes/objects/tabs.lua @@ -0,0 +1,765 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- tabs object +local newobject = loveframes.NewObject("tabs", "loveframes_object_tabs", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "tabs" + self.width = 100 + self.height = 50 + self.clickx = 0 + self.clicky = 0 + self.offsetx = 0 + self.tab = 1 + self.tabnumber = 1 + self.padding = 5 + self.tabheight = 25 + self.previoustabheight = 25 + self.buttonscrollamount = 200 + self.mousewheelscrollamount = 1500 + self.buttonareax = 0 + self.buttonareawidth = self.width + self.autosize = true + self.autobuttonareawidth = true + self.dtscrolling = true + self.internal = false + self.tooltipfont = loveframes.basicfontsmall + self.internals = {} + self.children = {} + + self:AddScrollButtons() + self:SetDrawFunc() +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 x, y = love.mouse.getPosition() + local tabheight = self.tabheight + local padding = self.padding + local autosize = self.autosize + local autobuttonareawidth = self.autobuttonareawidth + local padding = self.padding + local children = self.children + local numchildren = #children + local internals = self.internals + local tab = self.tab + local parent = self.parent + local base = loveframes.base + local update = self.Update + + -- 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 + + self:CheckHover() + + if numchildren > 0 and tab == 0 then + self.tab = 1 + end + + if autobuttonareawidth then + local width = self.width + self.buttonareawidth = width + end + + local pos = 0 + + for k, v in ipairs(internals) do + v:update(dt) + if v.type == "tabbutton" then + v.x = (v.parent.x + v.staticx) + pos + self.offsetx + self.buttonareax + v.y = (v.parent.y + v.staticy) + pos = pos + v.width - 1 + end + end + + if #self.children > 0 then + self.children[self.tab]:update(dt) + self.children[self.tab]:SetPos(padding, tabheight + padding) + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: draw() + - desc: draws the object +--]]--------------------------------------------------------- +function newobject:draw() + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local x = self.x + local y = self.y + local width = self.width + local height = self.height + local tabheight = self:GetHeightOfButtons() + local stencilfunc = function() love.graphics.rectangle("fill", x + self.buttonareax, y, self.buttonareawidth, height) end + + self:SetDrawOrder() + + local drawfunc = self.Draw or self.drawfunc + if drawfunc then + drawfunc(self) + end + + love.graphics.stencil(stencilfunc) + love.graphics.setStencilTest("greater", 0) + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + local col = loveframes.BoundingBox(x + self.buttonareax, v.x, self.y, v.y, self.buttonareawidth, v.width, tabheight, v.height) + if col or v.type == "scrollbutton" then + v:draw() + end + end + end + + love.graphics.setStencilTest() + + local children = self.children + if #children > 0 then + children[self.tab]:draw() + end + + drawfunc = self.DrawOver or self.drawoverfunc + if drawfunc then + drawfunc(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 children = self.children + local internals = self.internals + local numchildren = #children + local numinternals = #internals + local tab = self.tab + local hover = self.hover + + if hover and button == 1 then + local baseparent = self:GetBaseParent() + if baseparent and baseparent.type == "frame" then + baseparent:MakeTop() + end + end + + for k, v in ipairs(internals) do + v:mousepressed(x, y, button) + end + + if numchildren > 0 then + children[tab]: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 + local children = self.children + local numchildren = #children + local tab = self.tab + local internals = self.internals + + if not visible then + return + end + + for k, v in ipairs(internals) do + v:mousereleased(x, y, button) + end + + if numchildren > 0 then + children[tab]:mousereleased(x, y, button) + end + +end + +--[[--------------------------------------------------------- + - func: wheelmoved(x, y) + - desc: called when the player moves a mouse wheel +--]]--------------------------------------------------------- +function newobject:wheelmoved(x, y) + + local internals = self.internals + local numinternals = #internals + + if y < 0 then + local buttonheight = self:GetHeightOfButtons() + local col = loveframes.BoundingBox(self.x, x, self.y, y, self.width, 1, buttonheight, 1) + local visible = internals[numinternals - 1]:GetVisible() + if col and visible then + local scrollamount = -y * self.mousewheelscrollamount + local dtscrolling = self.dtscrolling + if dtscrolling then + local dt = love.timer.getDelta() + self.offsetx = self.offsetx + scrollamount * dt + else + self.offsetx = self.offsetx + scrollamount + end + if self.offsetx > 0 then + self.offsetx = 0 + end + end + elseif y > 0 then + local buttonheight = self:GetHeightOfButtons() + local col = loveframes.BoundingBox(self.x, x, self.y, y, self.width, 1, buttonheight, 1) + local visible = internals[numinternals]:GetVisible() + if col and visible then + local bwidth = self:GetWidthOfButtons() + local scrollamount = y * self.mousewheelscrollamount + local dtscrolling = self.dtscrolling + if dtscrolling then + local dt = love.timer.getDelta() + self.offsetx = self.offsetx - scrollamount * dt + else + self.offsetx = self.offsetx - scrollamount + end + if ((self.offsetx + bwidth) + self.width) < self.width then + self.offsetx = -(bwidth + 10) + end + end + end + +end + +--[[--------------------------------------------------------- + - func: AddTab(name, object, tip, image) + - desc: adds a new tab to the tab panel +--]]--------------------------------------------------------- +function newobject:AddTab(name, object, tip, image, onopened, onclosed) + + local padding = self.padding + local autosize = self.autosize + local retainsize = self.retainsize + local tabnumber = self.tabnumber + local tabheight = self.tabheight + local internals = self.internals + local state = self.state + + object:Remove() + object.parent = self + object:SetState(state) + object.staticx = 0 + object.staticy = 0 + + if tabnumber ~= 1 then + object.visible = false + end + + local tab = loveframes.objects["tabbutton"]:new(self, name, tabnumber, tip, image, onopened, onclosed) + + table.insert(self.children, object) + table.insert(self.internals, #self.internals - 1, tab) + self.tabnumber = tabnumber + 1 + + if autosize and not retainsize then + object:SetSize(self.width - padding * 2, (self.height - tabheight) - padding * 2) + end + + return tab + +end + +--[[--------------------------------------------------------- + - func: AddScrollButtons() + - desc: creates scroll buttons fot the tab panel + - note: for internal use only +--]]--------------------------------------------------------- +function newobject:AddScrollButtons() + + local internals = self.internals + local state = self.state + + for k, v in ipairs(internals) do + if v.type == "scrollbutton" then + table.remove(internals, k) + end + end + + local leftbutton = loveframes.objects["scrollbutton"]:new("left") + leftbutton.parent = self + leftbutton:SetPos(0, 0) + leftbutton:SetSize(15, 25) + leftbutton:SetAlwaysUpdate(true) + leftbutton.Update = function(object, dt) + object.staticx = 0 + object.staticy = 0 + if self.offsetx ~= 0 then + object.visible = true + else + object.visible = false + object.down = false + object.hover = false + end + if object.down then + if self.offsetx > 0 then + self.offsetx = 0 + elseif self.offsetx ~= 0 then + local scrollamount = self.buttonscrollamount + local dtscrolling = self.dtscrolling + if dtscrolling then + local dt = love.timer.getDelta() + self.offsetx = self.offsetx + scrollamount * dt + else + self.offsetx = self.offsetx + scrollamount + end + end + end + end + + local rightbutton = loveframes.objects["scrollbutton"]:new("right") + rightbutton.parent = self + rightbutton:SetPos(self.width - 15, 0) + rightbutton:SetSize(15, 25) + rightbutton:SetAlwaysUpdate(true) + rightbutton.Update = function(object, dt) + object.staticx = self.width - object.width + object.staticy = 0 + local bwidth = self:GetWidthOfButtons() + if (self.offsetx + bwidth) - (self.buttonareax * 2 - 1) > self.width then + object.visible = true + else + object.visible = false + object.down = false + object.hover = false + end + if object.down then + if ((self.x + self.offsetx) + bwidth) ~= (self.x + self.width) then + local scrollamount = self.buttonscrollamount + local dtscrolling = self.dtscrolling + if dtscrolling then + local dt = love.timer.getDelta() + self.offsetx = self.offsetx - scrollamount * dt + else + self.offsetx = self.offsetx - scrollamount + end + end + end + end + + leftbutton.state = state + rightbutton.state = state + + table.insert(internals, leftbutton) + table.insert(internals, rightbutton) + +end + +--[[--------------------------------------------------------- + - func: GetWidthOfButtons() + - desc: gets the total width of all of the tab buttons +--]]--------------------------------------------------------- +function newobject:GetWidthOfButtons() + + local width = 0 + local internals = self.internals + + for k, v in ipairs(internals) do + if v.type == "tabbutton" then + width = width + v.width + end + end + + return width + +end + +--[[--------------------------------------------------------- + - func: GetHeightOfButtons() + - desc: gets the height of one tab button +--]]--------------------------------------------------------- +function newobject:GetHeightOfButtons() + + return self.tabheight + +end + +--[[--------------------------------------------------------- + - func: SwitchToTab(tabnumber) + - desc: makes the specified tab the active tab +--]]--------------------------------------------------------- +function newobject:SwitchToTab(tabnumber) + + local children = self.children + + for k, v in ipairs(children) do + v.visible = false + end + + self.tab = tabnumber + self.children[tabnumber].visible = true + + return self + +end + +--[[--------------------------------------------------------- + - func: SetScrollButtonSize(width, height) + - desc: sets the size of the scroll buttons +--]]--------------------------------------------------------- +function newobject:SetScrollButtonSize(width, height) + + local internals = self.internals + + for k, v in ipairs(internals) do + if v.type == "scrollbutton" then + v:SetSize(width, height) + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetPadding(paddint) + - desc: sets the padding for the tab panel +--]]--------------------------------------------------------- +function newobject:SetPadding(padding) + + self.padding = padding + return self + +end + +--[[--------------------------------------------------------- + - func: SetPadding(paddint) + - desc: gets the padding of the tab panel +--]]--------------------------------------------------------- +function newobject:GetPadding() + + return self.padding + +end + +--[[--------------------------------------------------------- + - func: SetTabHeight(height) + - desc: sets the height of the tab buttons +--]]--------------------------------------------------------- +function newobject:SetTabHeight(height) + + local autosize = self.autosize + local padding = self.padding + local previoustabheight = self.previoustabheight + local children = self.children + local internals = self.internals + + self.tabheight = height + + local tabheight = self.tabheight + + if tabheight ~= previoustabheight then + for k, v in ipairs(children) do + local retainsize = v.retainsize + if autosize and not retainsize then + v:SetSize(self.width - padding*2, (self.height - tabheight) - padding*2) + end + end + self.previoustabheight = tabheight + end + + for k, v in ipairs(internals) do + if v.type == "tabbutton" then + v:SetHeight(self.tabheight) + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetToolTipFont(font) + - desc: sets the height of the tab buttons +--]]--------------------------------------------------------- +function newobject:SetToolTipFont(font) + + local internals = self.internals + + for k, v in ipairs(internals) do + if v.type == "tabbutton" and v.tooltip then + v.tooltip:SetFont(font) + end + end + + return self + +end + +--[[--------------------------------------------------------- + - func: GetTabNumber() + - desc: gets the object's tab number +--]]--------------------------------------------------------- +function newobject:GetTabNumber() + + return self.tab + +end + +--[[--------------------------------------------------------- + - func: RemoveTab(id) + - desc: removes a tab from the object +--]]--------------------------------------------------------- +function newobject:RemoveTab(id) + + local children = self.children + local internals = self.internals + local tab = children[id] + + if tab then + tab:Remove() + end + + for k, v in ipairs(internals) do + if v.type == "tabbutton" then + if v.tabnumber == id then + v:Remove() + end + end + end + + local tabnumber = 1 + + for k, v in ipairs(internals) do + if v.type == "tabbutton" then + v.tabnumber = tabnumber + tabnumber = tabnumber + 1 + end + end + + self.tabnumber = tabnumber + return self + +end + +--[[--------------------------------------------------------- + - func: SetButtonScrollAmount(speed) + - desc: sets the scroll amount of the object's scrollbar + buttons +--]]--------------------------------------------------------- +function newobject:SetButtonScrollAmount(amount) + + self.buttonscrollamount = amount + return self + +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 + return self + +end + +--[[--------------------------------------------------------- + - func: GetMouseWheelScrollAmount() + - desc: gets the scroll amount of the mouse wheel +--]]--------------------------------------------------------- +function newobject:GetButtonScrollAmount() + + return self.mousewheelscrollamount + +end + +--[[--------------------------------------------------------- + - func: SetDTScrolling(bool) + - desc: sets whether or not the object should use delta + time when scrolling +--]]--------------------------------------------------------- +function newobject:SetDTScrolling(bool) + + self.dtscrolling = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetDTScrolling() + - desc: gets whether or not the object should use delta + time when scrolling +--]]--------------------------------------------------------- +function newobject:GetDTScrolling() + + return self.dtscrolling + +end + +--[[--------------------------------------------------------- + - func: SetTabObject(id, object) + - desc: sets the object of a tab +--]]--------------------------------------------------------- +function newobject:SetTabObject(id, object) + + local children = self.children + local internals = self.internals + local tab = children[id] + local state = self.state + + if tab then + tab:Remove() + object:Remove() + object.parent = self + object:SetState(state) + object.staticx = 0 + object.staticy = 0 + children[id] = object + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetButtonAreaX(x) + - desc: sets the x position of the object's button area +--]]--------------------------------------------------------- +function newobject:SetButtonAreaX(x) + + self.buttonareax = x + return self + +end + +--[[--------------------------------------------------------- + - func: GetButtonAreaX() + - desc: gets the x position of the object's button area +--]]--------------------------------------------------------- +function newobject:GetButtonAreaX() + + return self.buttonareax + +end + +--[[--------------------------------------------------------- + - func: SetButtonAreaWidth(width) + - desc: sets the width of the object's button area +--]]--------------------------------------------------------- +function newobject:SetButtonAreaWidth(width) + + self.buttonareawidth = width + return self + +end + +--[[--------------------------------------------------------- + - func: GetButtonAreaWidth() + - desc: gets the width of the object's button area +--]]--------------------------------------------------------- +function newobject:GetButtonAreaWidth() + + return self.buttonareawidth + +end + +--[[--------------------------------------------------------- + - func: SetAutoButtonAreaWidth(bool) + - desc: sets whether or not the width of the object's + button area should be adjusted automatically +--]]--------------------------------------------------------- +function newobject:SetAutoButtonAreaWidth(bool) + + self.autobuttonareawidth = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetAutoButtonAreaWidth() + - desc: gets whether or not the width of the object's + button area should be adjusted automatically +--]]--------------------------------------------------------- +function newobject:GetAutoButtonAreaWidth() + + return self.autobuttonareawidth + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/text.lua b/loveframes/objects/text.lua new file mode 100644 index 0000000..3855b19 --- /dev/null +++ b/loveframes/objects/text.lua @@ -0,0 +1,802 @@ +--[[------------------------------------------------ + -- 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 +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- 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, 1} + self.shadowcolor = {0, 0, 0, 1} + self.linkcolor = {0, .4, 1, 1} + self.linkhovercolor = {0, 0, 1, 1} + self.ignorenewlines = false + self.shadow = false + self.linkcol = false + self.internal = false + self.linksenabled = false + self.detectlinks = false + self.OnClickLink = nil + + local skin = loveframes.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 + self:SetDrawFunc() +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.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: 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.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 = loveframes.utf8.gsub(v, "\n", " ") + end + v = loveframes.utf8.gsub(v, string.char(9), " ") + v = loveframes.utf8.gsub(v, "\n", " \n ") + local parts = loveframes.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, loveframes.utf8.len(data) do + local item = loveframes.utf8.sub(data, n, n) + local itemw = v.font:getWidth(item) + if n ~= loveframes.utf8.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 loveframes.utf8.len(text) > 7 and (loveframes.utf8.sub(text, 1, 7) == "http://" or loveframes.utf8.sub(text, 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() + local printfunc = function(text, x, y) + love.graphics.print(text, math.floor(x + 0.5), math.floor(y + 0.5)) + end + + 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)) + printfunc(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 + printfunc(text, x + textx, y + texty) + end + else + love.graphics.setFont(font) + if shadow then + love.graphics.setColor(unpack(shadowcolor)) + printfunc(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 + printfunc(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 + +---------- module end ---------- +end diff --git a/loveframes/objects/textinput.lua b/loveframes/objects/textinput.lua new file mode 100644 index 0000000..d6694ca --- /dev/null +++ b/loveframes/objects/textinput.lua @@ -0,0 +1,2136 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +local utf8 = require("utf8") + +return function(loveframes) +---------- module start ---------- + +-- 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.trackindicator = true + self.OnEnter = nil + self.OnTextChanged = nil + self.OnFocusGained = nil + self.OnFocusLost = nil + self.OnCopy = nil + self.OnPaste = nil + + self:SetDrawFunc() +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 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 + + -- 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 + + 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(loveframes.utf8.gsub(v, ".", 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 + (textoffsetx * 2) + 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() + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local x = self.x + local y = self.y + local width = self.width + local height = self.height + local stencilfunc = function() love.graphics.rectangle("fill", x, y, width, height) end + 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 + + love.graphics.stencil(stencilfunc) + love.graphics.setStencilTest("greater", 0) + + local drawfunc = self.Draw or self.drawfunc + if drawfunc then + drawfunc(self) + end + + love.graphics.setStencilTest() + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:draw() + end + end + + drawfunc = self.DrawOver or self.drawoverfunc + if drawfunc then + drawfunc(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 alt = love.keyboard.isDown("lalt", "ralt") + 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 == 1 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 + if not self.multiline then + if self.lines[1] ~= "" then + self.alltextselected = true + end + else + self.alltextselected = true + end + 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 not alt then + if focus then + self.line = math.max(self.line - scrollamount, 1) + elseif vbar then + local vbar = self:GetVerticalScrollBody().internals[1].internals[1] + vbar:Scroll(-scrollamount) + end + else + if focus then + self:MoveIndicator(-scrollamount) + elseif hbar then + local hbar = self:GetHorizontalScrollBody().internals[1].internals[1] + hbar:Scroll(-scrollamount) + end + end + elseif button == "wd" then + if not alt then + if focus then + self.line = math.min(self.line + scrollamount, #self.lines) + elseif vbar then + local vbar = self:GetVerticalScrollBody().internals[1].internals[1] + vbar:Scroll(scrollamount) + end + else + if focus then + self:MoveIndicator(scrollamount) + elseif hbar then + local hbar = self:GetHorizontalScrollBody().internals[1].internals[1] + hbar:Scroll(scrollamount) + end + 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, isrepeat) + - desc: called when the player presses a key +--]]--------------------------------------------------------- +function newobject:keypressed(key, isrepeat) + + 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 + + self.delay = time + repeatdelay + self.keydown = key + + if (loveframes.IsCtrlDown()) and focus then + if key == "a" then + if not self.multiline then + if self.lines[1] ~= "" then + self.alltextselected = true + end + else + self.alltextselected = true + end + elseif key == "c" and alltextselected 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 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 editable then + self:Paste() + else + self:RunKey(key, false) + end + else + self:RunKey(key, false) + 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) + + if loveframes.utf8.find(text, "kp.") then + text = loveframes.utf8.gsub(text, "kp", "") + end + + self:RunKey(text, true) + +end + +--[[--------------------------------------------------------- + - func: RunKey(key, istext) + - desc: runs a key event on the object +--]]--------------------------------------------------------- +function newobject:RunKey(key, istext) + + 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 not istext then + 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(loveframes.utf8.sub(text, 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 = utf8.len(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 ~= utf8.len(text) then + local width = font:getWidth(loveframes.utf8.sub(text, indicatornum, indicatornum)) + self.offsetx = offsetx + width + elseif indicatornum == utf8.len(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 == utf8.len(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 = utf8.len(lines[#lines]) + self.alltextselected = false + end + return + elseif key == "up" then + if multiline then + if line > 1 then + self.line = line - 1 + if indicatornum > utf8.len(lines[self.line]) then + self.indicatornum = utf8.len(lines[self.line]) + end + end + end + return + elseif key == "down" then + if multiline then + if line < #lines then + self.line = line + 1 + if indicatornum > utf8.len(lines[self.line]) then + self.indicatornum = utf8.len(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 utf8.len(oldtext) > 0 then + newindicatornum = utf8.len(lines[self.line]) + lines[self.line] = lines[self.line] .. oldtext + self:MoveIndicator(newindicatornum) + else + self:MoveIndicator(utf8.len(lines[self.line])) + end + end + end + local masked = self.masked + local cwidth = 0 + if masked then + local maskchar = self.maskchar + cwidth = font:getWidth(loveframes.utf8.gsub(text, ".", maskchar)) + else + cwidth = font:getWidth(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 < utf8.len(text) then + text = self:RemoveFromText(indicatornum + 1) + lines[line] = text + elseif indicatornum == utf8.len(text) and line < #lines then + local oldtext = lines[line + 1] + if utf8.len(oldtext) > 0 then + -- FIXME: newindicatornum here??? + -- newindicatornum = utf8.len(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 < utf8.len(self.lines[line]) then + newtext = loveframes.utf8.sub(self.lines[line], indicatornum + 1, utf8.len(self.lines[line])) + self.lines[line] = loveframes.utf8.sub(self.lines[line], 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 + local hbody = self:GetHorizontalScrollBody() + if hbody then + hbody:GetScrollBar():Scroll(-hbody:GetWidth()) + end + end + elseif key == "tab" then + if alltextselected then + return + end + ckey = key + self.lines[self.line] = self:AddIntoText(self.tabreplacement, self.indicatornum) + self:MoveIndicator(utf8.len(self.tabreplacement)) + end + else + if not editable then + return + end + -- do not continue if the text limit has been reached or exceeded + if utf8.len(text) >= self.limit and self.limit ~= 0 and not alltextselected then + return + end + -- check for unusable characters + if #self.usable > 0 then + local found = false + for k, v in ipairs(self.usable) do + if v == key 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 == key 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 ~= utf8.len(text) then + text = self:AddIntoText(key, indicatornum) + lines[line] = text + self:MoveIndicator(1) + elseif indicatornum == utf8.len(text) then + text = text .. key + lines[line] = text + self:MoveIndicator(1) + elseif indicatornum == 0 then + text = self:AddIntoText(key, 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(loveframes.utf8.gsub(text, ".", maskchar)) + cwidth = font:getWidth(loveframes.utf8.gsub(key, ".", maskchar)) + else + twidth = font:getWidth(text) + cwidth = font:getWidth(key) + end + -- swidth - 1 is for the "-" character + if (twidth + textoffsetx) >= (swidth - 1) then + self.offsetx = self.offsetx + cwidth + end + end + end + + local curtext = self:GetText() + if ontextchanged and initialtext ~= curtext then + ontextchanged(self, key) + end + + return self + +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 > utf8.len(text) then + self.indicatornum = utf8.len(text) + elseif self.indicatornum < 0 then + self.indicatornum = 0 + end + + self.showindicator = true + self:UpdateIndicator() + + return self + +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 + else + if love.keyboard.isDown("up", "down", "left", "right") then + self.showindicator = true + end + end + + local width = 0 + if masked then + width = font:getWidth(string.rep(self.maskchar,indicatornum)) + else + if indicatornum == 0 then + width = 0 + elseif indicatornum >= utf8.len(text) then + width = font:getWidth(text) + else + width = font:getWidth(loveframes.utf8.sub(text, 1, indicatornum)) + end + end + + if multiline then + self.indicatorx = textx + width + self.indicatory = texty + theight * line - theight + else + self.indicatorx = textx + width + self.indicatory = texty + end + + -- indicator should be visible, so correcting scrolls + if self.focus and self.trackindicator then + local indicatorRelativeX = width + self.textoffsetx - self.offsetx + local leftlimit, rightlimit = 1, self:GetWidth() - 1 + if self.linenumberspanel then + rightlimit = rightlimit - self:GetLineNumbersPanel().width + end + if self.vbar then + rightlimit = rightlimit - self:GetVerticalScrollBody().width + end + if not (indicatorRelativeX > leftlimit and indicatorRelativeX < rightlimit) then + local hbody = self:GetHorizontalScrollBody() + if hbody then + local twidth = 0 + for k, v in ipairs(lines) do + local linewidth = 0 + if self.masked then + linewidth = font:getWidth(loveframes.utf8.gsub(v, ".", self.maskchar)) + else + linewidth = font:getWidth(v) + end + if linewidth > twidth then + twidth = linewidth + end + end + local correction = self:GetWidth() / 8 + if indicatorRelativeX < leftlimit then + correction = correction * -1 + end + hbody:GetScrollBar():ScrollTo((width + correction) / twidth) + end + end + local indicatorRelativeY = (line - 1) * theight + self.textoffsety - self.offsety + local uplimit, downlimit = theight, self:GetHeight() - theight + if self.hbar then + downlimit = downlimit - self:GetHorizontalScrollBody().height + end + if not (indicatorRelativeY > uplimit and indicatorRelativeY < downlimit) then + local vbody = self:GetVerticalScrollBody() + if vbody then + local correction = self:GetHeight() / 8 / theight + if indicatorRelativeY < uplimit then + correction = correction * -1 + end + vbody:GetScrollBar():ScrollTo((line - 1 + correction)/#lines) + end + end + end + + return self + +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 = loveframes.utf8.sub(text, 1, p) + local part2 = loveframes.utf8.sub(text, p + 1) + local new = part1 .. 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 = loveframes.utf8.sub(text, 1, p - 1) + local part2 = loveframes.utf8.sub(text, 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.BoundingBox(selfx, x, selfy, y, selfwidth - 16, 1, self.height, 1) + elseif hbar and not vbar then + selfcol = loveframes.BoundingBox(selfx, x, selfy, y, selfwidth, 1, self.height - 16, 1) + elseif not vbar and not hbar then + selfcol = loveframes.BoundingBox(selfx, x, selfy, y, selfwidth, 1, self.height, 1) + elseif vbar and hbar then + selfcol = loveframes.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.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, utf8.len(curline) do + local char = loveframes.utf8.sub(text, 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.BoundingBox(tx, x, liney, y, width, 1, height, 1) + + xpos = xpos + width + + if col then + self:MoveIndicator(i - 1, true) + break + else + self.indicatornum = utf8.len(curline) + end + + if x < tx then + self:MoveIndicator(0, true) + end + + if x > (tx + width) then + self:MoveIndicator(utf8.len(curline), true) + end + end + + if utf8.len(curline) == 0 then + self.indicatornum = 0 + end + end + else + local i = 0 + for p, c in utf8.codes(text) do + i = i + 1 + local char = utf8.char(c) + 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.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(utf8.len(text), true) + end + end + end + + return self + +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 + + return self + +end + +--[[--------------------------------------------------------- + - func: SetTextOffsetX(num) + - desc: sets the object's text x offset +--]]--------------------------------------------------------- +function newobject:SetTextOffsetX(num) + + self.textoffsetx = num + return self + +end + +--[[--------------------------------------------------------- + - func: SetTextOffsetY(num) + - desc: sets the object's text y offset +--]]--------------------------------------------------------- +function newobject:SetTextOffsetY(num) + + self.textoffsety = num + return self + +end + +--[[--------------------------------------------------------- + - func: SetFont(font) + - desc: sets the object's font +--]]--------------------------------------------------------- +function newobject:SetFont(font) + + self.font = font + return self + +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 + + return self + +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 + return self + +end + +--[[--------------------------------------------------------- + - func: SetUsable(usable) + - desc: sets what characters can be used for the + object's text +--]]--------------------------------------------------------- +function newobject:SetUsable(usable) + + self.usable = usable + return self + +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 + return self + +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 + + return self + +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 = loveframes.utf8.gsub(text, string.char(9), tabreplacement) + text = loveframes.utf8.gsub(text, string.char(13), "") + + if multiline then + text = loveframes.utf8.gsub(text, string.char(92) .. string.char(110), string.char(10)) + local t = loveframes.SplitString(text, string.char(10)) + if #t > 0 then + self.lines = t + else + self.lines = {""} + end + self.line = #self.lines + self.indicatornum = utf8.len(self.lines[#self.lines]) + else + text = loveframes.utf8.gsub(text, string.char(92) .. string.char(110), "") + text = loveframes.utf8.gsub(text, string.char(10), "") + self.lines = {text} + self.line = 1 + self.indicatornum = utf8.len(text) + end + + return self + +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 + + return self + +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 + + return self + +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 + return self + +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 + return self + +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 + return self + +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 + return self + +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 + + return self + +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 + return self + +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 + return self + +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) + return self + +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 + + return self + +end + +--[[--------------------------------------------------------- + - func: Copy() + - desc: copies the object's text to the user's clipboard +--]]--------------------------------------------------------- +function newobject:Copy() + + local text = self:GetText() + love.system.setClipboardText(text) + + return self + +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 = utf8.len(curtext) + if curlength == limit then + return + else + local inputlimit = limit - curlength + if utf8.len(text) > inputlimit then + text = loveframes.utf8.sub(text, 1, inputlimit) + end + end + end + local charcheck = function(a) + if #usable > 0 then + if not loveframes.TableHasValue(usable, a) then + return "" + end + elseif #unusable > 0 then + if loveframes.TableHasValue(unusable, a) then + return "" + end + end + end + if #usable > 0 or #unusable > 0 then + text = loveframes.utf8.gsub(text, ".", 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.SplitString(text, string.char(10)) + local numparts = #parts + local oldlinedata = {} + local line = self.line + local first = loveframes.utf8.sub(lines[line], 0, indicatornum) + local last = loveframes.utf8.sub(lines[line], indicatornum + 1) + if numparts > 1 then + for i=1, numparts do + local part = loveframes.utf8.gsub(parts[i], string.char(13), "") + part = loveframes.utf8.gsub(part, string.char(9), " ") + if i ~= 1 then + table.insert(oldlinedata, lines[line]) + lines[line] = part + if i == numparts then + self.indicatornum = utf8.len(part) + 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 = loveframes.utf8.gsub(text, string.char(10), " ") + text = loveframes.utf8.gsub(text, string.char(13), " ") + text = loveframes.utf8.gsub(text, string.char(9), tabreplacement) + local length = utf8.len(text) + local new = first .. text .. last + lines[line] = new + self.indicatornum = indicatornum + length + if ontextchanged then + ontextchanged(self, text) + end + end + else + text = loveframes.utf8.gsub(text, string.char(10), " ") + text = loveframes.utf8.gsub(text, string.char(13), " ") + text = loveframes.utf8.gsub(text, string.char(9), tabreplacement) + local length = utf8.len(text) + local linetext = lines[1] + local part1 = loveframes.utf8.sub(linetext, 1, indicatornum) + local part2 = loveframes.utf8.sub(linetext, 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 + + return self + +end + +--[[--------------------------------------------------------- + - func: SelectAll() + - desc: selects all of the object's text +--]]--------------------------------------------------------- +function newobject:SelectAll() + + if not self.multiline then + if self.lines[1] ~= "" then + self.alltextselected = true + end + else + self.alltextselected = true + end + + return self + +end + +--[[--------------------------------------------------------- + - func: DeselectAll() + - desc: deselects all of the object's text +--]]--------------------------------------------------------- +function newobject:DeselectAll() + + self.alltextselected = false + return self + +end + +--[[--------------------------------------------------------- + - func: SetMasked(masked) + - desc: sets whether or not the object is masked +--]]--------------------------------------------------------- +function newobject:SetMasked(masked) + + self.masked = masked + return self + +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 + return self + +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 + return self + +end + +--[[--------------------------------------------------------- + - func: GetPlaceholderText() + - desc: gets the object's placeholder text +--]]--------------------------------------------------------- +function newobject:GetPlaceholderText() + + return self.placeholder + +end + +--[[--------------------------------------------------------- + - func: ClearLine(line) + - desc: clears the specified line +--]]--------------------------------------------------------- +function newobject:ClearLine(line) + + if self.lines[line] then + self.lines[line] = "" + end + + return self + +end + +--[[--------------------------------------------------------- + - func: SetTrackingEnabled(bool) + - desc: sets whether or not the object should + automatically scroll to the position of its + indicator +--]]--------------------------------------------------------- +function newobject:SetTrackingEnabled(bool) + + self.trackindicator = bool + return self + +end + +--[[--------------------------------------------------------- + - func: GetTrackingEnabled() + - desc: gets whether or not the object should + automatically scroll to the position of its + indicator +--]]--------------------------------------------------------- +function newobject:GetTrackingEnabled() + + return self.trackindicator + +end + +---------- module end ---------- +end diff --git a/loveframes/objects/tree.lua b/loveframes/objects/tree.lua new file mode 100644 index 0000000..8550da5 --- /dev/null +++ b/loveframes/objects/tree.lua @@ -0,0 +1,342 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- tree object +local newobject = loveframes.NewObject("tree", "loveframes_object_tree", true) + +--[[--------------------------------------------------------- + - func: initialize() + - desc: initializes the object +--]]--------------------------------------------------------- +function newobject:initialize() + + self.type = "tree" + self.width = 200 + self.height = 200 + self.offsetx = 0 + self.offsety = 0 + self.itemwidth = 0 + self.itemheight = 0 + self.extrawidth = 0 + self.extraheight = 0 + self.buttonscrollamount = 0.10 + self.vbar = false + self.hbar = false + self.internal = false + self.selectednode = false + self.OnSelectNode = nil + self.children = {} + self.internals = {} + + self:SetDrawFunc() +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 + + self:CheckHover() + + local parent = self.parent + local base = loveframes.base + local update = self.Update + + -- 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 + + self.itemwidth = 0 + self.itemheight = 0 + + for k, v in ipairs(self.children) do + v.x = (v.parent.x + v.staticx) - self.offsetx + v.y = (self.y + self.itemheight) - self.offsety + if v.width > self.itemwidth then + self.itemwidth = v.width + end + self.itemheight = self.itemheight + v.height + v:update(dt) + end + + if self.vbar then + self.itemwidth = self.itemwidth + 16 + 5 + end + + self.extrawidth = self.itemwidth - self.width + self.extraheight = self.itemheight - self.height + + if self.itemheight > self.height then + if not self.vbar then + local scrollbody = loveframes.objects["scrollbody"]:new(self, "vertical") + table.insert(self.internals, scrollbody) + self.vbar = true + if self.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 self.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 self.itemwidth > self.width then + if not self.hbar then + local scrollbody = loveframes.objects["scrollbody"]:new(self, "horizontal") + 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 self.hbar then + self:GetHorizontalScrollBody():Remove() + self.hbar = false + self.offsetx = 0 + if self.vbar then + local vbody = self:GetVerticalScrollBody() + if vbody then + vbody:SetHeight(vbody:GetHeight() + 15) + end + end + end + end + + for k, v in ipairs(self.internals) do + v:update(dt) + end + + if update then + update(self, dt) + end + +end + +--[[--------------------------------------------------------- + - func: draw() + - desc: draws the object +--]]--------------------------------------------------------- +function newobject:draw() + if loveframes.state ~= self.state then + return + end + + if not self.visible then + return + end + + local stencilfunc + + if self.vbar and not self.hbar then + stencilfunc = function() love.graphics.rectangle("fill", self.x, self.y, self.width - 16, self.height) end + elseif self.hbar and not self.vbar then + stencilfunc = function() love.graphics.rectangle("fill", self.x, self.y, self.width, self.height - 16) end + elseif self.vbar and self.hbar then + stencilfunc = function() love.graphics.rectangle("fill", self.x, self.y, self.width - 16, self.height - 16) end + end + + self:SetDrawOrder() + + love.graphics.stencil(stencilfunc) + love.graphics.setStencilTest("greater", 0) + + local drawfunc = self.Draw or self.drawfunc + if drawfunc then + drawfunc(self) + end + + local children = self.children + if children then + for k, v in ipairs(children) do + v:draw() + end + end + + love.graphics.setStencilTest() + + local internals = self.internals + if internals then + for k, v in ipairs(internals) do + v:draw() + end + 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 + + for k, v in ipairs(self.internals) do + v:mousepressed(x, y, button) + end + + for k, v in ipairs(self.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 + + for k, v in ipairs(self.internals) do + v:mousereleased(x, y, button) + end + + for k, v in ipairs(self.children) do + v:mousereleased(x, y, button) + end + +end + +--[[--------------------------------------------------------- + - func: AddNode(text) + - desc: adds a node to the object +--]]--------------------------------------------------------- +function newobject:AddNode(text) + + local node = loveframes.objects["treenode"]:new() + node.parent = self + node.tree = self + node.text = text + node.staticx = 0 + node.staticy = self.itemheight + table.insert(self.children, node) + return node + +end + +--[[--------------------------------------------------------- + - func: RemoveNode(id) + - desc: removes a node from the object +--]]--------------------------------------------------------- +function newobject:RemoveNode(id) + + for k, v in ipairs(self.children) do + if k == id then + v:Remove() + break + end + end + +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 + +---------- module end ---------- +end diff --git a/loveframes/skins/Blue/images/arrow-down.png b/loveframes/skins/Blue/images/arrow-down.png new file mode 100644 index 0000000..37d8058 Binary files /dev/null and b/loveframes/skins/Blue/images/arrow-down.png differ diff --git a/loveframes/skins/Blue/images/arrow-left.png b/loveframes/skins/Blue/images/arrow-left.png new file mode 100644 index 0000000..c5045dd Binary files /dev/null and b/loveframes/skins/Blue/images/arrow-left.png differ diff --git a/loveframes/skins/Blue/images/arrow-right.png b/loveframes/skins/Blue/images/arrow-right.png new file mode 100644 index 0000000..89b4818 Binary files /dev/null and b/loveframes/skins/Blue/images/arrow-right.png differ diff --git a/loveframes/skins/Blue/images/arrow-up.png b/loveframes/skins/Blue/images/arrow-up.png new file mode 100644 index 0000000..475d5db Binary files /dev/null and b/loveframes/skins/Blue/images/arrow-up.png differ diff --git a/loveframes/skins/Blue/images/close.png b/loveframes/skins/Blue/images/close.png new file mode 100644 index 0000000..0825add Binary files /dev/null and b/loveframes/skins/Blue/images/close.png differ diff --git a/loveframes/skins/Blue/images/collapse.png b/loveframes/skins/Blue/images/collapse.png new file mode 100644 index 0000000..ee7524b Binary files /dev/null and b/loveframes/skins/Blue/images/collapse.png differ diff --git a/loveframes/skins/Blue/images/expand.png b/loveframes/skins/Blue/images/expand.png new file mode 100644 index 0000000..753625b Binary files /dev/null and b/loveframes/skins/Blue/images/expand.png differ diff --git a/loveframes/skins/Blue/images/gradients/button-down.png b/loveframes/skins/Blue/images/gradients/button-down.png new file mode 100644 index 0000000..a3838c8 Binary files /dev/null and b/loveframes/skins/Blue/images/gradients/button-down.png differ diff --git a/loveframes/skins/Blue/images/gradients/button-hover.png b/loveframes/skins/Blue/images/gradients/button-hover.png new file mode 100644 index 0000000..13f51e0 Binary files /dev/null and b/loveframes/skins/Blue/images/gradients/button-hover.png differ diff --git a/loveframes/skins/Blue/images/gradients/button-nohover.png b/loveframes/skins/Blue/images/gradients/button-nohover.png new file mode 100644 index 0000000..39ff24a Binary files /dev/null and b/loveframes/skins/Blue/images/gradients/button-nohover.png differ diff --git a/loveframes/skins/Blue/images/gradients/button-unclickable.png b/loveframes/skins/Blue/images/gradients/button-unclickable.png new file mode 100644 index 0000000..ef8bf7d Binary files /dev/null and b/loveframes/skins/Blue/images/gradients/button-unclickable.png differ diff --git a/loveframes/skins/Blue/images/gradients/frame-body.png b/loveframes/skins/Blue/images/gradients/frame-body.png new file mode 100644 index 0000000..bed498d Binary files /dev/null and b/loveframes/skins/Blue/images/gradients/frame-body.png differ diff --git a/loveframes/skins/Blue/images/gradients/frame-topbar.png b/loveframes/skins/Blue/images/gradients/frame-topbar.png new file mode 100644 index 0000000..f5c3a8c Binary files /dev/null and b/loveframes/skins/Blue/images/gradients/frame-topbar.png differ diff --git a/loveframes/skins/Blue/images/gradients/progressbar.png b/loveframes/skins/Blue/images/gradients/progressbar.png new file mode 100644 index 0000000..7cdfb4b Binary files /dev/null and b/loveframes/skins/Blue/images/gradients/progressbar.png differ diff --git a/loveframes/skins/Blue/images/multichoice-arrow.png b/loveframes/skins/Blue/images/multichoice-arrow.png new file mode 100644 index 0000000..165121c Binary files /dev/null and b/loveframes/skins/Blue/images/multichoice-arrow.png differ diff --git a/loveframes/skins/Blue/images/tree-node-button-close.png b/loveframes/skins/Blue/images/tree-node-button-close.png new file mode 100644 index 0000000..7591f55 Binary files /dev/null and b/loveframes/skins/Blue/images/tree-node-button-close.png differ diff --git a/loveframes/skins/Blue/images/tree-node-button-open.png b/loveframes/skins/Blue/images/tree-node-button-open.png new file mode 100644 index 0000000..68b36cc Binary files /dev/null and b/loveframes/skins/Blue/images/tree-node-button-open.png differ diff --git a/loveframes/skins/Blue/skin.lua b/loveframes/skins/Blue/skin.lua new file mode 100644 index 0000000..1f4e879 --- /dev/null +++ b/loveframes/skins/Blue/skin.lua @@ -0,0 +1,2110 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- skin table +local skin = {} + +-- skin info (you always need this in a skin) +skin.name = "Blue" +skin.author = "Nikolai Resokav" +skin.version = "1.0" + +local bordercolor = {0.56, 0.56, 0.56, 1} + +-- add skin directives to this table +skin.directives = {} + +-- controls +skin.controls = {} +skin.controls.smallfont = love.graphics.newFont(11) +skin.controls.imagebuttonfont = love.graphics.newFont(15) + +-- frame +skin.controls.frame_body_color = {0.91, 0.91, 0.91, 1} +skin.controls.frame_name_color = {1, 1, 1, 1} +skin.controls.frame_name_font = skin.controls.smallfont + +-- button +skin.controls.button_text_down_color = {1, 1, 1, 1} +skin.controls.button_text_nohover_color = {0, 0, 0, 0.78} +skin.controls.button_text_hover_color = {1, 1, 1, 1} +skin.controls.button_text_nonclickable_color = {0, 0, 0, 0.39} +skin.controls.button_text_font = skin.controls.smallfont + +-- imagebutton +skin.controls.imagebutton_text_down_color = {1, 1, 1, 1} +skin.controls.imagebutton_text_nohover_color = {1, 1, 1, 0.78} +skin.controls.imagebutton_text_hover_color = {1, 1, 1, 1} +skin.controls.imagebutton_text_font = skin.controls.imagebuttonfont + +-- closebutton +skin.controls.closebutton_body_down_color = {1, 1, 1, 1} +skin.controls.closebutton_body_nohover_color = {1, 1, 1, 1} +skin.controls.closebutton_body_hover_color = {1, 1, 1, 1} + +-- progressbar +skin.controls.progressbar_body_color = {1, 1, 1, 1} +skin.controls.progressbar_text_color = {0, 0, 0, 1} +skin.controls.progressbar_text_font = skin.controls.smallfont + +-- list +skin.controls.list_body_color = {0.91, 0.91, 0.91, 1} + +-- scrollarea +skin.controls.scrollarea_body_color = {0.86, 0.86, 0.86, 1} + +-- scrollbody +skin.controls.scrollbody_body_color = {0, 0, 0, 0} + +-- panel +skin.controls.panel_body_color = {0.91, 0.91, 0.91, 1} + +-- tabpanel +skin.controls.tabpanel_body_color = {0.91, 0.91, 0.91, 1} + +-- tabbutton +skin.controls.tab_text_nohover_color = {0, 0, 0, 0.78} +skin.controls.tab_text_hover_color = {1, 1, 1, 1} +skin.controls.tab_text_font = skin.controls.smallfont + +-- multichoice +skin.controls.multichoice_body_color = {0.94, 0.94, 0.94, 1} +skin.controls.multichoice_text_color = {0, 0, 0, 1} +skin.controls.multichoice_text_font = skin.controls.smallfont + +-- multichoicelist +skin.controls.multichoicelist_body_color = {0.94, 0.94, 0.94, 0.78} + +-- multichoicerow +skin.controls.multichoicerow_body_nohover_color = {0.94, 0.94, 0.94, 1} +skin.controls.multichoicerow_body_hover_color = {0.2, 0.8, 1, 1} +skin.controls.multichoicerow_text_nohover_color = {0, 0, 0, 0.59} +skin.controls.multichoicerow_text_hover_color = {1, 1, 1, 1} +skin.controls.multichoicerow_text_font = skin.controls.smallfont + +-- tooltip +skin.controls.tooltip_body_color = {1, 1, 1, 1} + +-- textinput +skin.controls.textinput_body_color = {0.98, 0.98, 0.98, 1} +skin.controls.textinput_indicator_color = {0, 0, 0, 1} +skin.controls.textinput_text_normal_color = {0, 0, 0, 1} +skin.controls.textinput_text_placeholder_color = {0.5, 0.5, 0.5, 1} +skin.controls.textinput_text_selected_color = {1, 1, 1, 1} +skin.controls.textinput_highlight_bar_color = {0.2, 0.8, 1, 1} + +-- slider +skin.controls.slider_bar_outline_color = {0.86, 0.86, 0.86, 1} + +-- checkbox +skin.controls.checkbox_body_color = {1, 1, 1, 1} +skin.controls.checkbox_check_color = {0.5, 0.8, 1, 1} +skin.controls.checkbox_text_font = skin.controls.smallfont + +-- radiobutton +skin.controls.radiobutton_body_color = {1, 1, 1, 1} +skin.controls.radiobutton_check_color = {0.5, 0.8, 1, 1} +skin.controls.radiobutton_inner_border_color = {0.3, 0.72, 1, 1} +skin.controls.radiobutton_text_font = skin.controls.smallfont + +-- collapsiblecategory +skin.controls.collapsiblecategory_text_color = {1, 1, 1, 1} + +-- columnlist +skin.controls.columnlist_body_color = {0.91, 0.91, 0.91, 1} + +-- columlistarea +skin.controls.columnlistarea_body_color = {0.91, 0.91, 0.91, 1} + +-- columnlistheader +skin.controls.columnlistheader_text_down_color = {1, 1, 1, 1} +skin.controls.columnlistheader_text_nohover_color = {0, 0, 0, 0.78} +skin.controls.columnlistheader_text_hover_color = {1, 1, 1, 1} +skin.controls.columnlistheader_text_font = skin.controls.smallfont + +-- columnlistrow +skin.controls.columnlistrow_body1_color = {0.96, 0.96, 0.96, 1} +skin.controls.columnlistrow_body2_color = {1, 1, 1, 1} +skin.controls.columnlistrow_body_selected_color = {0.1, 0.78, 1, 1} +skin.controls.columnlistrow_body_hover_color = {0.4, 0.85, 1, 1} +skin.controls.columnlistrow_text_color = {0.39, 0.39, 0.39, 1} +skin.controls.columnlistrow_text_hover_color = {1, 1, 1, 1} +skin.controls.columnlistrow_text_selected_color = {1, 1, 1, 1} + +-- modalbackground +skin.controls.modalbackground_body_color = {1, 1, 1, 0.39} + +-- linenumberspanel +skin.controls.linenumberspanel_text_color = {0.67, 0.67, 0.67, 1} +skin.controls.linenumberspanel_body_color = {0.92, 0.92, 0.92, 1} + +-- grid +skin.controls.grid_body_color = {0.9, 0.9, 0.9, 1} + +-- form +skin.controls.form_text_color = {0, 0, 0, 1} +skin.controls.form_text_font = skin.controls.smallfont + +-- menu +skin.controls.menu_body_color = {1, 1, 1, 1} + +-- menuoption +skin.controls.menuoption_body_hover_color = {0.2, 0.8, 1, 1} +skin.controls.menuoption_text_hover_color = {1, 1, 1, 1} +skin.controls.menuoption_text_color = {0.71, 0.71, 0.71, 1} + +local function ParseHeaderText(str, hx, hwidth, tx) + + local font = love.graphics.getFont() + local twidth = love.graphics.getFont():getWidth(str) + + if (tx + twidth) - hwidth/2 > hx + hwidth then + if #str > 1 then + return ParseHeaderText(loveframes.utf8.sub(str, 1, #str - 1), hx, hwidth, tx, twidth) + else + return str + end + else + return str + end + +end + +local function ParseRowText(str, rx, rwidth, tx1, tx2) + + local twidth = love.graphics.getFont():getWidth(str) + + if (tx1 + tx2) + twidth > rx + rwidth then + if #str > 1 then + return ParseRowText(loveframes.utf8.sub(str, 1, #str - 1), rx, rwidth, tx1, tx2) + else + return str + end + else + return str + end + +end + +function skin.PrintText(text, x, y) + love.graphics.print(text, math.floor(x + 0.5), math.floor(y + 0.5)) +end + +--[[ +local function DrawColumnHeaderText(text, hx, hwidth, tx, twidth) + + local new = "" + if tx + width > hx + hwidth then +--]] + +--[[--------------------------------------------------------- + - func: OutlinedRectangle(x, y, width, height, ovt, ovb, ovl, ovr) + - desc: creates and outlined rectangle +--]]--------------------------------------------------------- +function skin.OutlinedRectangle(x, y, width, height, ovt, ovb, ovl, ovr) + + local ovt = ovt or false + local ovb = ovb or false + local ovl = ovl or false + local ovr = ovr or false + + -- top + if not ovt then + love.graphics.rectangle("fill", x, y, width, 1) + end + + -- bottom + if not ovb then + love.graphics.rectangle("fill", x, y + height - 1, width, 1) + end + + -- left + if not ovl then + love.graphics.rectangle("fill", x, y, 1, height) + end + + -- right + if not ovr then + love.graphics.rectangle("fill", x + width - 1, y, 1, height) + end + +end + +--[[--------------------------------------------------------- + - func: DrawFrame(object) + - desc: draws the frame object +--]]--------------------------------------------------------- +function skin.frame(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:GetHover() + local name = object:GetName() + local icon = object:GetIcon() + local bodycolor = skin.controls.frame_body_color + local topcolor = skin.controls.frame_top_color + local namecolor = skin.controls.frame_name_color + local font = skin.controls.frame_name_font + local topbarimage = skin.images["frame-topbar.png"] + local topbarimage_width = topbarimage:getWidth() + local topbarimage_height = topbarimage:getHeight() + local topbarimage_scalex = width/topbarimage_width + local topbarimage_scaley = 25/topbarimage_height + local bodyimage = skin.images["frame-body.png"] + local bodyimage_width = bodyimage:getWidth() + local bodyimage_height = bodyimage:getHeight() + local bodyimage_scalex = width/bodyimage_width + local bodyimage_scaley = height/bodyimage_height + + -- frame body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(bodyimage, x, y, 0, bodyimage_scalex, bodyimage_scaley) + + -- frame top bar + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(topbarimage, x, y, 0, topbarimage_scalex, topbarimage_scaley) + + -- frame name section + love.graphics.setFont(font) + + if icon then + local iconwidth = icon:getWidth() + local iconheight = icon:getHeight() + icon:setFilter("nearest", "nearest") + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(icon, x + 5, y + 5) + love.graphics.setColor(namecolor) + skin.PrintText(name, x + iconwidth + 10, y + 5) + else + love.graphics.setColor(namecolor) + skin.PrintText(name, x + 5, y + 5) + end + + -- frame border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + + love.graphics.setColor(1, 1, 1, 0.27) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, 24) + + love.graphics.setColor(0.86, 0.86, 0.86, 1) + skin.OutlinedRectangle(x + 1, y + 25, width - 2, height - 26) + +end + +--[[--------------------------------------------------------- + - func: DrawButton(object) + - desc: draws the button object +--]]--------------------------------------------------------- +function skin.button(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:GetHover() + local text = object:GetText() + local font = skin.controls.button_text_font + local twidth = font:getWidth(object.text) + local theight = font:getHeight(object.text) + local down = object:GetDown() + local checked = object.checked + local enabled = object:GetEnabled() + local clickable = object:GetClickable() + local textdowncolor = skin.controls.button_text_down_color + local texthovercolor = skin.controls.button_text_hover_color + local textnohovercolor = skin.controls.button_text_nohover_color + local textnonclickablecolor = skin.controls.button_text_nonclickable_color + local image_hover = skin.images["button-hover.png"] + local scaley = height/image_hover:getHeight() + + if not enabled or not clickable then + -- button body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(skin.images["button-unclickable.png"], x, y, 0, width, scaley) + -- button text + love.graphics.setFont(font) + love.graphics.setColor(textnonclickablecolor) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + return + end + + if object.toggleable then + if hover then + if down then + -- button body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(skin.images["button-down.png"], x, y, 0, width, scaley) + -- button text + love.graphics.setFont(font) + love.graphics.setColor(textdowncolor) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + else + -- button body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image_hover, x, y, 0, width, scaley) + -- button text + love.graphics.setFont(font) + love.graphics.setColor(texthovercolor) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + end + else + if object.toggle then + -- button body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(skin.images["button-down.png"], x, y, 0, width, scaley) + -- button text + love.graphics.setFont(font) + love.graphics.setColor(textdowncolor) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + else + -- button body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(skin.images["button-nohover.png"], x, y, 0, width, scaley) + -- button text + love.graphics.setFont(font) + love.graphics.setColor(textnohovercolor) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + end + end + else + if down or checked then + -- button body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(skin.images["button-down.png"], x, y, 0, width, scaley) + -- button text + love.graphics.setFont(font) + if object.image then + love.graphics.setColor(1, 1, 1) + love.graphics.draw(object.image, x + 2, y + height/2 - object.image:getHeight()/2) + love.graphics.setColor(textdowncolor) + local text = object.text + local font = skin.controls.button_text_font + while font:getWidth(text) > width - object.image:getWidth() - 10 do + text =loveframes.utf8.sub(text, 2) + while text:byte(1, 1) > 127 do text = loveframes.utf8.sub(text, 2) end + end + skin.PrintText(text, x + object.image:getWidth() + 4, y + height/2 - theight/2) + else + love.graphics.setColor(textdowncolor) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + end + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + elseif hover then + -- button body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image_hover, x, y, 0, width, scaley) + -- button text + love.graphics.setFont(font) + if object.image then + love.graphics.setColor(1, 1, 1) + love.graphics.draw(object.image, x + 2, y + height/2 - object.image:getHeight()/2) + love.graphics.setColor(texthovercolor) + local text = object.text + local font = skin.controls.button_text_font + while font:getWidth(text) > width - object.image:getWidth() - 10 do + text =loveframes.utf8.sub(text, 2) + while text:byte(1, 1) > 127 do text = loveframes.utf8.sub(text, 2) end + end + skin.PrintText(text, x + object.image:getWidth() + 4, y + height/2 - theight/2) + else + love.graphics.setColor(texthovercolor) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + end + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + else + -- button body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(skin.images["button-nohover.png"], x, y, 0, width, scaley) + -- button text + love.graphics.setFont(font) + if object.image then + love.graphics.setColor(1, 1, 1) + love.graphics.draw(object.image, object:GetX() + 2, object:GetY() + object:GetHeight()/2 - object.image:getHeight()/2) + love.graphics.setColor(textnohovercolor) + local text = object.text + local font = skin.controls.button_text_font + while font:getWidth(text) > width - object.image:getWidth() - 10 do + text =loveframes.utf8.sub(text, 2) + while text:byte(1, 1) > 127 do text = loveframes.utf8.sub(text, 2) end + end + skin.PrintText(text, x + object.image:getWidth() + 4, y + height/2 - theight/2) + else + love.graphics.setColor(textnohovercolor) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + end + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + end + end + + love.graphics.setColor(1, 1, 1, 0.59) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, height - 2) + +end + +--[[--------------------------------------------------------- + - func: DrawCloseButton(object) + - desc: draws the close button object +--]]--------------------------------------------------------- +function skin.closebutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local parent = object.parent + local parentwidth = parent:GetWidth() + local hover = object:GetHover() + local down = object.down + local image = skin.images["close.png"] + local bodydowncolor = skin.controls.closebutton_body_down_color + local bodyhovercolor = skin.controls.closebutton_body_hover_color + local bodynohovercolor = skin.controls.closebutton_body_nohover_color + + image:setFilter("nearest", "nearest") + + if down then + -- button body + love.graphics.setColor(bodydowncolor) + love.graphics.draw(image, x, y) + elseif hover then + -- button body + love.graphics.setColor(bodyhovercolor) + love.graphics.draw(image, x, y) + else + -- button body + love.graphics.setColor(bodynohovercolor) + love.graphics.draw(image, x, y) + end + +end + +--[[--------------------------------------------------------- + - func: DrawImage(object) + - desc: draws the image object +--]]--------------------------------------------------------- +function skin.image(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local orientation = object:GetOrientation() + local scalex = object:GetScaleX() + local scaley = object:GetScaleY() + local offsetx = object:GetOffsetX() + local offsety = object:GetOffsetY() + local shearx = object:GetShearX() + local sheary = object:GetShearY() + local image = object.image + local color = object.imagecolor + local stretch = object.stretch + + if stretch then + scalex, scaley = object:GetWidth() / image:getWidth(), object:GetHeight() / image:getHeight() + end + + if color then + love.graphics.setColor(color) + love.graphics.draw(image, x, y, orientation, scalex, scaley, offsetx, offsety, shearx, sheary) + else + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, orientation, scalex, scaley, offsetx, offsety, shearx, sheary) + end + +end + +--[[--------------------------------------------------------- + - func: DrawImageButton(object) + - desc: draws the image button object +--]]--------------------------------------------------------- +function skin.imagebutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local text = object:GetText() + local hover = object:GetHover() + local image = object:GetImage() + local imagecolor = object.imagecolor or {1, 1, 1, 1} + local down = object.down + local font = skin.controls.imagebutton_text_font + local twidth = font:getWidth(object.text) + local theight = font:getHeight(object.text) + local textdowncolor = skin.controls.imagebutton_text_down_color + local texthovercolor = skin.controls.imagebutton_text_hover_color + local textnohovercolor = skin.controls.imagebutton_text_nohover_color + local checked = object.checked + + if down then + if image then + love.graphics.setColor(imagecolor) + love.graphics.draw(image, x + 1, y + 1) + end + love.graphics.setFont(font) + love.graphics.setColor(0, 0, 0, 1) + skin.PrintText(text, x + width/2 - twidth/2 + 1, y + height - theight - 5 + 1) + love.graphics.setColor(textdowncolor) + skin.PrintText(text, x + width/2 - twidth/2 + 1, y + height - theight - 6 + 1) + elseif hover then + if image then + love.graphics.setColor(imagecolor) + love.graphics.draw(image, x, y) + end + love.graphics.setFont(font) + love.graphics.setColor(0, 0, 0, 1) + skin.PrintText(text, x + width/2 - twidth/2, y + height - theight - 5) + love.graphics.setColor(texthovercolor) + skin.PrintText(text, x + width/2 - twidth/2, y + height - theight - 6) + else + if image then + love.graphics.setColor(imagecolor) + love.graphics.draw(image, x, y) + end + love.graphics.setFont(font) + love.graphics.setColor(0, 0, 0, 1) + skin.PrintText(text, x + width/2 - twidth/2, y + height - theight - 5) + love.graphics.setColor(textnohovercolor) + skin.PrintText(text, x + width/2 - twidth/2, y + height - theight - 6) + end + if checked == true then + love.graphics.setColor(bordercolor) + love.graphics.setLineWidth(3) + love.graphics.setLineStyle("smooth") + love.graphics.rectangle("line", x+1, y+1, width-2, height-2) + end + +end + +--[[--------------------------------------------------------- + - func: DrawProgressBar(object) + - desc: draws the progress bar object +--]]--------------------------------------------------------- +function skin.progressbar(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local value = object:GetValue() + local max = object:GetMax() + local text = object:GetText() + local barwidth = object:GetBarWidth() + local font = skin.controls.progressbar_text_font + local twidth = font:getWidth(text) + local theight = font:getHeight("a") + local bodycolor = skin.controls.progressbar_body_color + local barcolor = skin.controls.progressbar_bar_color + local textcolor = skin.controls.progressbar_text_color + local image = skin.images["progressbar.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + + -- progress bar body + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, barwidth, scaley) + love.graphics.setFont(font) + love.graphics.setColor(textcolor) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + + -- progress bar border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + + object:SetText(value .. "/" ..max) + +end + +--[[--------------------------------------------------------- + - func: DrawScrollArea(object) + - desc: draws the scroll area object +--]]--------------------------------------------------------- +function skin.scrollarea(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bartype = object:GetBarType() + local bodycolor = skin.controls.scrollarea_body_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + love.graphics.setColor(bordercolor) + + if bartype == "vertical" then + --skin.OutlinedRectangle(x, y, width, height, true, true) + elseif bartype == "horizontal" then + --skin.OutlinedRectangle(x, y, width, height, false, false, true, true) + end + +end + +--[[--------------------------------------------------------- + - func: DrawScrollBar(object) + - desc: draws the scroll bar object +--]]--------------------------------------------------------- +function skin.scrollbar(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local dragging = object:IsDragging() + local hover = object:GetHover() + local bartype = object:GetBarType() + local bodydowncolor = skin.controls.scrollbar_body_down_color + local bodyhovercolor = skin.controls.scrollbar_body_hover_color + local bodynohvercolor = skin.controls.scrollbar_body_nohover_color + + if dragging then + local image = skin.images["button-down.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + elseif hover then + local image = skin.images["button-hover.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + else + local image = skin.images["button-nohover.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + end + +end + +--[[--------------------------------------------------------- + - func: DrawScrollBody(object) + - desc: draws the scroll body object +--]]--------------------------------------------------------- +function skin.scrollbody(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.scrollbody_body_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawPanel(object) + - desc: draws the panel object +--]]--------------------------------------------------------- +function skin.panel(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.panel_body_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + + love.graphics.setColor(1, 1, 1, 0.78) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, height - 2) + + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawList(object) + - desc: draws the list object +--]]--------------------------------------------------------- +function skin.list(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.list_body_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawList(object) + - desc: used to draw over the object and its children +--]]--------------------------------------------------------- +function skin.list_over(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawTabPanel(object) + - desc: draws the tab panel object +--]]--------------------------------------------------------- +function skin.tabpanel(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local buttonheight = object:GetHeightOfButtons() + local bodycolor = skin.controls.tabpanel_body_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y + buttonheight, width, height - buttonheight) + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y + buttonheight - 1, width, height - buttonheight + 2) + + object:SetScrollButtonSize(15, buttonheight) + +end + +--[[--------------------------------------------------------- + - func: DrawOverTabPanel(object) + - desc: draws over the tab panel object +--]]--------------------------------------------------------- +function skin.tabpanel_over(object) + +end + +--[[--------------------------------------------------------- + - func: DrawTabButton(object) + - desc: draws the tab button object +--]]--------------------------------------------------------- +function skin.tabbutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local hover = object:GetHover() + local text = object:GetText() + local image = object:GetImage() + local tabnumber = object:GetTabNumber() + local parent = object:GetParent() + local ptabnumber = parent:GetTabNumber() + local font = skin.controls.tab_text_font + local twidth = font:getWidth(object.text) + local theight = font:getHeight(object.text) + local imagewidth = 0 + local imageheight = 0 + local texthovercolor = skin.controls.button_text_hover_color + local textnohovercolor = skin.controls.button_text_nohover_color + + if image then + image:setFilter("nearest", "nearest") + imagewidth = image:getWidth() + imageheight = image:getHeight() + object.width = imagewidth + 15 + twidth + if imageheight > theight then + parent:SetTabHeight(imageheight + 5) + object.height = imageheight + 5 + else + object.height = parent.tabheight + end + else + object.width = 10 + twidth + object.height = parent.tabheight + end + + local width = object:GetWidth() + local height = object:GetHeight() + + if tabnumber == ptabnumber then + -- button body + local gradient = skin.images["button-hover.png"] + local gradientheight = gradient:getHeight() + local scaley = height/gradientheight + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(gradient, x, y, 0, width, scaley) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + if image then + -- button image + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x + 5, y + height/2 - imageheight/2) + -- button text + love.graphics.setFont(font) + love.graphics.setColor(texthovercolor) + skin.PrintText(text, x + imagewidth + 10, y + height/2 - theight/2) + else + -- button text + love.graphics.setFont(font) + love.graphics.setColor(texthovercolor) + skin.PrintText(text, x + 5, y + height/2 - theight/2) + end + else + -- button body + local gradient = skin.images["button-nohover.png"] + local gradientheight = gradient:getHeight() + local scaley = height/gradientheight + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(gradient, x, y, 0, width, scaley) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + if image then + -- button image + love.graphics.setColor(1, 1, 1, 0.59) + love.graphics.draw(image, x + 5, y + height/2 - imageheight/2) + -- button text + love.graphics.setFont(font) + love.graphics.setColor(textnohovercolor) + skin.PrintText(text, x + imagewidth + 10, y + height/2 - theight/2) + else + -- button text + love.graphics.setFont(font) + love.graphics.setColor(textnohovercolor) + skin.PrintText(text, x + 5, y + height/2 - theight/2) + end + end + +end + +--[[--------------------------------------------------------- + - func: DrawMultiChoice(object) + - desc: draws the multi choice object +--]]--------------------------------------------------------- +function skin.multichoice(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local text = object:GetText() + local choice = object:GetChoice() + local image = skin.images["multichoice-arrow.png"] + local font = skin.controls.multichoice_text_font + local theight = font:getHeight("a") + local bodycolor = skin.controls.multichoice_body_color + local textcolor = skin.controls.multichoice_text_color + + image:setFilter("nearest", "nearest") + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x + 1, y + 1, width - 2, height - 2) + + love.graphics.setColor(textcolor) + love.graphics.setFont(font) + + if choice == "" then + skin.PrintText(text, x + 5, y + height/2 - theight/2) + else + skin.PrintText(choice, x + 5, y + height/2 - theight/2) + end + + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x + width - 20, y + 5) + + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawMultiChoiceList(object) + - desc: draws the multi choice list object +--]]--------------------------------------------------------- +function skin.multichoicelist(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.multichoicelist_body_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawOverMultiChoiceList(object) + - desc: draws over the multi choice list object +--]]--------------------------------------------------------- +function skin.multichoicelist_over(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y - 1, width, height + 1) + +end + +--[[--------------------------------------------------------- + - func: DrawMultiChoiceRow(object) + - desc: draws the multi choice row object +--]]--------------------------------------------------------- +function skin.multichoicerow(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local text = object:GetText() + local font = skin.controls.multichoicerow_text_font + local bodyhovecolor = skin.controls.multichoicerow_body_hover_color + local texthovercolor = skin.controls.multichoicerow_text_hover_color + local bodynohovercolor = skin.controls.multichoicerow_body_nohover_color + local textnohovercolor = skin.controls.multichoicerow_text_nohover_color + + love.graphics.setFont(font) + + if object.hover then + love.graphics.setColor(bodyhovecolor) + love.graphics.rectangle("fill", x, y, width, height) + love.graphics.setColor(texthovercolor) + skin.PrintText(text, x + 5, y + 5) + else + love.graphics.setColor(bodynohovercolor) + love.graphics.rectangle("fill", x, y, width, height) + love.graphics.setColor(textnohovercolor) + skin.PrintText(text, x + 5, y + 5) + end + +end + +--[[--------------------------------------------------------- + - func: DrawToolTip(object) + - desc: draws the tool tip object +--]]--------------------------------------------------------- +function skin.tooltip(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.tooltip_body_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawText(object) + - desc: draws the text object +--]]--------------------------------------------------------- +function skin.text(object) + local textdata = object.formattedtext + local x = object.x + local y = object.y + local shadow = object.shadow + local shadowxoffset = object.shadowxoffset + local shadowyoffset = object.shadowyoffset + local shadowcolor = object.shadowcolor + local inlist, list = object:IsInList() + local printfunc = function(text, x, y) + love.graphics.print(text, math.floor(x + 0.5), math.floor(y + 0.5)) + end + + 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)) + printfunc(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 + printfunc(text, x + textx, y + texty) + end + else + love.graphics.setFont(font) + if shadow then + love.graphics.setColor(unpack(shadowcolor)) + printfunc(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 + printfunc(text, x + textx, y + texty) + end + end +end + +--[[--------------------------------------------------------- + - func: DrawTextInput(object) + - desc: draws the text input object +--]]--------------------------------------------------------- +function skin.textinput(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local font = object:GetFont() + local focus = object:GetFocus() + local showindicator = object:GetIndicatorVisibility() + local alltextselected = object:IsAllTextSelected() + local textx = object:GetTextX() + local texty = object:GetTextY() + local text = object:GetText() + local multiline = object:GetMultiLine() + local lines = object:GetLines() + local placeholder = object:GetPlaceholderText() + local offsetx = object:GetOffsetX() + local offsety = object:GetOffsetY() + local indicatorx = object:GetIndicatorX() + local indicatory = object:GetIndicatorY() + local vbar = object:HasVerticalScrollBar() + local hbar = object:HasHorizontalScrollBar() + local linenumbers = object:GetLineNumbersEnabled() + local itemwidth = object:GetItemWidth() + local masked = object:GetMasked() + local theight = font:getHeight("a") + local bodycolor = skin.controls.textinput_body_color + local textnormalcolor = skin.controls.textinput_text_normal_color + local textplaceholdercolor = skin.controls.textinput_text_placeholder_color + local textselectedcolor = skin.controls.textinput_text_selected_color + local highlightbarcolor = skin.controls.textinput_highlight_bar_color + local indicatorcolor = skin.controls.textinput_indicator_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + + if alltextselected then + local bary = 0 + if multiline then + for i=1, #lines do + local str = lines[i] + if masked then + str = loveframes.utf8.gsub(str, ".", "*") + end + local twidth = font:getWidth(str) + if twidth == 0 then + twidth = 5 + end + love.graphics.setColor(highlightbarcolor) + love.graphics.rectangle("fill", textx, texty + bary, twidth, theight) + bary = bary + theight + end + else + local twidth = 0 + if masked then + local maskchar = object:GetMaskChar() + twidth = font:getWidth(loveframes.utf8.gsub(text, ".", maskchar)) + else + twidth = font:getWidth(text) + end + love.graphics.setColor(highlightbarcolor) + love.graphics.rectangle("fill", textx, texty, twidth, theight) + end + end + + if showindicator and focus then + love.graphics.setColor(indicatorcolor) + love.graphics.rectangle("fill", indicatorx, indicatory, 1, theight) + end + + if not multiline then + object:SetTextOffsetY(height/2 - theight/2) + if offsetx ~= 0 then + object:SetTextOffsetX(0) + else + object:SetTextOffsetX(5) + end + else + if vbar then + if offsety ~= 0 then + if hbar then + object:SetTextOffsetY(5) + else + object:SetTextOffsetY(-5) + end + else + object:SetTextOffsetY(5) + end + else + object:SetTextOffsetY(5) + end + + if hbar then + if offsety ~= 0 then + if linenumbers then + local panel = object:GetLineNumbersPanel() + if vbar then + object:SetTextOffsetX(5) + else + object:SetTextOffsetX(-5) + end + else + if vbar then + object:SetTextOffsetX(5) + else + object:SetTextOffsetX(-5) + end + end + else + object:SetTextOffsetX(5) + end + else + object:SetTextOffsetX(5) + end + + end + + textx = object:GetTextX() + texty = object:GetTextY() + + love.graphics.setFont(font) + + if alltextselected then + love.graphics.setColor(textselectedcolor) + elseif #lines == 1 and lines[1] == "" then + love.graphics.setColor(textplaceholdercolor) + else + love.graphics.setColor(textnormalcolor) + end + + local str = "" + if multiline then + for i=1, #lines do + str = lines[i] + if masked then + local maskchar = object:GetMaskChar() + str = loveframes.utf8.gsub(str, ".", maskchar) + end + skin.PrintText(#str > 0 and str or (#lines == 1 and placeholder or ""), textx, texty + theight * i - theight) + end + else + str = lines[1] + if masked then + local maskchar = object:GetMaskChar() + str = loveframes.utf8.gsub(str, ".", maskchar) + end + skin.PrintText(#str > 0 and str or placeholder, textx, texty) + end + + love.graphics.setColor(0.9, 0.9, 0.9, 1) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, height - 2) + +end + +--[[--------------------------------------------------------- + - func: DrawOverTextInput(object) + - desc: draws over the text input object +--]]--------------------------------------------------------- +function skin.textinput_over(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawScrollButton(object) + - desc: draws the scroll button object +--]]--------------------------------------------------------- +function skin.scrollbutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:GetHover() + local scrolltype = object:GetScrollType() + local down = object.down + local bodydowncolor = skin.controls.button_body_down_color + local bodyhovercolor = skin.controls.button_body_hover_color + local bodynohovercolor = skin.controls.button_body_nohover_color + + if down then + -- button body + local image = skin.images["button-down.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + + elseif hover then + -- button body + local image = skin.images["button-hover.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + else + -- button body + local image = skin.images["button-nohover.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + end + + if scrolltype == "up" then + local image = skin.images["arrow-up.png"] + local imagewidth = image:getWidth() + local imageheight = image:getHeight() + image:setFilter("nearest", "nearest") + if hover then + love.graphics.setColor(1, 1, 1, 1) + else + love.graphics.setColor(1, 1, 1, 0.59) + end + love.graphics.draw(image, x + width/2 - imagewidth/2, y + height/2 - imageheight/2) + elseif scrolltype == "down" then + local image = skin.images["arrow-down.png"] + local imagewidth = image:getWidth() + local imageheight = image:getHeight() + image:setFilter("nearest", "nearest") + if hover then + love.graphics.setColor(1, 1, 1, 1) + else + love.graphics.setColor(1, 1, 1, 0.59) + end + love.graphics.draw(image, x + width/2 - imagewidth/2, y + height/2 - imageheight/2) + elseif scrolltype == "left" then + local image = skin.images["arrow-left.png"] + local imagewidth = image:getWidth() + local imageheight = image:getHeight() + image:setFilter("nearest", "nearest") + if hover then + love.graphics.setColor(1, 1, 1, 1) + else + love.graphics.setColor(1, 1, 1, 0.59) + end + love.graphics.draw(image, x + width/2 - imagewidth/2, y + height/2 - imageheight/2) + elseif scrolltype == "right" then + local image = skin.images["arrow-right.png"] + local imagewidth = image:getWidth() + local imageheight = image:getHeight() + image:setFilter("nearest", "nearest") + if hover then + love.graphics.setColor(1, 1, 1, 1) + else + love.graphics.setColor(1, 1, 1, 0.59) + end + love.graphics.draw(image, x + width/2 - imagewidth/2, y + height/2 - imageheight/2) + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawSlider(object) + - desc: draws the slider object +--]]--------------------------------------------------------- +function skin.slider(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local slidtype = object:GetSlideType() + local baroutlinecolor = skin.controls.slider_bar_outline_color + + if slidtype == "horizontal" then + love.graphics.setColor(baroutlinecolor) + love.graphics.rectangle("fill", x, y + height/2 - 5, width, 10) + love.graphics.setColor(bordercolor) + love.graphics.rectangle("fill", x + 5, y + height/2, width - 10, 1) + elseif slidtype == "vertical" then + love.graphics.setColor(baroutlinecolor) + love.graphics.rectangle("fill", x + width/2 - 5, y, 10, height) + love.graphics.setColor(bordercolor) + love.graphics.rectangle("fill", x + width/2, y + 5, 1, height - 10) + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawSliderButton(object) + - desc: draws the slider button object +--]]--------------------------------------------------------- +function skin.sliderbutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:GetHover() + local down = object.down + local parent = object:GetParent() + local enabled = parent:GetEnabled() + local bodydowncolor = skin.controls.button_body_down_color + local bodyhovercolor = skin.controls.button_body_hover_color + local bodynohvercolor = skin.controls.button_body_nohover_color + + if not enabled then + local image = skin.images["button-unclickable.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + -- button body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + return + end + + if down then + -- button body + local image = skin.images["button-down.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + elseif hover then + -- button body + local image = skin.images["button-hover.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + else + -- button body + local image = skin.images["button-nohover.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + -- button border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawCheckBox(object) + - desc: draws the check box object +--]]--------------------------------------------------------- +function skin.checkbox(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetBoxWidth() + local height = object:GetBoxHeight() + local checked = object:GetChecked() + local hover = object:GetHover() + local bodycolor = skin.controls.checkbox_body_color + local checkcolor = skin.controls.checkbox_check_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + + if checked then + love.graphics.setColor(checkcolor) + love.graphics.rectangle("fill", x + 4, y + 4, width - 8, height - 8) + end + + if hover then + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x + 4, y + 4, width - 8, height - 8) + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawCheckBox(object) + - desc: draws the radio button object +--]]--------------------------------------------------------- +function skin.radiobutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetBoxWidth() + local height = object:GetBoxHeight() + local checked = object:GetChecked() + local hover = object:GetHover() + local bodycolor = skin.controls.radiobutton_body_color + local checkcolor = skin.controls.radiobutton_check_color + local inner_border = skin.controls.radiobutton_inner_border_color + + love.graphics.setColor(bordercolor) + love.graphics.setLineStyle("smooth") + love.graphics.setLineWidth(1) + love.graphics.circle("line", x + 10, y + 10, 8, 15) + + if checked then + love.graphics.setColor(checkcolor) + love.graphics.circle("fill", x + 10, y + 10, 5, 360) + love.graphics.setColor(inner_border) + love.graphics.circle("line", x + 10, y + 10, 5, 360) + end + + if hover then + love.graphics.setColor(bordercolor) + love.graphics.circle("line", x + 10, y + 10, 5, 360) + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawCollapsibleCategory(object) + - desc: draws the collapsible category object +--]]--------------------------------------------------------- +function skin.collapsiblecategory(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local text = object:GetText() + local open = object:GetOpen() + local textcolor = skin.controls.collapsiblecategory_text_color + local font = skin.controls.smallfont + local image = skin.images["button-nohover.png"] + local topbarimage = skin.images["frame-topbar.png"] + local topbarimage_width = topbarimage:getWidth() + local topbarimage_height = topbarimage:getHeight() + local topbarimage_scalex = width/topbarimage_width + local topbarimage_scaley = 25/topbarimage_height + local imageheight = image:getHeight() + local scaley = height/imageheight + + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(topbarimage, x, y, 0, topbarimage_scalex, topbarimage_scaley) + + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + + love.graphics.setColor(1, 1, 1, 1) + if open then + local icon = skin.images["collapse.png"] + icon:setFilter("nearest", "nearest") + love.graphics.draw(icon, x + width - 21, y + 5) + love.graphics.setColor(1, 1, 1, 0.27) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, 24) + else + local icon = skin.images["expand.png"] + icon:setFilter("nearest", "nearest") + love.graphics.draw(icon, x + width - 21, y + 5) + love.graphics.setColor(1, 1, 1, 0.27) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, 23) + end + + love.graphics.setFont(font) + love.graphics.setColor(textcolor) + skin.PrintText(text, x + 5, y + 5) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawColumnList(object) + - desc: draws the column list object +--]]--------------------------------------------------------- +function skin.columnlist(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.columnlist_body_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawColumnListHeader(object) + - desc: draws the column list header object +--]]--------------------------------------------------------- +function skin.columnlistheader(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:GetHover() + local down = object.down + local font = skin.controls.columnlistheader_text_font + local theight = font:getHeight(object.name) + local bodydowncolor = skin.controls.columnlistheader_body_down_color + local textdowncolor = skin.controls.columnlistheader_text_down_color + local bodyhovercolor = skin.controls.columnlistheader_body_hover_color + local textdowncolor = skin.controls.columnlistheader_text_hover_color + local nohovercolor = skin.controls.columnlistheader_body_nohover_color + local textnohovercolor = skin.controls.columnlistheader_text_nohover_color + + local name = ParseHeaderText(object:GetName(), x, width, x + width/2, twidth) + local twidth = font:getWidth(name) + + if down then + local image = skin.images["button-down.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + -- header body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + -- header name + love.graphics.setFont(font) + love.graphics.setColor(textdowncolor) + skin.PrintText(name, x + width/2 - twidth/2, y + height/2 - theight/2) + -- header border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + elseif hover then + local image = skin.images["button-hover.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + -- header body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + -- header name + love.graphics.setFont(font) + love.graphics.setColor(textdowncolor) + skin.PrintText(name, x + width/2 - twidth/2, y + height/2 - theight/2) + -- header border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + else + local image = skin.images["button-nohover.png"] + local imageheight = image:getHeight() + local scaley = height/imageheight + -- header body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + -- header name + love.graphics.setFont(font) + love.graphics.setColor(textnohovercolor) + skin.PrintText(name, x + width/2 - twidth/2, y + height/2 - theight/2) + -- header border + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawColumnListArea(object) + - desc: draws the column list area object +--]]--------------------------------------------------------- +function skin.columnlistarea(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.columnlistarea_body_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + + local cheight = 0 + local columns = object:GetParent():GetChildren() + if #columns > 0 then + cheight = columns[1]:GetHeight() + end + + local image = skin.images["button-nohover.png"] + local scaley = cheight/image:getHeight() + + -- header body + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, x, y, 0, width, scaley) + + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, cheight, true, false, true, true) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawOverColumnListArea(object) + - desc: draws over the column list area object +--]]--------------------------------------------------------- +function skin.columnlistarea_over(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawColumnListRow(object) + - desc: draws the column list row object +--]]--------------------------------------------------------- +function skin.columnlistrow(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local colorindex = object:GetColorIndex() + local font = object:GetFont() + local columndata = object:GetColumnData() + local textx = object:GetTextX() + local texty = object:GetTextY() + local parent = object:GetParent() + local theight = font:getHeight("a") + local hover = object:GetHover() + local selected = object:GetSelected() + local body1color = skin.controls.columnlistrow_body1_color + local body2color = skin.controls.columnlistrow_body2_color + local bodyhovercolor = skin.controls.columnlistrow_body_hover_color + local bodyselectedcolor = skin.controls.columnlistrow_body_selected_color + local textcolor = skin.controls.columnlistrow_text_color + local texthovercolor = skin.controls.columnlistrow_text_hover_color + local textselectedcolor = skin.controls.columnlistrow_text_selected_color + + object:SetTextPos(5, height/2 - theight/2) + + if selected then + love.graphics.setColor(bodyselectedcolor) + love.graphics.rectangle("fill", x, y, width, height) + elseif hover then + love.graphics.setColor(bodyhovercolor) + love.graphics.rectangle("fill", x, y, width, height) + elseif colorindex == 1 then + love.graphics.setColor(body1color) + love.graphics.rectangle("fill", x, y, width, height) + else + love.graphics.setColor(body2color) + love.graphics.rectangle("fill", x, y, width, height) + end + + love.graphics.setFont(font) + if selected then + love.graphics.setColor(textselectedcolor) + elseif hover then + love.graphics.setColor(texthovercolor) + else + love.graphics.setColor(textcolor) + end + for k, v in ipairs(columndata) do + local rwidth = parent.parent:GetColumnWidth(k) + if rwidth then + local text = ParseRowText(v, x, rwidth, x, textx) + skin.PrintText(text, x + textx, y + texty) + x = x + parent.parent.children[k]:GetWidth() + else + break + end + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawModalBackground(object) + - desc: draws the modal background object +--]]--------------------------------------------------------- +function skin.modalbackground(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.modalbackground_body_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawLineNumbersPanel(object) + - desc: draws the line numbers panel object +--]]--------------------------------------------------------- +function skin.linenumberspanel(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local offsety = object:GetOffsetY() + local parent = object:GetParent() + local lines = parent:GetLines() + local font = parent:GetFont() + local theight = font:getHeight("a") + local textcolor = skin.controls.linenumberspanel_text_color + local bodycolor = skin.controls.linenumberspanel_body_color + + object:SetWidth(10 + font:getWidth(#lines)) + love.graphics.setFont(font) + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + + love.graphics.setColor(bordercolor) + --skin.OutlinedRectangle(x, y, width, height, true, true, true, false) + + local startline = math.ceil(offsety / theight) + if startline < 1 then + startline = 1 + end + local endline = math.ceil(startline + (height / theight)) + 1 + if endline > #lines then + endline = #lines + end + + for i=startline, endline do + love.graphics.setColor(textcolor) + skin.PrintText(i, x + 5, (y + (theight * (i - 1))) - offsety) + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawNumberBox(object) + - desc: draws the numberbox object +--]]--------------------------------------------------------- +function skin.numberbox(object) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawGrid(object) + - desc: draws the grid object +--]]--------------------------------------------------------- +function skin.grid(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.grid_body_color + + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + + local cx = x + local cy = y + local cw = object.cellwidth + (object.cellpadding * 2) + local ch = object.cellheight + (object.cellpadding * 2) + + for i=1, object.rows do + for n=1, object.columns do + local ovt = false + local ovl = false + if i > 1 then + ovt = true + end + if n > 1 then + ovl = true + end + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", cx, cy, cw, ch) + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(cx, cy, cw, ch, ovt, false, ovl, false) + cx = cx + cw + end + cx = x + cy = cy + ch + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawForm(object) + - desc: draws the form object +--]]--------------------------------------------------------- +function skin.form(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local topmargin = object.topmargin + local name = object.name + local font = skin.controls.form_text_font + local textcolor = skin.controls.form_text_color + local twidth = font:getWidth(name) + + love.graphics.setFont(font) + love.graphics.setColor(textcolor) + skin.PrintText(name, x + 7, y) + + love.graphics.setColor(bordercolor) + love.graphics.rectangle("fill", x, y + 7, 5, 1) + love.graphics.rectangle("fill", x + twidth + 9, y + 7, width - (twidth + 9), 1) + love.graphics.rectangle("fill", x, y + height, width, 1) + love.graphics.rectangle("fill", x, y + 7, 1, height - 7) + love.graphics.rectangle("fill", x + width - 1, y + 7, 1, height - 7) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawMenu(object) + - desc: draws the menu object +--]]--------------------------------------------------------- +function skin.menu(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.menu_body_color + + love.graphics.setColor(bodycolor) + love.graphics.rectangle("fill", x, y, width, height) + + love.graphics.setColor(bordercolor) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawMenuOption(object) + - desc: draws the menuoption object +--]]--------------------------------------------------------- +function skin.menuoption(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:GetHover() + local text = object:GetText() + local icon = object:GetIcon() + local option_type = object.option_type + local body_hover_color = skin.controls.menuoption_body_hover_color + local text_hover_color = skin.controls.menuoption_text_hover_color + local text_color = skin.controls.menuoption_text_color + local twidth = skin.controls.smallfont:getWidth(text) + + + if option_type == "divider" then + love.graphics.setColor(0.78, 0.78, 0.78, 1) + love.graphics.rectangle("fill", x + 4, y + 2, width - 8, 1) + object.contentheight = 10 + else + love.graphics.setFont(skin.controls.smallfont) + if hover then + love.graphics.setColor(body_hover_color) + love.graphics.rectangle("fill", x + 2, y + 2, width - 4, height - 4) + love.graphics.setColor(text_hover_color) + skin.PrintText(text, x + 26, y + 5) + else + love.graphics.setColor(text_color) + skin.PrintText(text, x + 26, y + 5) + end + if icon then + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(icon, x + 5, y + 5) + end + object.contentwidth = twidth + 31 + object.contentheight = 25 + end + +end + +function skin.tree(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(0.78, 0.78, 0.78, 1) + love.graphics.rectangle("fill", x, y, width, height) + +end + +function skin.treenode(object) + + local icon = object.icon + local buttonimage = skin.images["tree-node-button-open.png"] + local width = 0 + local x = object.x + local leftpadding = 15 * object.level + + if object.level > 0 then + leftpadding = leftpadding + buttonimage:getWidth() + 5 + else + leftpadding = buttonimage:getWidth() + 5 + end + + local iconwidth + if icon then + iconwidth = icon:getWidth() + end + + local twidth = loveframes.basicfont:getWidth(object.text) + local theight = loveframes.basicfont:getHeight(object.text) + + if object.tree.selectednode == object then + love.graphics.setColor(0.4, 0.55, 1, 1) + love.graphics.rectangle("fill", x + leftpadding + 2 + iconwidth, object.y + 2, twidth, theight) + end + + width = width + iconwidth + loveframes.basicfont:getWidth(object.text) + leftpadding + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(icon, x + leftpadding, object.y) + love.graphics.setFont(loveframes.basicfont) + love.graphics.setColor(0, 0, 0, 1) + skin.PrintText(object.text, x + leftpadding + 2 + iconwidth, object.y + 2) + + object:SetWidth(width + 5) + +end + +function skin.treenodebutton(object) + + local leftpadding = 15 * object.parent.level + local image + + if object.parent.open then + image = skin.images["tree-node-button-close.png"] + else + image = skin.images["tree-node-button-open.png"] + end + + image:setFilter("nearest", "nearest") + + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(image, object.x, object.y) + + object:SetPos(2 + leftpadding, 3) + object:SetSize(image:getWidth(), image:getHeight()) + +end + +-- register the skin +loveframes.RegisterSkin(skin) + +---------- module end ---------- +end diff --git a/loveframes/skins/Default/images/arrow-down.png b/loveframes/skins/Default/images/arrow-down.png new file mode 100644 index 0000000..504f527 Binary files /dev/null and b/loveframes/skins/Default/images/arrow-down.png differ diff --git a/loveframes/skins/Default/images/arrow-left.png b/loveframes/skins/Default/images/arrow-left.png new file mode 100644 index 0000000..3521f01 Binary files /dev/null and b/loveframes/skins/Default/images/arrow-left.png differ diff --git a/loveframes/skins/Default/images/arrow-right.png b/loveframes/skins/Default/images/arrow-right.png new file mode 100644 index 0000000..1ed3fa4 Binary files /dev/null and b/loveframes/skins/Default/images/arrow-right.png differ diff --git a/loveframes/skins/Default/images/arrow-up.png b/loveframes/skins/Default/images/arrow-up.png new file mode 100644 index 0000000..f6b9375 Binary files /dev/null and b/loveframes/skins/Default/images/arrow-up.png differ diff --git a/loveframes/skins/Default/images/check-off.png b/loveframes/skins/Default/images/check-off.png new file mode 100644 index 0000000..6e18c0e Binary files /dev/null and b/loveframes/skins/Default/images/check-off.png differ diff --git a/loveframes/skins/Default/images/check-on.png b/loveframes/skins/Default/images/check-on.png new file mode 100644 index 0000000..0bce921 Binary files /dev/null and b/loveframes/skins/Default/images/check-on.png differ diff --git a/loveframes/skins/Default/images/close.png b/loveframes/skins/Default/images/close.png new file mode 100644 index 0000000..a1605dd Binary files /dev/null and b/loveframes/skins/Default/images/close.png differ diff --git a/loveframes/skins/Default/images/collapse.png b/loveframes/skins/Default/images/collapse.png new file mode 100644 index 0000000..ad22eb9 Binary files /dev/null and b/loveframes/skins/Default/images/collapse.png differ diff --git a/loveframes/skins/Default/images/drawing.svg b/loveframes/skins/Default/images/drawing.svg new file mode 100644 index 0000000..9ee9086 --- /dev/null +++ b/loveframes/skins/Default/images/drawing.svg @@ -0,0 +1,519 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/loveframes/skins/Default/images/expand.png b/loveframes/skins/Default/images/expand.png new file mode 100644 index 0000000..c1bd253 Binary files /dev/null and b/loveframes/skins/Default/images/expand.png differ diff --git a/loveframes/skins/Default/images/multichoice-arrow.png b/loveframes/skins/Default/images/multichoice-arrow.png new file mode 100644 index 0000000..710b2e4 Binary files /dev/null and b/loveframes/skins/Default/images/multichoice-arrow.png differ diff --git a/loveframes/skins/Default/images/radio-off.png b/loveframes/skins/Default/images/radio-off.png new file mode 100644 index 0000000..4ae6d7b Binary files /dev/null and b/loveframes/skins/Default/images/radio-off.png differ diff --git a/loveframes/skins/Default/images/radio-on.png b/loveframes/skins/Default/images/radio-on.png new file mode 100644 index 0000000..161a23d Binary files /dev/null and b/loveframes/skins/Default/images/radio-on.png differ diff --git a/loveframes/skins/Default/images/slider.png b/loveframes/skins/Default/images/slider.png new file mode 100644 index 0000000..fa14edd Binary files /dev/null and b/loveframes/skins/Default/images/slider.png differ diff --git a/loveframes/skins/Default/images/tree-node-button-close.png b/loveframes/skins/Default/images/tree-node-button-close.png new file mode 100644 index 0000000..644acd4 Binary files /dev/null and b/loveframes/skins/Default/images/tree-node-button-close.png differ diff --git a/loveframes/skins/Default/images/tree-node-button-open.png b/loveframes/skins/Default/images/tree-node-button-open.png new file mode 100644 index 0000000..509c172 Binary files /dev/null and b/loveframes/skins/Default/images/tree-node-button-open.png differ diff --git a/loveframes/skins/Default/skin.lua b/loveframes/skins/Default/skin.lua new file mode 100644 index 0000000..f897880 --- /dev/null +++ b/loveframes/skins/Default/skin.lua @@ -0,0 +1,1788 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- skin table +local skin = {} + +-- skin info (you always need this in a skin) +skin.name = "Default" +skin.author = "ingsoc451" +skin.version = "0.9" + +local color = function(s, a) return {loveframes.Color(s, a)} end + +-- Controls +skin.controls = {} +skin.controls.smallfont = love.graphics.newFont(11) +skin.controls.imagebuttonfont = love.graphics.newFont(15) +skin.controls.color_image = color"FFFFFF" + +skin.controls.color_back0 = color"f0f0f0" +skin.controls.color_back1 = color"e0e0e0" +skin.controls.color_back2 = color"c0c0c0" +skin.controls.color_back3 = color"a0a0a0" +skin.controls.color_fore0 = color"505050" +skin.controls.color_fore1 = color"373737" +skin.controls.color_fore2 = color"202020" +skin.controls.color_fore3 = color"101010" +skin.controls.color_active = color"6298b3" --some blue + +-- Directives +skin.directives = {} + +skin.directives.text_default_color = skin.controls.color_fore0 + + +local function ParseHeaderText(str, hx, hwidth, tx) + + local font = love.graphics.getFont() + local twidth = love.graphics.getFont():getWidth(str) + + if (tx + twidth) - hwidth/2 > hx + hwidth then + if #str > 1 then + return ParseHeaderText(loveframes.utf8.sub(str, 1, #str - 1), hx, hwidth, tx, twidth) + else + return str + end + else + return str + end + +end + +local function ParseRowText(str, rx, rwidth, tx1, tx2) + + local twidth = love.graphics.getFont():getWidth(str) + + if (tx1 + tx2) + twidth > rx + rwidth then + if #str > 1 then + return ParseRowText(loveframes.utf8.sub(str, 1, #str - 1), rx, rwidth, tx1, tx2) + else + return str + end + else + return str + end + +end + +function skin.PrintText(text, x, y) + love.graphics.print(text, math.floor(x + 0.5), math.floor(y + 0.5)) +end + +--[[--------------------------------------------------------- + - func: OutlinedRectangle(x, y, width, height, ovt, ovb, ovl, ovr) + - desc: creates and outlined rectangle +--]]--------------------------------------------------------- +function skin.OutlinedRectangle(x, y, width, height, ovt, ovb, ovl, ovr) + + local ovt = ovt or false + local ovb = ovb or false + local ovl = ovl or false + local ovr = ovr or false + + -- top + if not ovt then + love.graphics.rectangle("fill", x, y, width, 1) + end + + -- bottom + if not ovb then + love.graphics.rectangle("fill", x, y + height - 1, width, 1) + end + + -- left + if not ovl then + love.graphics.rectangle("fill", x, y, 1, height) + end + + -- right + if not ovr then + love.graphics.rectangle("fill", x + width - 1, y, 1, height) + end + +end + +--[[--------------------------------------------------------- + - func: DrawFrame(object) + - desc: draws the frame object +--]]--------------------------------------------------------- +function skin.frame(object) + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:IsTopChild() + local name = object:GetName() + local icon = object:GetIcon() + local font = skin.controls.smallfont + + local body = skin.controls.color_back0 + local top = hover and skin.controls.color_active or skin.controls.color_fore0 + local fore = skin.controls.color_back0 + local border = skin.controls.color_back1 + + -- frame body + love.graphics.setColor(body) + love.graphics.rectangle("fill", x, y, width, height) + + -- frame top bar + love.graphics.setColor(top) + love.graphics.rectangle("fill", x, y, width, 25) + + -- frame name section + love.graphics.setFont(font) + + if icon then + local iconwidth = icon:getWidth() + local iconheight = icon:getHeight() + --icon:setFilter("nearest", "nearest") + love.graphics.setColor(skin.controls.color_image) + love.graphics.draw(icon, x + 5, y + 5) + love.graphics.setColor(fore) + skin.PrintText(name, x + iconwidth + 10, y + 5) + else + love.graphics.setColor(fore) + skin.PrintText(name, x + 5, y + 5) + end + + -- frame border + love.graphics.setColor(border) + skin.OutlinedRectangle(x, y, width, height) + love.graphics.setColor(border) + skin.OutlinedRectangle(x, y, width, height) +end + +--[[--------------------------------------------------------- + - func: DrawButton(object) + - desc: draws the button object +--]]--------------------------------------------------------- + +function skin.button(object) + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:GetHover() + local text = object:GetText() + local font = skin.controls.smallfont + local twidth = font:getWidth(object.text) + local theight = font:getHeight(object.text) + local down = object:GetDown() + local checked = object.checked + local enabled = object:GetEnabled() + local clickable = object:GetClickable() + local back, fore, border + + love.graphics.setFont(font) + + if not enabled or not clickable then + back = skin.controls.color_back1 + fore = skin.controls.color_back2 + border = skin.controls.color_back2 + -- button body + love.graphics.setColor(back) + love.graphics.rectangle("fill", x, y, width, height) + -- button text + love.graphics.setFont(font) + love.graphics.setColor(skin.controls.color_back3) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + -- button border + love.graphics.setColor(border) + skin.OutlinedRectangle(x, y, width, height) + return + end + + if object.toggleable then + if hover then + if down then + back = skin.controls.color_active + fore = skin.controls.color_back0 + border = skin.controls.color_fore2 + else + back = skin.controls.color_active + fore = skin.controls.color_back0 + border = skin.controls.color_fore1 + end + else + if object.toggle then + back = skin.controls.color_fore0 + fore = skin.controls.color_back0 + border = skin.controls.color_fore2 + else + back = skin.controls.color_back2 + fore = skin.controls.color_fore0 + border = skin.controls.color_fore0 + end + end + + -- button body + love.graphics.setColor(back) + love.graphics.rectangle("fill", x, y, width, height) + -- button text + love.graphics.setColor(fore) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + -- button border + love.graphics.setColor(border) + skin.OutlinedRectangle(x, y, width, height) + + else + if down or checked then + back = skin.controls.color_fore0 + fore = skin.controls.color_back0 + border = skin.controls.color_fore2 + elseif hover then + back = skin.controls.color_active + fore = skin.controls.color_back0 + border = skin.controls.color_fore1 + else + back = skin.controls.color_back2 + fore = skin.controls.color_fore0 + border = skin.controls.color_fore0 + end + + -- button body + love.graphics.setColor(back) + love.graphics.rectangle("fill", x, y, width, height) + -- button text + if object.image then + love.graphics.setColor(skin.controls.color_image) + love.graphics.draw(object.image, x + 5, y + height/2 - object.image:getHeight()/2) + end + + love.graphics.setColor(fore) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + -- button border + love.graphics.setColor(border) + skin.OutlinedRectangle(x, y, width, height) + end + + love.graphics.setColor(skin.controls.color_back0) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, height - 2) + +end + +--[[--------------------------------------------------------- + - func: DrawCloseButton(object) + - desc: draws the close button object +--]]--------------------------------------------------------- +function skin.closebutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local parent = object.parent + local parentwidth = parent:GetWidth() + local hover = object:GetHover() + local down = object.down + local image = skin.images["close.png"] + local fore + + --image:setFilter("nearest", "nearest") + + if down then + fore = skin.controls.color_back2 + elseif hover then + fore = skin.controls.color_back1 + else + fore = skin.controls.color_back0 + end + + love.graphics.setColor(fore) + love.graphics.draw(image, x, y) +end + +--[[--------------------------------------------------------- + - func: DrawImage(object) + - desc: draws the image object +--]]--------------------------------------------------------- +function skin.image(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local orientation = object:GetOrientation() + local scalex = object:GetScaleX() + local scaley = object:GetScaleY() + local offsetx = object:GetOffsetX() + local offsety = object:GetOffsetY() + local shearx = object:GetShearX() + local sheary = object:GetShearY() + local image = object.image + local imagecolor = object.imagecolor or skin.controls.color_image + local stretch = object.stretch + + if stretch then + scalex, scaley = object:GetWidth() / image:getWidth(), object:GetHeight() / image:getHeight() + end + + love.graphics.setColor(imagecolor) + love.graphics.draw(image, x, y, orientation, scalex, scaley, offsetx, offsety, shearx, sheary) + +end + +--[[--------------------------------------------------------- + - func: DrawImageButton(object) + - desc: draws the image button object +--]]--------------------------------------------------------- +function skin.imagebutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local text = object:GetText() + local hover = object:GetHover() + local image = object:GetImage() + local imagecolor = object.imagecolor or skin.controls.color_image + local down = object.down + local font = skin.controls.imagebuttonfont + local twidth = font:getWidth(object.text) + local theight = font:getHeight(object.text) + local checked = object.checked + + local fore1, fore2 = skin.controls.color_back0 + + if down then + if image then + love.graphics.setColor(imagecolor) + love.graphics.draw(image, x + 1, y + 1) + end + love.graphics.setFont(font) + love.graphics.setColor(skin.controls.color_back2) + skin.PrintText(text, x + width/2 - twidth/2 + 1, y + height - theight - 5 + 1) + love.graphics.setColor(skin.controls.color_fore3) + skin.PrintText(text, x + width/2 - twidth/2 + 1, y + height - theight - 6 + 1) + elseif hover then + if image then + love.graphics.setColor(imagecolor) + love.graphics.draw(image, x, y) + end + love.graphics.setFont(font) + love.graphics.setColor(skin.controls.color_back1) + skin.PrintText(text, x + width/2 - twidth/2, y + height - theight - 5) + love.graphics.setColor(skin.controls.color_fore2) + skin.PrintText(text, x + width/2 - twidth/2, y + height - theight - 6) + else + if image then + love.graphics.setColor(imagecolor) + love.graphics.draw(image, x, y) + end + love.graphics.setFont(font) + love.graphics.setColor(skin.controls.color_back0) + skin.PrintText(text, x + width/2 - twidth/2, y + height - theight - 5) + love.graphics.setColor(skin.controls.color_fore0) + skin.PrintText(text, x + width/2 - twidth/2, y + height - theight - 6) + end + if checked == true then + love.graphics.setColor(skin.controls.color_back2) + love.graphics.setLineWidth(3) + love.graphics.setLineStyle("smooth") + love.graphics.rectangle("line", x+1, y+1, width-2, height-2) + end + +end + +--[[--------------------------------------------------------- + - func: DrawProgressBar(object) + - desc: draws the progress bar object +--]]--------------------------------------------------------- +function skin.progressbar(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local value = object:GetValue() + local max = object:GetMax() + local text = object:GetText() + local barwidth = object:GetBarWidth() + local font = skin.controls.smallfont + local twidth = font:getWidth(text) + local theight = font:getHeight("m") + + -- progress bar body + love.graphics.setColor(skin.controls.color_back0) + love.graphics.rectangle("fill", x, y, width, height) + love.graphics.setColor(skin.controls.color_back2) + love.graphics.rectangle("fill", x, y, barwidth, height) + love.graphics.setFont(font) + love.graphics.setColor(skin.controls.color_fore0) + skin.PrintText(text, x + width/2 - twidth/2, y + height/2 - theight/2) + + -- progress bar border + love.graphics.setColor(skin.controls.color_fore0) + skin.OutlinedRectangle(x, y, width, height) + + object:SetText(value .. "/" ..max) + +end + +--[[--------------------------------------------------------- + - func: DrawScrollArea(object) + - desc: draws the scroll area object +--]]--------------------------------------------------------- +function skin.scrollarea(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bartype = object:GetBarType() + + love.graphics.setColor(skin.controls.color_back0) + love.graphics.rectangle("fill", x, y, width, height) + love.graphics.setColor(skin.controls.color_back1) + + if bartype == "vertical" then + skin.OutlinedRectangle(x, y, width, height, true, true) + elseif bartype == "horizontal" then + skin.OutlinedRectangle(x, y, width, height, false, false, true, true) + end + +end + +--[[--------------------------------------------------------- + - func: DrawScrollBar(object) + - desc: draws the scroll bar object +--]]--------------------------------------------------------- +function skin.scrollbar(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local dragging = object:IsDragging() + local hover = object:GetHover() + local bartype = object:GetBarType() + local back, border + + if dragging then + back = skin.controls.color_fore0 + border = skin.controls.color_fore2 + elseif hover then + back = skin.controls.color_active + border = skin.controls.color_fore1 + else + back = skin.controls.color_back2 + border = skin.controls.color_fore0 + end + + love.graphics.setColor(back) + love.graphics.rectangle("fill", x, y, width, height) + love.graphics.setColor(border) + skin.OutlinedRectangle(x, y, width, height) + + love.graphics.setColor(skin.controls.color_back0) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, height - 2) +end + +--[[--------------------------------------------------------- + - func: DrawScrollBody(object) + - desc: draws the scroll body object +--]]--------------------------------------------------------- +function skin.scrollbody(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.scrollbody_body_color + + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", x, y, width, height) + love.graphics.setColor(skin.controls.color_back2) + skin.OutlinedRectangle(x, y, width, height) + +end + + +--[[--------------------------------------------------------- + - func: DrawScrollButton(object) + - desc: draws the scroll button object +--]]--------------------------------------------------------- +function skin.scrollbutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:GetHover() + local scrolltype = object:GetScrollType() + local down = object.down + local back, fore, border + + if down then + back = skin.controls.color_fore0 + fore = skin.controls.color_back0 + border = skin.controls.color_fore2 + elseif hover then + back = skin.controls.color_active + fore = skin.controls.color_back0 + border = skin.controls.color_fore1 + else + back = skin.controls.color_back2 + fore = skin.controls.color_fore0 + border = skin.controls.color_fore0 + end + + -- button back + love.graphics.setColor(back) + love.graphics.rectangle("fill", x, y, width, height) + -- button border + love.graphics.setColor(border) + skin.OutlinedRectangle(x, y, width, height) + + local image + if scrolltype == "up" then + image = skin.images["arrow-up.png"] + elseif scrolltype == "down" then + image = skin.images["arrow-down.png"] + elseif scrolltype == "left" then + image = skin.images["arrow-left.png"] + elseif scrolltype == "right" then + image = skin.images["arrow-right.png"] + end + + local imagewidth = image:getWidth() + local imageheight = image:getHeight() + --image:setFilter("nearest", "nearest") + love.graphics.setColor(fore) + + love.graphics.draw(image, x + width/2 - imagewidth/2, y + height/2 - imageheight/2) + + love.graphics.setColor(skin.controls.color_back0) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, height - 2) +end + +--[[--------------------------------------------------------- + - func: skin.DrawSlider(object) + - desc: draws the slider object +--]]--------------------------------------------------------- +function skin.slider(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local slidtype = object:GetSlideType() + local body = skin.controls.color_back1 + local border = skin.controls.color_back3 + + if slidtype == "horizontal" then + love.graphics.setColor(body) + love.graphics.rectangle("fill", x, y + height/2 - 3, width, 6) + love.graphics.setColor(border) + love.graphics.rectangle("fill", x + 5, y + height/2 - 1, width - 10, 2) + elseif slidtype == "vertical" then + love.graphics.setColor(body) + love.graphics.rectangle("fill", x + width/2 - 3, y, 6, height) + love.graphics.setColor(border) + love.graphics.rectangle("fill", x + width/2 - 1, y + 5, 2, height - 10) + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawSliderButton(object) + - desc: draws the slider button object +--]]--------------------------------------------------------- +function skin.sliderbutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:GetHover() + local down = object.down + local parent = object:GetParent() + local enabled = parent:GetEnabled() + + if not enabled then + -- button body + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", x, y, width, height) + -- button border + love.graphics.setColor(skin.controls.color_back2) + skin.OutlinedRectangle(x, y, width, height) + return + end + + + local image = skin.images["slider.png"] + local imagewidth = image:getWidth() + local imageheight = image:getHeight() + --image:setFilter("nearest", "nearest") + + local fore + if down then + fore = skin.controls.color_fore0 + elseif hover then + fore = skin.controls.color_active + else + fore = skin.controls.color_back3 + end + + love.graphics.setColor(fore) + love.graphics.draw(image, x + (width - imagewidth) / 2, y + (height - imageheight) / 2) +end + +--[[--------------------------------------------------------- + - func: DrawPanel(object) + - desc: draws the panel object +--]]--------------------------------------------------------- +function skin.panel(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", x, y, width, height) + + --love.graphics.setColor(skin.controls.color_back1) + --skin.OutlinedRectangle(x + 1, y + 1, width - 2, height - 2) + + love.graphics.setColor(skin.controls.color_back2) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawList(object) + - desc: draws the list object +--]]--------------------------------------------------------- +function skin.list(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local bodycolor = skin.controls.list_body_color + + love.graphics.setColor(skin.controls.color_back0) + love.graphics.rectangle("fill", x, y, width, height) + --love.graphics.setColor(skin.controls.color_back3) + --skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawList(object) + - desc: used to draw over the object and its children +--]]--------------------------------------------------------- +function skin.list_over(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(skin.controls.color_back3) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawTabPanel(object) + - desc: draws the tab panel object +--]]--------------------------------------------------------- +function skin.tabpanel(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local buttonheight = object:GetHeightOfButtons() + + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", x, y + buttonheight, width, height - buttonheight) + love.graphics.setColor(skin.controls.color_back2) + skin.OutlinedRectangle(x, y + buttonheight - 1, width, height - buttonheight + 2) + + object:SetScrollButtonSize(15, buttonheight) + +end + +--[[--------------------------------------------------------- + - func: DrawOverTabPanel(object) + - desc: draws over the tab panel object +--]]--------------------------------------------------------- +function skin.tabpanel_over(object) + +end + +--[[--------------------------------------------------------- + - func: DrawTabButton(object) + - desc: draws the tab button object +--]]--------------------------------------------------------- +function skin.tabbutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local hover = object:GetHover() + local text = object:GetText() + local image = object:GetImage() + local tabnumber = object:GetTabNumber() + local parent = object:GetParent() + local ptabnumber = parent:GetTabNumber() + local font = skin.controls.smallfont + local twidth = font:getWidth(object.text) + local theight = font:getHeight(object.text) + local imagewidth = 0 + local imageheight = 0 + local texthovercolor = skin.controls.button_text_hover_color + local textnohovercolor = skin.controls.button_text_nohover_color + + if image then + --image:setFilter("nearest", "nearest") + imagewidth = image:getWidth() + imageheight = image:getHeight() + object.width = imagewidth + 15 + twidth + if imageheight > theight then + parent:SetTabHeight(imageheight + 10) + object.height = imageheight + 10 + else + object.height = parent.tabheight + end + else + object.width = 10 + twidth + object.height = parent.tabheight + end + + local width = object:GetWidth() + local height = object:GetHeight() + + local back, fore, border + + if tabnumber ~= ptabnumber then + back = skin.controls.color_back1 + border = skin.controls.color_back2 + fore = skin.controls.color_fore0 + else + back = skin.controls.color_active + border = skin.controls.color_back2 + fore = skin.controls.color_back0 + end + + -- button body + love.graphics.setColor(back) + love.graphics.rectangle("fill", x, y, width, height) + -- button border + love.graphics.setColor(border) + skin.OutlinedRectangle(x, y, width, height) + + love.graphics.setFont(font) + if image then + -- button image + love.graphics.setColor(skin.controls.color_image) + love.graphics.draw(image, x + 5, y + height/2 - imageheight/2) + -- button text + love.graphics.setColor(fore) + skin.PrintText(text, x + imagewidth + 10, y + height/2 - theight/2) + else + -- button text + love.graphics.setColor(fore) + skin.PrintText(text, x + 5, y + height/2 - theight/2) + end + +end + +--[[--------------------------------------------------------- + - func: DrawMultiChoice(object) + - desc: draws the multi choice object +--]]--------------------------------------------------------- +function skin.multichoice(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local text = object:GetText() + local choice = object:GetChoice() + local image = skin.images["multichoice-arrow.png"] + local font = skin.controls.smallfont + local theight = font:getHeight("a") + local hover = object:GetHover() + --local down = object:GetDown() + + --image:setFilter("nearest", "nearest") + + local back, fore, border + if hover then + back = skin.controls.color_active + fore = skin.controls.color_back0 + border = skin.controls.color_fore1 + else + back = skin.controls.color_back2 + fore = skin.controls.color_fore0 + border = skin.controls.color_fore0 + end + + love.graphics.setColor(back) + love.graphics.rectangle("fill", x, y, width, height) + + love.graphics.setColor(fore) + love.graphics.setFont(font) + + if choice == "" then + skin.PrintText(text, x + 5, y + height/2 - theight/2) + else + skin.PrintText(choice, x + 5, y + height/2 - theight/2) + end + + love.graphics.draw(image, x + width - 20, y + 5) + + love.graphics.setColor(border) + skin.OutlinedRectangle(x, y, width, height) + + love.graphics.setColor(skin.controls.color_back0) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, height - 2) +end + +--[[--------------------------------------------------------- + - func: DrawMultiChoiceList(object) + - desc: draws the multi choice list object +--]]--------------------------------------------------------- +function skin.multichoicelist(object) + + local skin = object:GetSkin() + local x = object:GetX() + 2 + local y = object:GetY() + local width = object:GetWidth() - 4 + local height = object:GetHeight() + + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawOverMultiChoiceList(object) + - desc: draws over the multi choice list object +--]]--------------------------------------------------------- +function skin.multichoicelist_over(object) + + local skin = object:GetSkin() + local x = object:GetX() + 2 + local y = object:GetY() - 1 + local width = object:GetWidth() - 4 + local height = object:GetHeight() + 1 + + love.graphics.setColor(skin.controls.color_fore0) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawMultiChoiceRow(object) + - desc: draws the multi choice row object +--]]--------------------------------------------------------- +function skin.multichoicerow(object) + + local skin = object:GetSkin() + local x = object:GetX() + 2 + local y = object:GetY() + local width = object:GetWidth() - 4 + local height = object:GetHeight() + local text = object:GetText() + local font = skin.controls.smallfont + local back, fore + + love.graphics.setFont(font) + + if object.hover then + back = skin.controls.color_active + fore = skin.controls.color_back0 + else + back = skin.controls.color_back2 + fore = skin.controls.color_fore0 + end + + love.graphics.setColor(back) + love.graphics.rectangle("fill", x, y, width, height) + love.graphics.setColor(fore) + skin.PrintText(text, x + 5, y + 5) + +end + +--[[--------------------------------------------------------- + - func: DrawToolTip(object) + - desc: draws the tool tip object +--]]--------------------------------------------------------- +function skin.tooltip(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", x, y, width, height) + love.graphics.setColor(skin.controls.color_back2) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: DrawText(object) + - desc: draws the text object +--]]--------------------------------------------------------- +function skin.text(object) + local textdata = object.formattedtext + local x = object.x + local y = object.y + local shadow = object.shadow + local shadowxoffset = object.shadowxoffset + local shadowyoffset = object.shadowyoffset + local shadowcolor = object.shadowcolor + local inlist, list = object:IsInList() + local printfunc = function(text, x, y) + love.graphics.print(text, math.floor(x + 0.5), math.floor(y + 0.5)) + end + + 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)) + printfunc(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 + printfunc(text, x + textx, y + texty) + end + else + love.graphics.setFont(font) + if shadow then + love.graphics.setColor(unpack(shadowcolor)) + printfunc(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 + printfunc(text, x + textx, y + texty) + end + end +end + +--[[--------------------------------------------------------- + - func: DrawTextInput(object) + - desc: draws the text input object +--]]--------------------------------------------------------- +function skin.textinput(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local font = object:GetFont() + local focus = object:GetFocus() + local showindicator = object:GetIndicatorVisibility() + local alltextselected = object:IsAllTextSelected() + local textx = object:GetTextX() + local texty = object:GetTextY() + local text = object:GetText() + local multiline = object:GetMultiLine() + local lines = object:GetLines() + local placeholder = object:GetPlaceholderText() + local offsetx = object:GetOffsetX() + local offsety = object:GetOffsetY() + local indicatorx = object:GetIndicatorX() + local indicatory = object:GetIndicatorY() + local vbar = object:HasVerticalScrollBar() + local hbar = object:HasHorizontalScrollBar() + local linenumbers = object:GetLineNumbersEnabled() + local itemwidth = object:GetItemWidth() + local masked = object:GetMasked() + local theight = font:getHeight("a") + + love.graphics.setColor(skin.controls.color_back0) + love.graphics.rectangle("fill", x, y, width, height) + + if alltextselected then + local bary = 0 + if multiline then + for i=1, #lines do + local str = lines[i] + if masked then + str = loveframes.utf8.gsub(str, ".", "*") + end + local twidth = font:getWidth(str) + if twidth == 0 then + twidth = 5 + end + love.graphics.setColor(skin.controls.color_active) + love.graphics.rectangle("fill", textx, texty + bary, twidth, theight) + bary = bary + theight + end + else + local twidth = 0 + if masked then + local maskchar = object:GetMaskChar() + twidth = font:getWidth(loveframes.utf8.gsub(text, ".", maskchar)) + else + twidth = font:getWidth(text) + end + love.graphics.setColor(skin.controls.color_active) + love.graphics.rectangle("fill", textx, texty, twidth, theight) + end + end + + if showindicator and focus then + love.graphics.setColor(skin.controls.color_fore0) + love.graphics.rectangle("fill", indicatorx, indicatory, 1, theight) + end + + if not multiline then + object:SetTextOffsetY(height/2 - theight/2) + if offsetx ~= 0 then + object:SetTextOffsetX(0) + else + object:SetTextOffsetX(5) + end + else + if vbar then + if offsety ~= 0 then + if hbar then + object:SetTextOffsetY(5) + else + object:SetTextOffsetY(-5) + end + else + object:SetTextOffsetY(5) + end + else + object:SetTextOffsetY(5) + end + + if hbar then + if offsety ~= 0 then + if linenumbers then + local panel = object:GetLineNumbersPanel() + if vbar then + object:SetTextOffsetX(5) + else + object:SetTextOffsetX(-5) + end + else + if vbar then + object:SetTextOffsetX(5) + else + object:SetTextOffsetX(-5) + end + end + else + object:SetTextOffsetX(5) + end + else + object:SetTextOffsetX(5) + end + + end + + textx = object:GetTextX() + texty = object:GetTextY() + + love.graphics.setFont(font) + + if alltextselected then + love.graphics.setColor(skin.controls.color_back0) + elseif #lines == 1 and lines[1] == "" then + love.graphics.setColor(skin.controls.color_back2) + else + love.graphics.setColor(skin.controls.color_fore0) + end + + local str = "" + if multiline then + for i=1, #lines do + str = lines[i] + if masked then + local maskchar = object:GetMaskChar() + str = loveframes.utf8.gsub(str, ".", maskchar) + end + skin.PrintText(#str > 0 and str or (#lines == 1 and placeholder or ""), textx, texty + theight * i - theight) + end + else + str = lines[1] + if masked then + local maskchar = object:GetMaskChar() + str = loveframes.utf8.gsub(str, ".", maskchar) + end + skin.PrintText(#str > 0 and str or placeholder, textx, texty) + end + + --love.graphics.setColor(skin.controls.color_back3) + --skin.OutlinedRectangle(x + 1, y + 1, width - 2, height - 2) + +end + +--[[--------------------------------------------------------- + - func: DrawOverTextInput(object) + - desc: draws over the text input object +--]]--------------------------------------------------------- +function skin.textinput_over(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(skin.controls.color_back3) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawCheckBox(object) + - desc: draws the check box object +--]]--------------------------------------------------------- +function skin.checkbox(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetBoxWidth() + local height = object:GetBoxHeight() + local checked = object:GetChecked() + local hover = object:GetHover() + local buttonimage + if checked then + buttonimage = skin.images["check-on.png"] + else + buttonimage = skin.images["check-off.png"] + end + + if hover ~= checked then + love.graphics.setColor(skin.controls.color_active) + else + love.graphics.setColor(skin.controls.color_fore0) + end + local iwidth = buttonimage:getWidth() + local iheight = buttonimage:getHeight() + --buttonimage:setFilter("nearest", "nearest") + love.graphics.draw(buttonimage, x + (width - iwidth) / 2, y + (height - iheight) / 2) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawCheckBox(object) + - desc: draws the radio button object +--]]--------------------------------------------------------- +function skin.radiobutton(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetBoxWidth() + local height = object:GetBoxHeight() + local checked = object:GetChecked() + local hover = object:GetHover() + local buttonimage + if checked then + buttonimage = skin.images["radio-on.png"] + else + buttonimage = skin.images["radio-off.png"] + end + + if hover ~= checked then + love.graphics.setColor(skin.controls.color_active) + else + love.graphics.setColor(skin.controls.color_fore0) + end + local iwidth = buttonimage:getWidth() + local iheight = buttonimage:getHeight() + --buttonimage:setFilter("nearest", "nearest") + love.graphics.draw(buttonimage, x + (width - iwidth) / 2, y + (height - iheight) / 2) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawCollapsibleCategory(object) + - desc: draws the collapsible category object +--]]--------------------------------------------------------- +function skin.collapsiblecategory(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local text = object:GetText() + local open = object:GetOpen() + local font = skin.controls.smallfont + + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", x, y, width, height) + + love.graphics.setColor(skin.controls.color_active) + love.graphics.rectangle("fill", x, y, width, 25) + + love.graphics.setColor(skin.controls.color_back3) + skin.OutlinedRectangle(x, y, width, height) + + love.graphics.setColor(skin.controls.color_back0) + if open then + local icon = skin.images["collapse.png"] + --icon:setFilter("nearest", "nearest") + love.graphics.draw(icon, x + width - 21, y + 5) + love.graphics.setColor(skin.controls.color_back0) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, 24) + else + local icon = skin.images["expand.png"] + --icon:setFilter("nearest", "nearest") + love.graphics.draw(icon, x + width - 21, y + 5) + love.graphics.setColor(skin.controls.color_back0) + skin.OutlinedRectangle(x + 1, y + 1, width - 2, 23) + end + + love.graphics.setFont(font) + love.graphics.setColor(skin.controls.color_back0) + skin.PrintText(text, x + 5, y + 5) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawColumnList(object) + - desc: draws the column list object +--]]--------------------------------------------------------- +function skin.columnlist(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(skin.controls.color_active) + love.graphics.rectangle("fill", x, y, width, height) +end + +--[[--------------------------------------------------------- + - func: skin.DrawColumnListHeader(object) + - desc: draws the column list header object +--]]--------------------------------------------------------- +function skin.columnlistheader(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:GetHover() + local down = object.down + local font = skin.controls.smallfont + local theight = font:getHeight(object.name) + + local name = ParseHeaderText(object:GetName(), x, width, x + width/2) + local twidth = font:getWidth(name) + + local back, fore, border + + if down then + back = skin.controls.color_fore0 + fore = skin.controls.color_back0 + border = skin.controls.color_fore2 + elseif hover then + back = skin.controls.color_active + fore = skin.controls.color_back0 + border = skin.controls.color_fore1 + else + back = skin.controls.color_back2 + fore = skin.controls.color_fore0 + border = skin.controls.color_fore0 + end + + -- header body + love.graphics.setColor(back) + love.graphics.rectangle("fill", x, y, width, height) + -- header name + love.graphics.setFont(font) + love.graphics.setColor(fore) + skin.PrintText(name, x + width/2 - twidth/2, y + height/2 - theight/2) + -- header border + love.graphics.setColor(border) + skin.OutlinedRectangle(x, y, width+1, height) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawColumnListArea(object) + - desc: draws the column list area object +--]]--------------------------------------------------------- +function skin.columnlistarea(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(skin.controls.color_back0) + love.graphics.rectangle("fill", x, y, width, height) + + local cheight = 0 + local columns = object:GetParent():GetChildren() + if #columns > 0 then + cheight = columns[1]:GetHeight() + end + + -- header body + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", x, y, width, cheight) + + love.graphics.setColor(skin.controls.color_back2) + skin.OutlinedRectangle(x, y, width, cheight) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawOverColumnListArea(object) + - desc: draws over the column list area object +--]]--------------------------------------------------------- +function skin.columnlistarea_over(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(skin.controls.color_back3) + skin.OutlinedRectangle(x, y, width, height) +end + +--[[--------------------------------------------------------- + - func: skin.DrawColumnListRow(object) + - desc: draws the column list row object +--]]--------------------------------------------------------- +function skin.columnlistrow(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local colorindex = object:GetColorIndex() + local font = object:GetFont() + local columndata = object:GetColumnData() + local textx = object:GetTextX() + local texty = object:GetTextY() + local parent = object:GetParent() + local theight = font:getHeight("a") + local hover = object:GetHover() + local selected = object:GetSelected() + + object:SetTextPos(5, height/2 - theight/2) + + if selected then + love.graphics.setColor(skin.controls.color_back3) + elseif hover then + love.graphics.setColor(skin.controls.color_active) + elseif colorindex == 1 then + love.graphics.setColor(skin.controls.color_back1) + else + love.graphics.setColor(skin.controls.color_back2) + end + + love.graphics.rectangle("fill", x, y, width, height) + + love.graphics.setFont(font) + if selected then + love.graphics.setColor(skin.controls.color_fore3) + elseif hover then + love.graphics.setColor(skin.controls.color_back0) + else + love.graphics.setColor(skin.controls.color_fore0) + end + for k, v in ipairs(columndata) do + local rwidth = parent.parent:GetColumnWidth(k) + if rwidth then + local text = ParseRowText(v, x, rwidth, x, textx) + skin.PrintText(text, x + textx, y + texty) + x = x + parent.parent.children[k]:GetWidth() + else + break + end + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawModalBackground(object) + - desc: draws the modal background object +--]]--------------------------------------------------------- +function skin.modalbackground(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(skin.controls.color_back2) + love.graphics.rectangle("fill", x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawLineNumbersPanel(object) + - desc: draws the line numbers panel object +--]]--------------------------------------------------------- +function skin.linenumberspanel(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local offsety = object:GetOffsetY() + local parent = object:GetParent() + local lines = parent:GetLines() + local font = parent:GetFont() + local theight = font:getHeight("8") + + + object:SetWidth(10 + font:getWidth(#lines)) + love.graphics.setFont(font) + + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", x, y, width, height) + + love.graphics.setColor(skin.controls.color_back0) + skin.OutlinedRectangle(x, y, width, height, true, true, true, false) + + local startline = math.ceil(offsety / theight) + if startline < 1 then + startline = 1 + end + local endline = math.ceil(startline + (height / theight)) + 1 + if endline > #lines then + endline = #lines + end + + for i=startline, endline do + love.graphics.setColor(skin.controls.color_back3) + skin.PrintText(i, x + 5, (y + (theight * (i - 1))) - offsety) + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawNumberBox(object) + - desc: draws the numberbox object +--]]--------------------------------------------------------- +function skin.numberbox(object) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawGrid(object) + - desc: draws the grid object +--]]--------------------------------------------------------- +function skin.grid(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + --love.graphics.setColor(colors.hl4) + --love.graphics.rectangle("fill", x-1, y-1, width+2, height+2) + + local cx = x + local cy = y + local cw = object.cellwidth + (object.cellpadding * 2) + local ch = object.cellheight + (object.cellpadding * 2) + + for i=1, object.rows do + for n=1, object.columns do + local ovt = false + local ovl = false + if i > 1 then + ovt = true + end + if n > 1 then + ovl = true + end + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", cx, cy, cw, ch) + love.graphics.setColor(skin.controls.color_back3) + skin.OutlinedRectangle(cx, cy, cw, ch, ovt, false, ovl, false) + cx = cx + cw + end + cx = x + cy = cy + ch + end + +end + +--[[--------------------------------------------------------- + - func: skin.DrawForm(object) + - desc: draws the form object +--]]--------------------------------------------------------- +function skin.form(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local topmargin = object.topmargin + local name = object.name + local font = skin.controls.smallfont + local textcolor = skin.controls.form_text_color + local twidth = font:getWidth(name) + + love.graphics.setFont(font) + love.graphics.setColor(skin.controls.color_fore0) + skin.PrintText(name, x + 7, y) + + love.graphics.setColor(skin.controls.color_back3) + love.graphics.rectangle("fill", x, y + 7, 5, 1) + love.graphics.rectangle("fill", x + twidth + 9, y + 7, width - (twidth + 9), 1) + love.graphics.rectangle("fill", x, y + height, width, 1) + love.graphics.rectangle("fill", x, y + 7, 1, height - 7) + love.graphics.rectangle("fill", x + width - 1, y + 7, 1, height - 7) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawMenu(object) + - desc: draws the menu object +--]]--------------------------------------------------------- +function skin.menu(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", x, y, width, height) + + love.graphics.setColor(skin.controls.color_back2) + skin.OutlinedRectangle(x, y, width, height) + +end + +--[[--------------------------------------------------------- + - func: skin.DrawMenuOption(object) + - desc: draws the menuoption object +--]]--------------------------------------------------------- +function skin.menuoption(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + local hover = object:GetHover() + local text = object:GetText() + local icon = object:GetIcon() + local option_type = object.option_type + local font = skin.controls.smallfont + local twidth = font:getWidth(text) + + + if option_type == "divider" then + love.graphics.setColor(skin.controls.color_fore0) + love.graphics.rectangle("fill", x + 4, y + 2, width - 8, 1) + object.contentheight = 10 + else + love.graphics.setFont(font) + if hover then + love.graphics.setColor(skin.controls.color_active) + love.graphics.rectangle("fill", x + 2, y + 2, width - 4, height - 4) + love.graphics.setColor(skin.controls.color_back0) + skin.PrintText(text, x + 26, y + 5) + else + love.graphics.setColor(skin.controls.color_fore0) + skin.PrintText(text, x + 26, y + 5) + end + if icon then + love.graphics.setColor(skin.controls.color_image) + love.graphics.draw(icon, x + 5, y + 5) + end + object.contentwidth = twidth + 31 + object.contentheight = 25 + end + +end + +function skin.tree(object) + + local skin = object:GetSkin() + local x = object:GetX() + local y = object:GetY() + local width = object:GetWidth() + local height = object:GetHeight() + + love.graphics.setColor(skin.controls.color_back1) + love.graphics.rectangle("fill", x, y, width, height) + +end + +function skin.treenode(object) + + local icon = object.icon + local buttonimage = skin.images["tree-node-button-open.png"] + local width = 0 + local x = object.x + local leftpadding = 15 * object.level + + if object.level > 0 then + leftpadding = leftpadding + buttonimage:getWidth() + 5 + else + leftpadding = buttonimage:getWidth() + 5 + end + + local iconwidth = 24 + if icon then + iconwidth = icon:getWidth() + end + + local twidth = loveframes.basicfont:getWidth(object.text) + local theight = loveframes.basicfont:getHeight(object.text) + + if object.tree.selectednode == object then + love.graphics.setColor(skin.controls.color_active) + love.graphics.rectangle("fill", x + leftpadding + 2 + iconwidth, object.y + 2, twidth, theight) + end + + width = width + iconwidth + loveframes.basicfont:getWidth(object.text) + leftpadding + --love.graphics.setColor(skin.controls.color_image) + --love.graphics.draw(icon, x + leftpadding, object.y) + love.graphics.setFont(loveframes.basicfont) + love.graphics.setColor(skin.controls.color_fore0) + skin.PrintText(object.text, x + leftpadding + 2 + iconwidth, object.y + 2) + + object:SetWidth(width + 5) + +end + +function skin.treenodebutton(object) + + local leftpadding = 15 * object.parent.level + local image + + if object.parent.open then + image = skin.images["tree-node-button-close.png"] + else + image = skin.images["tree-node-button-open.png"] + end + + --image:setFilter("nearest", "nearest") + + love.graphics.setColor(skin.controls.color_image) + love.graphics.draw(image, object.x, object.y) + + object:SetPos(2 + leftpadding, 3) + object:SetSize(image:getWidth(), image:getHeight()) + +end + +-- register the main skin +loveframes.RegisterSkin(skin) + +-- alternate active colors +local subskin = {} +subskin.author = skin.author +subskin.version = skin.version +subskin.base = skin.name +subskin.directory = skin.dir --loveframes.config["DIRECTORY"] .. "/skins/" ..skin.name +subskin.controls = {} + +local colors = { + red = color"b36262", + orange = color"b38a62", + green = color"7db362", + cyan = color"62b3a5", + blue = color"6298b3", + magenta = color"8a62b3", + pink = color"cc70b5" +} + +for k, v in pairs(colors) do + subskin.name = skin.name .. " " .. k + subskin.controls.color_active = v + loveframes.RegisterSkin(subskin) +end + +-- Dark variant with alternate active colors +subskin.controls.color_back0 = color"101010" +subskin.controls.color_back1 = color"202020" +subskin.controls.color_back2 = color"373737" +subskin.controls.color_back3 = color"505050" +subskin.controls.color_fore0 = color"a0a0a0" +subskin.controls.color_fore1 = color"c0c0c0" +subskin.controls.color_fore2 = color"e0e0e0" +subskin.controls.color_fore3 = color"f0f0f0" + +for k, v in pairs(colors) do + subskin.name = "Dark " .. k + subskin.controls.color_active = v + loveframes.RegisterSkin(subskin) +end + +--return skin + +---------- module end ---------- +end diff --git a/loveframes/skins/Orange/images/arrow-down.png b/loveframes/skins/Orange/images/arrow-down.png new file mode 100644 index 0000000..37d8058 Binary files /dev/null and b/loveframes/skins/Orange/images/arrow-down.png differ diff --git a/loveframes/skins/Orange/images/arrow-left.png b/loveframes/skins/Orange/images/arrow-left.png new file mode 100644 index 0000000..c5045dd Binary files /dev/null and b/loveframes/skins/Orange/images/arrow-left.png differ diff --git a/loveframes/skins/Orange/images/arrow-right.png b/loveframes/skins/Orange/images/arrow-right.png new file mode 100644 index 0000000..89b4818 Binary files /dev/null and b/loveframes/skins/Orange/images/arrow-right.png differ diff --git a/loveframes/skins/Orange/images/arrow-up.png b/loveframes/skins/Orange/images/arrow-up.png new file mode 100644 index 0000000..475d5db Binary files /dev/null and b/loveframes/skins/Orange/images/arrow-up.png differ diff --git a/loveframes/skins/Orange/images/close.png b/loveframes/skins/Orange/images/close.png new file mode 100644 index 0000000..0825add Binary files /dev/null and b/loveframes/skins/Orange/images/close.png differ diff --git a/loveframes/skins/Orange/images/collapse.png b/loveframes/skins/Orange/images/collapse.png new file mode 100644 index 0000000..ee7524b Binary files /dev/null and b/loveframes/skins/Orange/images/collapse.png differ diff --git a/loveframes/skins/Orange/images/expand.png b/loveframes/skins/Orange/images/expand.png new file mode 100644 index 0000000..753625b Binary files /dev/null and b/loveframes/skins/Orange/images/expand.png differ diff --git a/loveframes/skins/Orange/images/gradients/button-down.png b/loveframes/skins/Orange/images/gradients/button-down.png new file mode 100644 index 0000000..c7dd887 Binary files /dev/null and b/loveframes/skins/Orange/images/gradients/button-down.png differ diff --git a/loveframes/skins/Orange/images/gradients/button-hover.png b/loveframes/skins/Orange/images/gradients/button-hover.png new file mode 100644 index 0000000..f621691 Binary files /dev/null and b/loveframes/skins/Orange/images/gradients/button-hover.png differ diff --git a/loveframes/skins/Orange/images/gradients/button-nohover.png b/loveframes/skins/Orange/images/gradients/button-nohover.png new file mode 100644 index 0000000..39ff24a Binary files /dev/null and b/loveframes/skins/Orange/images/gradients/button-nohover.png differ diff --git a/loveframes/skins/Orange/images/gradients/button-unclickable.png b/loveframes/skins/Orange/images/gradients/button-unclickable.png new file mode 100644 index 0000000..ef8bf7d Binary files /dev/null and b/loveframes/skins/Orange/images/gradients/button-unclickable.png differ diff --git a/loveframes/skins/Orange/images/gradients/frame-body.png b/loveframes/skins/Orange/images/gradients/frame-body.png new file mode 100644 index 0000000..bed498d Binary files /dev/null and b/loveframes/skins/Orange/images/gradients/frame-body.png differ diff --git a/loveframes/skins/Orange/images/gradients/frame-topbar.png b/loveframes/skins/Orange/images/gradients/frame-topbar.png new file mode 100644 index 0000000..3667096 Binary files /dev/null and b/loveframes/skins/Orange/images/gradients/frame-topbar.png differ diff --git a/loveframes/skins/Orange/images/gradients/progressbar.png b/loveframes/skins/Orange/images/gradients/progressbar.png new file mode 100644 index 0000000..7cdfb4b Binary files /dev/null and b/loveframes/skins/Orange/images/gradients/progressbar.png differ diff --git a/loveframes/skins/Orange/images/multichoice-arrow.png b/loveframes/skins/Orange/images/multichoice-arrow.png new file mode 100644 index 0000000..165121c Binary files /dev/null and b/loveframes/skins/Orange/images/multichoice-arrow.png differ diff --git a/loveframes/skins/Orange/skin.lua b/loveframes/skins/Orange/skin.lua new file mode 100644 index 0000000..a1b11f8 --- /dev/null +++ b/loveframes/skins/Orange/skin.lua @@ -0,0 +1,48 @@ +--[[------------------------------------------------ + -- Love Frames - A GUI library for LOVE -- + -- Copyright (c) 2012-2014 Kenny Shields -- +--]]------------------------------------------------ + +return function(loveframes) +---------- module start ---------- + +-- skin table +local skin = {} + +-- skin info (you always need this in a skin) +skin.name = "Orange" +skin.author = "Nikolai Resokav" +skin.version = "1.0" +skin.base = "Blue" + +-- controls +skin.controls = {} + +-- multichoicerow +skin.controls.multichoicerow_body_hover_color = {1, 0.6, 0, 1} + +-- slider +skin.controls.slider_bar_outline_color = {0.86, 0.86, 0.86, 1} + +-- checkbox +skin.controls.checkbox_check_color = {1, 0.6, 0, 1} + +-- radiobutton +skin.controls.radiobutton_body_color = {1, 1, 1, 1} +skin.controls.radiobutton_check_color = {1, 0.6, 0, 1} +skin.controls.radiobutton_inner_border_color = {0.8, 0.48, 0, 1} +skin.controls.radiobutton_text_font = smallfont + + +-- columnlistrow +skin.controls.columnlistrow_body_selected_color = {1, 0.6, 0, 1} +skin.controls.columnlistrow_body_hover_color = {1, 0.68, 0.2, 1} + +-- menuoption +skin.controls.menuoption_body_hover_color = {1, 0.6, 0, 1} + +-- register the skin +loveframes.RegisterSkin(skin) + +---------- module end ---------- +end diff --git a/loveframes/templates/base.lua b/loveframes/templates/base.lua new file mode 100644 index 0000000..27cab7a --- /dev/null +++ b/loveframes/templates/base.lua @@ -0,0 +1 @@ +--[[------------------------------------------------ -- Love Frames - A GUI library for LOVE -- -- Copyright (c) 2012-2014 Kenny Shields -- --]]------------------------------------------------ --[[------------------------------------------------ -- note: This is the base template for all Love Frames objects. You should not edit or delete this template unless you know what you are doing. --]]------------------------------------------------ -- template table local template = {} -- template name template.name = "Base" -- template properties template.properties = {} template.properties["*"] = { state = "none", x = 0, y = 0, width = 5, height = 5, staticx = 0, staticy = 0, draworder = 0, drawfunc = nil, drawoverfunc = nil, collide = true, internal = false, visible = true, hover = false, alwaysupdate = false, retainsize = false, calledmousefunc = false, skin = nil, clickbounds = nil, Draw = nil, DrawOver = nil, Update = nil, OnMouseEnter = nil, OnMouseExit = nil } return template \ No newline at end of file diff --git a/loveframes/third-party/middleclass.lua b/loveframes/third-party/middleclass.lua new file mode 100644 index 0000000..7e36bcd --- /dev/null +++ b/loveframes/third-party/middleclass.lua @@ -0,0 +1,183 @@ +local middleclass = { + _VERSION = 'middleclass v4.1.1', + _DESCRIPTION = 'Object Orientation for Lua', + _URL = 'https://github.com/kikito/middleclass', + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2011 Enrique García Cota + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ]] +} + +local function _createIndexWrapper(aClass, f) + if f == nil then + return aClass.__instanceDict + else + return function(self, name) + local value = aClass.__instanceDict[name] + + if value ~= nil then + return value + elseif type(f) == "function" then + return (f(self, name)) + else + return f[name] + end + end + end +end + +local function _propagateInstanceMethod(aClass, name, f) + f = name == "__index" and _createIndexWrapper(aClass, f) or f + aClass.__instanceDict[name] = f + + for subclass in pairs(aClass.subclasses) do + if rawget(subclass.__declaredMethods, name) == nil then + _propagateInstanceMethod(subclass, name, f) + end + end +end + +local function _declareInstanceMethod(aClass, name, f) + aClass.__declaredMethods[name] = f + + if f == nil and aClass.super then + f = aClass.super.__instanceDict[name] + end + + _propagateInstanceMethod(aClass, name, f) +end + +local function _tostring(self) return "class " .. self.name end +local function _call(self, ...) return self:new(...) end + +local function _createClass(name, super) + local dict = {} + dict.__index = dict + + local aClass = { name = name, super = super, static = {}, + __instanceDict = dict, __declaredMethods = {}, + subclasses = setmetatable({}, {__mode='k'}) } + + if super then + setmetatable(aClass.static, { + __index = function(_,k) + local result = rawget(dict,k) + if result == nil then + return super.static[k] + end + return result + end + }) + else + setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) end }) + end + + setmetatable(aClass, { __index = aClass.static, __tostring = _tostring, + __call = _call, __newindex = _declareInstanceMethod }) + + return aClass +end + +local function _includeMixin(aClass, mixin) + assert(type(mixin) == 'table', "mixin must be a table") + + for name,method in pairs(mixin) do + if name ~= "included" and name ~= "static" then aClass[name] = method end + end + + for name,method in pairs(mixin.static or {}) do + aClass.static[name] = method + end + + if type(mixin.included)=="function" then mixin:included(aClass) end + return aClass +end + +local DefaultMixin = { + __tostring = function(self) return "instance of " .. tostring(self.class) end, + + initialize = function(self, ...) end, + + isInstanceOf = function(self, aClass) + return type(aClass) == 'table' + and type(self) == 'table' + and (self.class == aClass + or type(self.class) == 'table' + and type(self.class.isSubclassOf) == 'function' + and self.class:isSubclassOf(aClass)) + end, + + static = { + allocate = function(self) + assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'") + return setmetatable({ class = self }, self.__instanceDict) + end, + + new = function(self, ...) + assert(type(self) == 'table', "Make sure that you are using 'Class:new' instead of 'Class.new'") + local instance = self:allocate() + instance:initialize(...) + return instance + end, + + subclass = function(self, name) + assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'") + assert(type(name) == "string", "You must provide a name(string) for your class") + + local subclass = _createClass(name, self) + + for methodName, f in pairs(self.__instanceDict) do + _propagateInstanceMethod(subclass, methodName, f) + end + subclass.initialize = function(instance, ...) return self.initialize(instance, ...) end + + self.subclasses[subclass] = true + self:subclassed(subclass) + + return subclass + end, + + subclassed = function(self, other) end, + + isSubclassOf = function(self, other) + return type(other) == 'table' and + type(self.super) == 'table' and + ( self.super == other or self.super:isSubclassOf(other) ) + end, + + include = function(self, ...) + assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'") + for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end + return self + end + } +} + +function middleclass.class(name, super) + assert(type(name) == 'string', "A name (string) is needed for the new class") + return super and super:subclass(name) or _includeMixin(_createClass(name), DefaultMixin) +end + +setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end }) + +return middleclass diff --git a/loveframes/third-party/utf8.lua b/loveframes/third-party/utf8.lua new file mode 100644 index 0000000..7f13914 --- /dev/null +++ b/loveframes/third-party/utf8.lua @@ -0,0 +1,1045 @@ +-- $Id: utf8.lua 179 2009-04-03 18:10:03Z pasta $ +-- +-- Provides UTF-8 aware string functions implemented in pure lua: +-- * utf8len(s) +-- * utf8sub(s, i, j) +-- * utf8reverse(s) +-- * utf8char(unicode) +-- * utf8unicode(s, i, j) +-- * utf8gensub(s, sub_len) +-- * utf8find(str, regex, init, plain) +-- * utf8match(str, regex, init) +-- * utf8gmatch(str, regex, all) +-- * utf8gsub(str, regex, repl, limit) +-- +-- If utf8data.lua (containing the lower<->upper case mappings) is loaded, these +-- additional functions are available: +-- * utf8upper(s) +-- * utf8lower(s) +-- +-- All functions behave as their non UTF-8 aware counterparts with the exception +-- that UTF-8 characters are used instead of bytes for all units. + +--[[ +Copyright (c) 2006-2007, Kyle Smith +All rights reserved. + +Contributors: + Alimov Stepan + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--]] + +-- ABNF from RFC 3629 +-- +-- UTF8-octets = *( UTF8-char ) +-- UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 +-- UTF8-1 = %x00-7F +-- UTF8-2 = %xC2-DF UTF8-tail +-- UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / +-- %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) +-- UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / +-- %xF4 %x80-8F 2( UTF8-tail ) +-- UTF8-tail = %x80-BF +-- + +local byte = string.byte +local char = string.char +local dump = string.dump +local find = string.find +local format = string.format +local len = string.len +local lower = string.lower +local rep = string.rep +local sub = string.sub +local upper = string.upper + +-- returns the number of bytes used by the UTF-8 character at byte i in s +-- also doubles as a UTF-8 character validator +local function utf8charbytes (s, i) + -- argument defaults + i = i or 1 + + -- argument checking + if type(s) ~= "string" then + error("bad argument #1 to 'utf8charbytes' (string expected, got ".. type(s).. ")") + end + if type(i) ~= "number" then + error("bad argument #2 to 'utf8charbytes' (number expected, got ".. type(i).. ")") + end + + local c = byte(s, i) + + -- determine bytes needed for character, based on RFC 3629 + -- validate byte 1 + if c > 0 and c <= 127 then + -- UTF8-1 + return 1 + + elseif c >= 194 and c <= 223 then + -- UTF8-2 + local c2 = byte(s, i + 1) + + if not c2 then + error("UTF-8 string terminated early") + end + + -- validate byte 2 + if c2 < 128 or c2 > 191 then + error("Invalid UTF-8 character") + end + + return 2 + + elseif c >= 224 and c <= 239 then + -- UTF8-3 + local c2 = byte(s, i + 1) + local c3 = byte(s, i + 2) + + if not c2 or not c3 then + error("UTF-8 string terminated early") + end + + -- validate byte 2 + if c == 224 and (c2 < 160 or c2 > 191) then + error("Invalid UTF-8 character") + elseif c == 237 and (c2 < 128 or c2 > 159) then + error("Invalid UTF-8 character") + elseif c2 < 128 or c2 > 191 then + error("Invalid UTF-8 character") + end + + -- validate byte 3 + if c3 < 128 or c3 > 191 then + error("Invalid UTF-8 character") + end + + return 3 + + elseif c >= 240 and c <= 244 then + -- UTF8-4 + local c2 = byte(s, i + 1) + local c3 = byte(s, i + 2) + local c4 = byte(s, i + 3) + + if not c2 or not c3 or not c4 then + error("UTF-8 string terminated early") + end + + -- validate byte 2 + if c == 240 and (c2 < 144 or c2 > 191) then + error("Invalid UTF-8 character") + elseif c == 244 and (c2 < 128 or c2 > 143) then + error("Invalid UTF-8 character") + elseif c2 < 128 or c2 > 191 then + error("Invalid UTF-8 character") + end + + -- validate byte 3 + if c3 < 128 or c3 > 191 then + error("Invalid UTF-8 character") + end + + -- validate byte 4 + if c4 < 128 or c4 > 191 then + error("Invalid UTF-8 character") + end + + return 4 + + else + error("Invalid UTF-8 character") + end +end + +-- returns the number of characters in a UTF-8 string +local function utf8len (s) + -- argument checking + if type(s) ~= "string" then + for k,v in pairs(s) do print('"',tostring(k),'"',tostring(v),'"') end + error("bad argument #1 to 'utf8len' (string expected, got ".. type(s).. ")") + end + + local pos = 1 + local bytes = len(s) + local length = 0 + + while pos <= bytes do + length = length + 1 + pos = pos + utf8charbytes(s, pos) + end + + return length +end + +-- functions identically to string.sub except that i and j are UTF-8 characters +-- instead of bytes +local function utf8sub (s, i, j) + -- argument defaults + j = j or -1 + + local pos = 1 + local bytes = len(s) + local length = 0 + + -- only set l if i or j is negative + local l = (i >= 0 and j >= 0) or utf8len(s) + local startChar = (i >= 0) and i or l + i + 1 + local endChar = (j >= 0) and j or l + j + 1 + + -- can't have start before end! + if startChar > endChar then + return "" + end + + -- byte offsets to pass to string.sub + local startByte,endByte = 1,bytes + + while pos <= bytes do + length = length + 1 + + if length == startChar then + startByte = pos + end + + pos = pos + utf8charbytes(s, pos) + + if length == endChar then + endByte = pos - 1 + break + end + end + + if startChar > length then startByte = bytes+1 end + if endChar < 1 then endByte = 0 end + + return sub(s, startByte, endByte) +end + +--[[ +-- replace UTF-8 characters based on a mapping table +local function utf8replace (s, mapping) + -- argument checking + if type(s) ~= "string" then + error("bad argument #1 to 'utf8replace' (string expected, got ".. type(s).. ")") + end + if type(mapping) ~= "table" then + error("bad argument #2 to 'utf8replace' (table expected, got ".. type(mapping).. ")") + end + + local pos = 1 + local bytes = len(s) + local charbytes + local newstr = "" + + while pos <= bytes do + charbytes = utf8charbytes(s, pos) + local c = sub(s, pos, pos + charbytes - 1) + + newstr = newstr .. (mapping[c] or c) + + pos = pos + charbytes + end + + return newstr +end + + +-- identical to string.upper except it knows about unicode simple case conversions +local function utf8upper (s) + return utf8replace(s, utf8_lc_uc) +end + +-- identical to string.lower except it knows about unicode simple case conversions +local function utf8lower (s) + return utf8replace(s, utf8_uc_lc) +end +]] + +-- identical to string.reverse except that it supports UTF-8 +local function utf8reverse (s) + -- argument checking + if type(s) ~= "string" then + error("bad argument #1 to 'utf8reverse' (string expected, got ".. type(s).. ")") + end + + local bytes = len(s) + local pos = bytes + local charbytes + local newstr = "" + + while pos > 0 do + local c = byte(s, pos) + while c >= 128 and c <= 191 do + pos = pos - 1 + c = byte(s, pos) + end + + charbytes = utf8charbytes(s, pos) + + newstr = newstr .. sub(s, pos, pos + charbytes - 1) + + pos = pos - 1 + end + + return newstr +end + +-- http://en.wikipedia.org/wiki/Utf8 +-- http://developer.coronalabs.com/code/utf-8-conversion-utility +local function utf8char(unicode) + if unicode <= 0x7F then return char(unicode) end + + if (unicode <= 0x7FF) then + local Byte0 = 0xC0 + math.floor(unicode / 0x40); + local Byte1 = 0x80 + (unicode % 0x40); + return char(Byte0, Byte1); + end; + + if (unicode <= 0xFFFF) then + local Byte0 = 0xE0 + math.floor(unicode / 0x1000); + local Byte1 = 0x80 + (math.floor(unicode / 0x40) % 0x40); + local Byte2 = 0x80 + (unicode % 0x40); + return char(Byte0, Byte1, Byte2); + end; + + if (unicode <= 0x10FFFF) then + local code = unicode + local Byte3= 0x80 + (code % 0x40); + code = math.floor(code / 0x40) + local Byte2= 0x80 + (code % 0x40); + code = math.floor(code / 0x40) + local Byte1= 0x80 + (code % 0x40); + code = math.floor(code / 0x40) + local Byte0= 0xF0 + code; + + return char(Byte0, Byte1, Byte2, Byte3); + end; + + error 'Unicode cannot be greater than U+10FFFF!' +end + +local shift_6 = 2^6 +local shift_12 = 2^12 +local shift_18 = 2^18 + +local utf8unicode +utf8unicode = function(str, i, j, byte_pos) + i = i or 1 + j = j or i + + if i > j then return end + + local ch,bytes + + if byte_pos then + bytes = utf8charbytes(str,byte_pos) + ch = sub(str,byte_pos,byte_pos-1+bytes) + else + ch,byte_pos = utf8sub(str,i,i), 0 + bytes = #ch + end + + local unicode + + if bytes == 1 then unicode = byte(ch) end + if bytes == 2 then + local byte0,byte1 = byte(ch,1,2) + local code0,code1 = byte0-0xC0,byte1-0x80 + unicode = code0*shift_6 + code1 + end + if bytes == 3 then + local byte0,byte1,byte2 = byte(ch,1,3) + local code0,code1,code2 = byte0-0xE0,byte1-0x80,byte2-0x80 + unicode = code0*shift_12 + code1*shift_6 + code2 + end + if bytes == 4 then + local byte0,byte1,byte2,byte3 = byte(ch,1,4) + local code0,code1,code2,code3 = byte0-0xF0,byte1-0x80,byte2-0x80,byte3-0x80 + unicode = code0*shift_18 + code1*shift_12 + code2*shift_6 + code3 + end + + return unicode,utf8unicode(str, i+1, j, byte_pos+bytes) +end + +-- Returns an iterator which returns the next substring and its byte interval +local function utf8gensub(str, sub_len) + sub_len = sub_len or 1 + local byte_pos = 1 + local length = #str + return function(skip) + if skip then byte_pos = byte_pos + skip end + local char_count = 0 + local start = byte_pos + repeat + if byte_pos > length then return end + char_count = char_count + 1 + local bytes = utf8charbytes(str,byte_pos) + byte_pos = byte_pos+bytes + + until char_count == sub_len + + local last = byte_pos-1 + local slice = sub(str,start,last) + return slice, start, last + end +end + +local function binsearch(sortedTable, item, comp) + local head, tail = 1, #sortedTable + local mid = math.floor((head + tail)/2) + if not comp then + while (tail - head) > 1 do + if sortedTable[tonumber(mid)] > item then + tail = mid + else + head = mid + end + mid = math.floor((head + tail)/2) + end + end + if sortedTable[tonumber(head)] == item then + return true, tonumber(head) + elseif sortedTable[tonumber(tail)] == item then + return true, tonumber(tail) + else + return false + end +end +local function classMatchGenerator(class, plain) + local codes = {} + local ranges = {} + local ignore = false + local range = false + local firstletter = true + local unmatch = false + + local it = utf8gensub(class) + + local skip + for c, _, be in it do + skip = be + if not ignore and not plain then + if c == "%" then + ignore = true + elseif c == "-" then + table.insert(codes, utf8unicode(c)) + range = true + elseif c == "^" then + if not firstletter then + error('!!!') + else + unmatch = true + end + elseif c == ']' then + break + else + if not range then + table.insert(codes, utf8unicode(c)) + else + table.remove(codes) -- removing '-' + table.insert(ranges, {table.remove(codes), utf8unicode(c)}) + range = false + end + end + elseif ignore and not plain then + if c == 'a' then -- %a: represents all letters. (ONLY ASCII) + table.insert(ranges, {65, 90}) -- A - Z + table.insert(ranges, {97, 122}) -- a - z + elseif c == 'c' then -- %c: represents all control characters. + table.insert(ranges, {0, 31}) + table.insert(codes, 127) + elseif c == 'd' then -- %d: represents all digits. + table.insert(ranges, {48, 57}) -- 0 - 9 + elseif c == 'g' then -- %g: represents all printable characters except space. + table.insert(ranges, {1, 8}) + table.insert(ranges, {14, 31}) + table.insert(ranges, {33, 132}) + table.insert(ranges, {134, 159}) + table.insert(ranges, {161, 5759}) + table.insert(ranges, {5761, 8191}) + table.insert(ranges, {8203, 8231}) + table.insert(ranges, {8234, 8238}) + table.insert(ranges, {8240, 8286}) + table.insert(ranges, {8288, 12287}) + elseif c == 'l' then -- %l: represents all lowercase letters. (ONLY ASCII) + table.insert(ranges, {97, 122}) -- a - z + elseif c == 'p' then -- %p: represents all punctuation characters. (ONLY ASCII) + table.insert(ranges, {33, 47}) + table.insert(ranges, {58, 64}) + table.insert(ranges, {91, 96}) + table.insert(ranges, {123, 126}) + elseif c == 's' then -- %s: represents all space characters. + table.insert(ranges, {9, 13}) + table.insert(codes, 32) + table.insert(codes, 133) + table.insert(codes, 160) + table.insert(codes, 5760) + table.insert(ranges, {8192, 8202}) + table.insert(codes, 8232) + table.insert(codes, 8233) + table.insert(codes, 8239) + table.insert(codes, 8287) + table.insert(codes, 12288) + elseif c == 'u' then -- %u: represents all uppercase letters. (ONLY ASCII) + table.insert(ranges, {65, 90}) -- A - Z + elseif c == 'w' then -- %w: represents all alphanumeric characters. (ONLY ASCII) + table.insert(ranges, {48, 57}) -- 0 - 9 + table.insert(ranges, {65, 90}) -- A - Z + table.insert(ranges, {97, 122}) -- a - z + elseif c == 'x' then -- %x: represents all hexadecimal digits. + table.insert(ranges, {48, 57}) -- 0 - 9 + table.insert(ranges, {65, 70}) -- A - F + table.insert(ranges, {97, 102}) -- a - f + else + if not range then + table.insert(codes, utf8unicode(c)) + else + table.remove(codes) -- removing '-' + table.insert(ranges, {table.remove(codes), utf8unicode(c)}) + range = false + end + end + ignore = false + else + if not range then + table.insert(codes, utf8unicode(c)) + else + table.remove(codes) -- removing '-' + table.insert(ranges, {table.remove(codes), utf8unicode(c)}) + range = false + end + ignore = false + end + + firstletter = false + end + + table.sort(codes) + + local function inRanges(charCode) + for _,r in ipairs(ranges) do + if r[1] <= charCode and charCode <= r[2] then + return true + end + end + return false + end + if not unmatch then + return function(charCode) + return binsearch(codes, charCode) or inRanges(charCode) + end, skip + else + return function(charCode) + return charCode ~= -1 and not (binsearch(codes, charCode) or inRanges(charCode)) + end, skip + end +end + +--[[ +-- utf8sub with extra argument, and extra result value +local function utf8subWithBytes (s, i, j, sb) + -- argument defaults + j = j or -1 + + local pos = sb or 1 + local bytes = len(s) + local length = 0 + + -- only set l if i or j is negative + local l = (i >= 0 and j >= 0) or utf8len(s) + local startChar = (i >= 0) and i or l + i + 1 + local endChar = (j >= 0) and j or l + j + 1 + + -- can't have start before end! + if startChar > endChar then + return "" + end + + -- byte offsets to pass to string.sub + local startByte,endByte = 1,bytes + + while pos <= bytes do + length = length + 1 + + if length == startChar then + startByte = pos + end + + pos = pos + utf8charbytes(s, pos) + + if length == endChar then + endByte = pos - 1 + break + end + end + + if startChar > length then startByte = bytes+1 end + if endChar < 1 then endByte = 0 end + + return sub(s, startByte, endByte), endByte + 1 +end +]] + +local cache = setmetatable({},{ + __mode = 'kv' +}) +local cachePlain = setmetatable({},{ + __mode = 'kv' +}) +local function matcherGenerator(regex, plain) + local matcher = { + functions = {}, + captures = {} + } + if not plain then + cache[regex] = matcher + else + cachePlain[regex] = matcher + end + local function simple(func) + return function(cC) + if func(cC) then + matcher:nextFunc() + matcher:nextStr() + else + matcher:reset() + end + end + end + local function star(func) + return function(cC) + if func(cC) then + matcher:fullResetOnNextFunc() + matcher:nextStr() + else + matcher:nextFunc() + end + end + end + local function minus(func) + return function(cC) + if func(cC) then + matcher:fullResetOnNextStr() + end + matcher:nextFunc() + end + end + local function question(func) + return function(cC) + if func(cC) then + matcher:fullResetOnNextFunc() + matcher:nextStr() + end + matcher:nextFunc() + end + end + + local function capture(id) + return function(_) + local l = matcher.captures[id][2] - matcher.captures[id][1] + local captured = utf8sub(matcher.string, matcher.captures[id][1], matcher.captures[id][2]) + local check = utf8sub(matcher.string, matcher.str, matcher.str + l) + if captured == check then + for _ = 0, l do + matcher:nextStr() + end + matcher:nextFunc() + else + matcher:reset() + end + end + end + local function captureStart(id) + return function(_) + matcher.captures[id][1] = matcher.str + matcher:nextFunc() + end + end + local function captureStop(id) + return function(_) + matcher.captures[id][2] = matcher.str - 1 + matcher:nextFunc() + end + end + + local function balancer(str) + local sum = 0 + local bc, ec = utf8sub(str, 1, 1), utf8sub(str, 2, 2) + local skip = len(bc) + len(ec) + bc, ec = utf8unicode(bc), utf8unicode(ec) + return function(cC) + if cC == ec and sum > 0 then + sum = sum - 1 + if sum == 0 then + matcher:nextFunc() + end + matcher:nextStr() + elseif cC == bc then + sum = sum + 1 + matcher:nextStr() + else + if sum == 0 or cC == -1 then + sum = 0 + matcher:reset() + else + matcher:nextStr() + end + end + end, skip + end + + matcher.functions[1] = function(_) + matcher:fullResetOnNextStr() + matcher.seqStart = matcher.str + matcher:nextFunc() + if (matcher.str > matcher.startStr and matcher.fromStart) or matcher.str >= matcher.stringLen then + matcher.stop = true + matcher.seqStart = nil + end + end + + local lastFunc + local ignore = false + local skip = nil + local it = (function() + local gen = utf8gensub(regex) + return function() + return gen(skip) + end + end)() + local cs = {} + for c, bs, be in it do + skip = nil + if plain then + table.insert(matcher.functions, simple(classMatchGenerator(c, plain))) + else + if ignore then + if find('123456789', c, 1, true) then + if lastFunc then + table.insert(matcher.functions, simple(lastFunc)) + lastFunc = nil + end + table.insert(matcher.functions, capture(tonumber(c))) + elseif c == 'b' then + if lastFunc then + table.insert(matcher.functions, simple(lastFunc)) + lastFunc = nil + end + local b + b, skip = balancer(sub(regex, be + 1, be + 9)) + table.insert(matcher.functions, b) + else + lastFunc = classMatchGenerator('%' .. c) + end + ignore = false + else + if c == '*' then + if lastFunc then + table.insert(matcher.functions, star(lastFunc)) + lastFunc = nil + else + error('invalid regex after ' .. sub(regex, 1, bs)) + end + elseif c == '+' then + if lastFunc then + table.insert(matcher.functions, simple(lastFunc)) + table.insert(matcher.functions, star(lastFunc)) + lastFunc = nil + else + error('invalid regex after ' .. sub(regex, 1, bs)) + end + elseif c == '-' then + if lastFunc then + table.insert(matcher.functions, minus(lastFunc)) + lastFunc = nil + else + error('invalid regex after ' .. sub(regex, 1, bs)) + end + elseif c == '?' then + if lastFunc then + table.insert(matcher.functions, question(lastFunc)) + lastFunc = nil + else + error('invalid regex after ' .. sub(regex, 1, bs)) + end + elseif c == '^' then + if bs == 1 then + matcher.fromStart = true + else + error('invalid regex after ' .. sub(regex, 1, bs)) + end + elseif c == '$' then + if be == len(regex) then + matcher.toEnd = true + else + error('invalid regex after ' .. sub(regex, 1, bs)) + end + elseif c == '[' then + if lastFunc then + table.insert(matcher.functions, simple(lastFunc)) + end + lastFunc, skip = classMatchGenerator(sub(regex, be + 1)) + elseif c == '(' then + if lastFunc then + table.insert(matcher.functions, simple(lastFunc)) + lastFunc = nil + end + table.insert(matcher.captures, {}) + table.insert(cs, #matcher.captures) + table.insert(matcher.functions, captureStart(cs[#cs])) + if sub(regex, be + 1, be + 1) == ')' then matcher.captures[#matcher.captures].empty = true end + elseif c == ')' then + if lastFunc then + table.insert(matcher.functions, simple(lastFunc)) + lastFunc = nil + end + local cap = table.remove(cs) + if not cap then + error('invalid capture: "(" missing') + end + table.insert(matcher.functions, captureStop(cap)) + elseif c == '.' then + if lastFunc then + table.insert(matcher.functions, simple(lastFunc)) + end + lastFunc = function(cC) return cC ~= -1 end + elseif c == '%' then + ignore = true + else + if lastFunc then + table.insert(matcher.functions, simple(lastFunc)) + end + lastFunc = classMatchGenerator(c) + end + end + end + end + if #cs > 0 then + error('invalid capture: ")" missing') + end + if lastFunc then + table.insert(matcher.functions, simple(lastFunc)) + end + + table.insert(matcher.functions, function() + if matcher.toEnd and matcher.str ~= matcher.stringLen then + matcher:reset() + else + matcher.stop = true + end + end) + + matcher.nextFunc = function(self) + self.func = self.func + 1 + end + matcher.nextStr = function(self) + self.str = self.str + 1 + end + matcher.strReset = function(self) + local oldReset = self.reset + local str = self.str + self.reset = function(s) + s.str = str + s.reset = oldReset + end + end + matcher.fullResetOnNextFunc = function(self) + local oldReset = self.reset + local func = self.func +1 + local str = self.str + self.reset = function(s) + s.func = func + s.str = str + s.reset = oldReset + end + end + matcher.fullResetOnNextStr = function(self) + local oldReset = self.reset + local str = self.str + 1 + local func = self.func + self.reset = function(s) + s.func = func + s.str = str + s.reset = oldReset + end + end + + matcher.process = function(self, str, start) + + self.func = 1 + start = start or 1 + self.startStr = (start >= 0) and start or utf8len(str) + start + 1 + self.seqStart = self.startStr + self.str = self.startStr + self.stringLen = utf8len(str) + 1 + self.string = str + self.stop = false + + self.reset = function(s) + s.func = 1 + end + + -- local lastPos = self.str + -- local lastByte + local ch + while not self.stop do + if self.str < self.stringLen then + --[[ if lastPos < self.str then + print('last byte', lastByte) + ch, lastByte = utf8subWithBytes(str, 1, self.str - lastPos - 1, lastByte) + ch, lastByte = utf8subWithBytes(str, 1, 1, lastByte) + lastByte = lastByte - 1 + else + ch, lastByte = utf8subWithBytes(str, self.str, self.str) + end + lastPos = self.str ]] + ch = utf8sub(str, self.str,self.str) + --print('char', ch, utf8unicode(ch)) + self.functions[self.func](utf8unicode(ch)) + else + self.functions[self.func](-1) + end + end + + if self.seqStart then + local captures = {} + for _,pair in pairs(self.captures) do + if pair.empty then + table.insert(captures, pair[1]) + else + table.insert(captures, utf8sub(str, pair[1], pair[2])) + end + end + return self.seqStart, self.str - 1, unpack(captures) + end + end + + return matcher +end + +-- string.find +local function utf8find(str, regex, init, plain) + local matcher = cache[regex] or matcherGenerator(regex, plain) + return matcher:process(str, init) +end + +-- string.match +local function utf8match(str, regex, init) + init = init or 1 + local found = {utf8find(str, regex, init)} + if found[1] then + if found[3] then + return unpack(found, 3) + end + return utf8sub(str, found[1], found[2]) + end +end + +-- string.gmatch +local function utf8gmatch(str, regex, all) + regex = (utf8sub(regex,1,1) ~= '^') and regex or '%' .. regex + local lastChar = 1 + return function() + local found = {utf8find(str, regex, lastChar)} + if found[1] then + lastChar = found[2] + 1 + if found[all and 1 or 3] then + return unpack(found, all and 1 or 3) + end + return utf8sub(str, found[1], found[2]) + end + end +end + +local function replace(repl, args) + local ret = '' + if type(repl) == 'string' then + local ignore = false + local num + for c in utf8gensub(repl) do + if not ignore then + if c == '%' then + ignore = true + else + ret = ret .. c + end + else + num = tonumber(c) + if num then + ret = ret .. args[num] + else + ret = ret .. c + end + ignore = false + end + end + elseif type(repl) == 'table' then + ret = repl[args[1] or args[0]] or '' + elseif type(repl) == 'function' then + if #args > 0 then + ret = repl(unpack(args, 1)) or '' + else + ret = repl(args[0]) or '' + end + end + return ret +end +-- string.gsub +local function utf8gsub(str, regex, repl, limit) + limit = limit or -1 + local ret = '' + local prevEnd = 1 + local it = utf8gmatch(str, regex, true) + local found = {it()} + local n = 0 + while #found > 0 and limit ~= n do + local args = {[0] = utf8sub(str, found[1], found[2]), unpack(found, 3)} + ret = ret .. utf8sub(str, prevEnd, found[1] - 1) + .. replace(repl, args) + prevEnd = found[2] + 1 + n = n + 1 + found = {it()} + end + return ret .. utf8sub(str, prevEnd), n +end + +local utf8 = {} +utf8.len = utf8len +utf8.sub = utf8sub +utf8.reverse = utf8reverse +utf8.char = utf8char +utf8.unicode = utf8unicode +utf8.gensub = utf8gensub +utf8.byte = utf8unicode +utf8.find = utf8find +utf8.match = utf8match +utf8.gmatch = utf8gmatch +utf8.gsub = utf8gsub +utf8.dump = dump +utf8.format = format +utf8.lower = lower +utf8.upper = upper +utf8.rep = rep +return utf8 diff --git a/main.lua b/main.lua index 1b84bf4..0392dc1 100644 --- a/main.lua +++ b/main.lua @@ -1,6 +1,38 @@ +local Time = require"time" +local loveframes = require"loveframes" + local Clock = require"clock" -local clockTransform = love.math.newTransform(love.graphics.getWidth()/2, love.graphics.getHeight()/2) -local clock = Clock:new(nil, 400, clockTransform) +local clock = Clock:new(nil, love.graphics.getHeight()) + +local controls = loveframes.Create("panel"):SetPos(10,10) +local toggleTime = loveframes.Create("checkbox"):SetParent(controls):SetPos(0,0) +toggleTime:SetText("Show time") +local hourBox = loveframes.Create("numberbox"):SetMinMax(1, 12):SetValue(12) +hourBox:SetParent(controls):SetPos(0,30) +local minuteBox = loveframes.Create("numberbox"):SetMinMax(0, 59):SetValue(0) +loveframes.Create("text"):SetText(":"):SetParent(controls):SetPos(80,30) +minuteBox:SetParent(controls):SetPos(100,30) + +function setClock() + local t = Time:new(hourBox:GetValue(), minuteBox:GetValue()) + clock:setTime(t) +end +hourBox.OnValueChanged = setClock +minuteBox.OnValueChanged = setClock + +function updateInputNoCallback(input, value) + callback = input.onValueChanged + input.onValueChanged = nil + input:SetValue(value) + input.onValueChanged = callback +end + +function updateInputs(time) + local h, m = time:get() + updateInputNoCallback(hourBox, h) + updateInputNoCallback(minuteBox, math.floor(m)) +end +clock.onSetHands = updateInputs function love.mousemoved(x, y, dx, dy, istouch) clock:movemouse(x, y) @@ -9,17 +41,36 @@ end -- todo mousepressed & mousereleased function love.mousepressed(x, y, button, istouch, presses) clock:pressmouse(x, y, button) + loveframes.mousepressed(x, y, button) end function love.mousereleased(x, y, button, istouch, presses) clock:releasemouse(button) + loveframes.mousereleased(x, y, button) +end + +function love.keypressed(key, scancode, isrepeat) + loveframes.keypressed(key, isrepeat) +end + +function love.keyreleased(key) + loveframes.keyreleased(key) +end + +function love.textinput(text) + loveframes.textinput(text) end function love.load() - love.graphics.setBackgroundColor(0,0,0) + love.graphics.setBackgroundColor(0.02,0.53,0.77) +end + +function love.update(dt) + loveframes.update(dt) end function love.draw() - love.graphics.print(clock:getTime():fmt()) + loveframes.draw() + local shift = (love.graphics.getWidth() - love.graphics.getHeight()) / 2 clock:draw() end diff --git a/time.lua b/time.lua new file mode 100644 index 0000000..b46aa73 --- /dev/null +++ b/time.lua @@ -0,0 +1,39 @@ +local MAX_MINUTES = 60 * 12 + +local Time = {} +Time.__index = Time + +local function newTimeRaw(rawMinutes) + local t = { + rawminutes = rawMinutes % MAX_MINUTES + } + return setmetatable(t, Time) +end + +function Time:new(hour, minute) + return newTimeRaw((hour or 12) % 12 * 60 + (minute or 0)) +end + +function Time:get() + local h = math.floor(self.rawminutes / 60 % 12) + if h == 0 then h = 12 end + local m = self.rawminutes % 60 + return h, m +end + +function Time:getHoursSinceTwelve() + local h = self.rawminutes / 60 + if h < 1 then h = h + 12 end + return h +end + +function Time:addMinutes(minutes) + return newTimeRaw(self.rawminutes + minutes) +end + +function Time:__tostring() + local h, m = self:get() + return string.format("%2d:%02d", h, m) +end + +return Time