[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ncustom: https://www.paypal.com/donate/?hosted_button_id=PHU9JP36QQYG2\n"
  },
  {
    "path": ".gitignore",
    "content": "Slab.ini\n.luarc.json\n"
  },
  {
    "path": "API.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nif SLAB_PATH == nil then\n\tSLAB_PATH = (...):match(\"(.-)[^%.]+$\")\nend\n\nSLAB_FILE_PATH = debug.getinfo(1, 'S').source:match(\"^@(.+)/\")\nSLAB_FILE_PATH = SLAB_FILE_PATH == nil and \"\" or SLAB_FILE_PATH\nlocal StatsData = {}\nlocal PrevStatsData = {}\n\nlocal Button = require(SLAB_PATH .. '.Internal.UI.Button')\nlocal CheckBox = require(SLAB_PATH .. '.Internal.UI.CheckBox')\nlocal ColorPicker = require(SLAB_PATH .. '.Internal.UI.ColorPicker')\nlocal ComboBox = require(SLAB_PATH .. '.Internal.UI.ComboBox')\nlocal Config = require(SLAB_PATH .. '.Internal.Core.Config')\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal Scale = require(SLAB_PATH .. \".Internal.Core.Scale\")\nlocal Dialog = require(SLAB_PATH .. '.Internal.UI.Dialog')\nlocal Dock = require(SLAB_PATH .. '.Internal.UI.Dock')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal FileSystem = require(SLAB_PATH .. '.Internal.Core.FileSystem')\nlocal Image = require(SLAB_PATH .. '.Internal.UI.Image')\nlocal Input = require(SLAB_PATH .. '.Internal.UI.Input')\nlocal Keyboard = require(SLAB_PATH .. '.Internal.Input.Keyboard')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal ListBox = require(SLAB_PATH .. '.Internal.UI.ListBox')\nlocal Messages = require(SLAB_PATH .. '.Internal.Core.Messages')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Menu = require(SLAB_PATH .. '.Internal.UI.Menu')\nlocal MenuState = require(SLAB_PATH .. '.Internal.UI.MenuState')\nlocal MenuBar = require(SLAB_PATH .. '.Internal.UI.MenuBar')\nlocal Region = require(SLAB_PATH .. '.Internal.UI.Region')\nlocal Separator = require(SLAB_PATH .. '.Internal.UI.Separator')\nlocal Shape = require(SLAB_PATH .. '.Internal.UI.Shape')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Text = require(SLAB_PATH .. '.Internal.UI.Text')\nlocal Tree = require(SLAB_PATH .. '.Internal.UI.Tree')\nlocal Utility = require(SLAB_PATH .. '.Internal.Core.Utility')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\n\n--[[\n\tSlab\n\n\tSlab is an immediate mode GUI toolkit for the Love 2D framework. This library is designed to\n\tallow users to easily add this library to their existing Love 2D projects and quickly create\n\ttools to enable them to iterate on their ideas quickly. The user should be able to utilize this\n\tlibrary with minimal integration steps and is completely written in Lua and utilizes\n\tthe Love 2D API. No compiled binaries are required and the user will have access to the source\n\tso that they may make adjustments that meet the needs of their own projects and tools. Refer\n\tto main.lua and SlabTest.lua for example usage of this library.\n\n\tSupported Version: 11.3.0\n\n\tAPI:\n\t\tInitialize\n\t\tGetVersion\n\t\tGetLoveVersion\n\t\tUpdate\n\t\tDraw\n\t\tSetINIStatePath\n\t\tGetINIStatePath\n\t\tSetVerbose\n\t\tGetMessages\n\n\t\tStyle:\n\t\t\tGetStyle\n\t\t\tPushFont\n\t\t\tPopFont\n\n\t\tWindow:\n\t\t\tBeginWindow\n\t\t\tEndWindow\n\t\t\tGetWindowPosition\n\t\t\tGetWindowSize\n\t\t\tGetWindowContentSize\n\t\t\tGetWindowActiveSize\n\t\t\tIsWindowAppearing\n\t\t\tPushID\n\t\t\tPopID\n\n\t\tMenu:\n\t\t\tBeginMainMenuBar\n\t\t\tEndMainMenuBar\n\t\t\tBeginMenuBar\n\t\t\tEndMenuBar\n\t\t\tBeginMenu\n\t\t\tEndMenu\n\t\t\tBeginContextMenuItem\n\t\t\tBeginContextMenuWindow\n\t\t\tEndContextMenu\n\t\t\tMenuItem\n\t\t\tMenuItemChecked\n\n\t\tSeparator\n\t\tButton\n\t\tRadioButton\n\t\tText\n\t\tTextSelectable\n\t\tTextf\n\t\tGetTextSize\n\t\tGetTextWidth\n\t\tGetTextHeight\n\t\tCheckBox\n\t\tInput\n\t\tInputNumberDrag\n\t\tInputNumberSlider\n\t\tGetInputText\n\t\tGetInputNumber\n\t\tGetInputCursorPos\n\t\tIsInputFocused\n\t\tIsAnyInputFocused\n\t\tSetInputFocus\n\t\tSetInputCursorPos\n\t\tSetInputCursorPosLine\n\t\tBeginTree\n\t\tEndTree\n\t\tBeginComboBox\n\t\tEndComboBox\n\t\tImage\n\n\t\tCursor:\n\t\t\tSameLine\n\t\t\tNewLine\n\t\t\tSetCursorPos\n\t\t\tGetCursorPos\n\t\t\tIndent\n\t\t\tUnindent\n\n\t\tProperties\n\n\t\tListBox:\n\t\t\tBeginListBox\n\t\t\tEndListBox\n\t\t\tBeginListBoxItem\n\t\t\tIsListBoxItemClicked\n\t\t\tEndListBoxItem\n\n\t\tDialog:\n\t\t\tOpenDialog\n\t\t\tBeginDialog\n\t\t\tEndDialog\n\t\t\tCloseDialog\n\t\t\tMessageBox\n\t\t\tFileDialog\n\t\t\tColorPicker\n\n\t\tMouse:\n\t\t\tIsMouseDown\n\t\t\tIsMouseClicked\n\t\t\tIsMouseReleased\n\t\t\tIsMouseDoubleClicked\n\t\t\tIsMouseDragging\n\t\t\tGetMousePosition\n\t\t\tGetMousePositionWindow\n\t\t\tGetMouseDelta\n\t\t\tSetCustomMouseCursor\n\t\t\tClearCustomMouseCursor\n\n\t\tControl:\n\t\t\tIsControlHovered\n\t\t\tIsControlClicked\n\t\t\tGetControlSize\n\t\t\tIsVoidHovered\n\t\t\tIsVoidClicked\n\n\t\tKeyboard:\n\t\t\tIsKeyDown\n\t\t\tIsKeyPressed\n\t\t\tIsKeyReleased\n\n\t\tShape:\n\t\t\tRectangle\n\t\t\tCircle\n\t\t\tTriangle\n\t\t\tLine\n\t\t\tCurve\n\t\t\tGetCurveControlPointCount\n\t\t\tGetCurveControlPoint\n\t\t\tEvaluateCurve\n\t\t\tEvaluateCurveMouse\n\t\t\tPolygon\n\n\t\tStats:\n\t\t\tBeginStat\n\t\t\tEndStat\n\t\t\tEnableStats\n\t\t\tIsStatsEnabled\n\t\t\tFlushStats\n\n\t\tLayout:\n\t\t\tBeginLayout\n\t\t\tEndLayout\n\t\t\tSetLayoutColumn\n\t\t\tGetLayoutSize\n\n\t\tScroll:\n\t\t\tSetScrollSpeed\n\t\t\tGetScrollSpeed\n\n\t\tShader:\n\t\t\tPushShader\n\t\t\tPopShader\n\n\t\tDock:\n\t\t\tEnableDocks\n\t\t\tDisableDocks\n\t\t\tSetDockOptions\n--]]\nlocal Slab = {}\n\n-- Slab version numbers.\nlocal Version_Major = 0\nlocal Version_Minor = 9\nlocal Version_Revision = 0\n\nlocal FrameStatHandle = nil\n\n-- The path to save the UI state to a file. This will default to the save directory.\nlocal INIStatePath = \"Slab.ini\"\nlocal IsDefault = true\nlocal QuitFn = nil\nlocal Verbose = false\nlocal Initialized = false\nlocal DontInterceptEventHandlers = false\n\n\nlocal ModifyCursor = true\n\nlocal function LoadState()\n\tif INIStatePath == nil then return end\n\n\tlocal Result, Error = Config.LoadFile(INIStatePath, IsDefault)\n\tif Result ~= nil then\n\t\tDock.Load(Result)\n\t\tTree.Load(Result)\n\t\tWindow.Load(Result)\n\tend\n\n\tif Verbose then\n\t\tprint(\"Load INI file:\", INIStatePath, \"Error:\", Error)\n\tend\nend\n\nlocal function SaveState()\n\tif INIStatePath == nil then return end\n\n\tlocal Table = {}\n\tDock.Save(Table)\n\tTree.Save(Table)\n\tWindow.Save(Table)\n\tlocal Result, Error = Config.Save(INIStatePath, Table, IsDefault)\n\n\tif Verbose then\n\t\tprint(\"Save INI file:\", INIStatePath, \"Error:\", Error)\n\tend\nend\n\nlocal function TextInput(Ch)\n\tInput.Text(Ch)\n\n\tif (not DontInterceptEventHandlers) and love.textinput ~= nil then\n\t\tlove.textinput(Ch)\n\tend\nend\n\nlocal function WheelMoved(X, Y)\n\tWindow.WheelMoved(X, Y)\n\n\tif (not DontInterceptEventHandlers) and love.wheelmoved ~= nil then\n\t\tlove.wheelmoved(X, Y)\n\tend\nend\n\nlocal function OnQuit()\n\tSaveState()\n\n\tif QuitFn ~= nil then\n\t\tQuitFn()\n\tend\nend\n\n--[[\n\tEvent forwarding\n--]]\n\nSlab.OnTextInput = TextInput;\nSlab.OnWheelMoved = WheelMoved;\nSlab.OnQuit = OnQuit;\nSlab.OnKeyPressed = Keyboard.OnKeyPressed;\nSlab.OnKeyReleased = Keyboard.OnKeyReleased;\nSlab.OnMouseMoved = Mouse.OnMouseMoved\nSlab.OnMousePressed = Mouse.OnMousePressed;\nSlab.OnMouseReleased = Mouse.OnMouseReleased;\n\n--[[\n\tInitialize\n\n\tInitializes Slab and hooks into the required events. This function should be called in love.load.\n\n\targs: [Table] The list of parameters passed in by the user on the command-line. This should be passed in from\n\t\tlove.load function. Below is a list of arguments available to modify Slab:\n\t\tNoMessages: [String] Disables the messaging system that warns developers of any changes in the API.\n\t\tNoDocks: [String] Disables all docks.\n\t\tNoCursor: [String] Disables modifying the cursor\n\n\tReturn: None.\n--]]\nfunction Slab.Initialize(args, dontInterceptEventHandlers)\n\tif Initialized then\n\t\treturn\n\tend\n\n\tDontInterceptEventHandlers = dontInterceptEventHandlers\n\tStyle.API.Initialize()\n\n\targs = args or {}\n\tif type(args) == 'table' then\n\t\tfor I, V in ipairs(args) do\n\t\t\tif string.lower(V) == 'nomessages' then\n\t\t\t\tMessages.SetEnabled(false)\n\t\t\telseif string.lower(V) == 'nodocks' then\n\t\t\t\tSlab.DisableDocks({'Left', 'Right', 'Bottom'})\n\t\t\telseif string.lower(V) == 'nocursor' then\n\t\t\t\tModifyCursor = false\n\t\t\tend\n\t\tend\n\tend\n\n\tif not dontInterceptEventHandlers then\n\t\tlove.handlers['textinput'] = TextInput\n\t\tlove.handlers['wheelmoved'] = WheelMoved\n\n\t\t-- In Love 11.3, overriding love.handlers['quit'] doesn't seem to affect the callback during shutdown.\n\t\t-- Storing and overriding love.quit manually will properly call Slab's callback. This function will call\n\t\t-- the stored function once Slab is finished with its process.\n\t\tQuitFn = love.quit\n\t\tlove.quit = OnQuit\n\tend\n\n\tKeyboard.Initialize(args, dontInterceptEventHandlers)\n\tMouse.Initialize(args, dontInterceptEventHandlers)\n\n\tLoadState()\n\n\tInitialized = true\nend\n\n--[[\n\tGetVersion\n\n\tRetrieves the current version of Slab being used as a string.\n\n\tReturn: [String] String of the current Slab version.\n--]]\nfunction Slab.GetVersion()\n\treturn string.format(\"%d.%d.%d\", Version_Major, Version_Minor, Version_Revision)\nend\n\n--[[\n\tGetLoveVersion\n\n\tRetrieves the current version of Love being used as a string.\n\n\tReturn: [String] String of the current Love version.\n--]]\nfunction Slab.GetLoveVersion()\n\tlocal Major, Minor, Revision, Codename = love.getVersion()\n\treturn string.format(\"%d.%d.%d - %s\", Major, Minor, Revision, Codename)\nend\n\n--[[\n\tUpdate\n\n\tUpdates the input state and states of various widgets. This function must be called every frame.\n\tThis should be called before any Slab calls are made to ensure proper responses to Input are made.\n\n\tdt: [Number] The delta time for the frame. This should be passed in from love.update.\n\n\tReturn: None.\n--]]\nfunction Slab.Update(dt)\n\tStats.Reset(false)\n\tFrameStatHandle = Stats.Begin('Frame', 'Slab')\n\tlocal StatHandle = Stats.Begin('Update', 'Slab')\n\n\tMouse.Update()\n\tKeyboard.Update()\n\tInput.Update(dt)\n\tDrawCommands.Reset()\n\tWindow.Reset()\n\tLayoutManager.Validate()\n\n\tif MenuState.IsOpened then\n\t\tMenuState.WasOpened = MenuState.IsOpened\n\t\tif Mouse.IsClicked(1) then\n\t\t\tMenuState.RequestClose = true\n\t\tend\n\tend\n\n\tStats.End(StatHandle)\nend\n\n--[[\n\tDraw\n\n\tThis function will execute all buffered draw calls from the various Slab calls made prior. This\n\tfunction should be called from love.draw and should be called at the very to ensure Slab is rendered\n\tabove the user's workspace.\n\n\tReturn: None.\n--]]\nfunction Slab.Draw()\n\tif Stats.IsEnabled() then\n\t\tPrevStatsData = love.graphics.getStats(PrevStatsData)\n\tend\n\n\tlocal StatHandle = Stats.Begin('Draw', 'Slab')\n\n\tWindow.Validate()\n\n\tlocal MovingInstance = Window.GetMovingInstance()\n\tif MovingInstance ~= nil then\n\t\tDock.DrawOverlay()\n\t\tDock.SetPendingWindow(MovingInstance)\n\telse\n\t\tDock.Commit()\n\tend\n\n\tif MenuState.RequestClose then\n\t\tMenu.Close()\n\t\tMenuBar.Clear()\n\tend\n\n\tif ModifyCursor then\n\t\tMouse.Draw()\n\tend\n\n\tif Mouse.IsReleased(1) then\n\t\tButton.ClearClicked()\n\tend\n\n\tlove.graphics.setColor(1, 1, 1, 1)\n\tlove.graphics.push()\n\tlove.graphics.origin()\n\tDrawCommands.Execute()\n\tlove.graphics.pop()\n\tlove.graphics.setColor(1, 1, 1, 1)\n\n\tStats.End(StatHandle)\n\n\t-- Only call end if 'Update' was called and a valid handle was retrieved. This can happen for developers using a custom\n\t-- run function with a fixed update.\n\tif FrameStatHandle ~= nil then\n\t\tStats.End(FrameStatHandle)\n\t\tFrameStatHandle = nil\n\tend\n\n\tif Stats.IsEnabled() then\n\t\tStatsData = love.graphics.getStats(StatsData)\n\t\tfor k, v in pairs(StatsData) do\n\t\t\tStatsData[k] = v - PrevStatsData[k]\n\t\tend\n\tend\nend\n\n--[[\n\tSetINIStatePath\n\n\tSets the INI path to save the UI state. If nil, Slab will not save the state to disk.\n\n\tReturn: None.\n--]]\nfunction Slab.SetINIStatePath(Path)\n\tINIStatePath = Path\n\tIsDefault = false\nend\n\n--[[\n\tGetINIStatePath\n\n\tGets the INI path to save the UI state. This value can be nil.\n\n\tReturn: [String] The path on disk the UI state will be saved to.\n--]]\nfunction Slab.GetINIStatePath()\n\treturn INIStatePath\nend\n\n--[[\n\tSetVerbose\n\n\tEnable/Disables internal Slab logging. Could be useful for diagnosing problems that occur inside of Slab.\n\n\tIsVerbose: [Boolean] Flag to enable/disable verbose logging.\n\n\tReturn: None.\n--]]\nfunction Slab.SetVerbose(IsVerbose)\n\tVerbose = (IsVerbose == nil or type(IsVerbose) ~= 'boolean') and false or IsVerbose\nend\n\n--[[\n\tGetMessages\n\n\tRetrieves a list of existing messages that has been captured by Slab.\n\n\tReturn: [Table] List of messages that have been broadcasted from Slab.\n--]]\nfunction Slab.GetMessages()\n\treturn Messages.Get()\nend\n\n--[[\n\tGetStyle\n\n\tRetrieve the style table associated with the current instance of Slab. This will allow the user to add custom styling\n\tto their controls.\n\n\tReturn: [Table] The style table.\n--]]\nfunction Slab.GetStyle()\n\treturn Style\nend\n\n\n--[[\n    SetScale\n\n    Sets the rendering scale for the Slab context.\n\n\tscaleFactor: [number] The scale factor to use\n\n\tReturn: None.\n--]]\nfunction Slab.SetScale(scaleFactor)\n\tScale.SetScale(scaleFactor)\nend\n\n\n--[[\n    GetScale\n\n\tRetrieve the scale of the current Slab context.\n\n\tReturn: [number] The current scale.\n--]]\nfunction Slab.GetScale()\n\treturn Scale.GetScale()\nend\n\n\n--[[\n\tPushFont\n\n\tPushes a Love font object onto the font stack. All text rendering will use this font until PopFont is called.\n\n\tFont: [Object] The Love font object to use.\n\n\tReturn: None.\n--]]\nfunction Slab.PushFont(Font)\n\tStyle.API.PushFont(Font)\nend\n\n--[[\n\tPopFont\n\n\tPops the last font from the stack.\n\n\tReturn: None.\n--]]\nfunction Slab.PopFont()\n\tStyle.API.PopFont()\nend\n\n--[[\n\tBeginWindow\n\n\tThis function begins the process of drawing widgets to a window. This function must be followed up with\n\tan EndWindow call to ensure proper behavior of drawing windows.\n\n\tId: [String] A unique string identifying this window in the project.\n\tOptions: [Table] List of options that control how this window will behave.\n\t\tX: [Number] The X position to start rendering the window at.\n\t\tY: [Number] The Y position to start rendering the window at.\n\t\tW: [Number] The starting width of the window.\n\t\tH: [Number] The starting height of the window.\n\t\tContentW: [Number] The starting width of the content contained within this window.\n\t\tContentH: [Number] The starting height of the content contained within this window.\n\t\tBgColor: [Table] The background color value for this window. Will use the default style WindowBackgroundColor if this is empty.\n\t\tTitle: [String] The title to display for this window. If emtpy, no title bar will be rendered and the window will not be movable.\n\t\tTitleH: [Number] The height of the title bar. By default, this will be the height of the current font set in the style. If no title is\n\t\t\tset, this is ignored.\n\t\tTitleAlignX: [String] Horizontal alignment of the title. The available options are 'left', 'center', and 'right'. The default is 'center'.\n\t\tTitleAlignY: [String] Vertical alignment of the title. The available options are 'top', 'center', and 'bottom'. The default is 'center'.\n\t\tAllowMove: [Boolean] Controls whether the window is movable within the title bar area. The default value is true.\n\t\tAllowResize: [Boolean] Controls whether the window is resizable. The default value is true. AutoSizeWindow must be false for this to work.\n\t\tAllowFocus: [Boolean] Controls whether the window can be focused. The default value is true.\n\t\tBorder: [Number] The value which controls how much empty space should be left between all sides of the window from the content.\n\t\t\tThe default value is 4.0\n\t\tNoOutline: [Boolean] Controls whether an outline should not be rendered. The default value is false.\n\t\tIsMenuBar: [Boolean] Controls whether if this window is a menu bar or not. This flag should be ignored and is used by the menu bar\n\t\t\tsystem. The default value is false.\n\t\tAutoSizeWindow: [Boolean] Automatically updates the window size to match the content size. The default value is true.\n\t\tAutoSizeWindowW: [Boolean] Automatically update the window width to match the content size. This value is taken from AutoSizeWindow by default.\n\t\tAutoSizeWindowH: [Boolean] Automatically update the window height to match the content size. This value is taken from AutoSizeWindow by default.\n\t\tAutoSizeContent: [Boolean] The content size of the window is automatically updated with each new widget. The default value is true.\n\t\tLayer: [String] The layer to which to draw this window. This is used internally and should be ignored by the user.\n\t\tResetPosition: [Boolean] Determines if the window should reset any delta changes to its position.\n\t\tResetSize: [Boolean] Determines if the window should reset any delta changes to its size.\n\t\tResetContent: [Boolean] Determines if the window should reset any delta changes to its content size.\n\t\tResetLayout: [Boolean] Will reset the position, size, and content. Short hand for the above 3 flags.\n\t\tSizerFilter: [Table] Specifies what sizers are enabled for the window. If nothing is specified, all sizers are available. The values can\n\t\t\tbe: NW, NE, SW, SE, N, S, E, W\n\t\tCanObstruct: [Boolean] Sets whether this window is considered for obstruction of other windows and their controls. The default value is true.\n\t\tRounding: [Number] Amount of rounding to apply to the corners of the window.\n\t\tIsOpen: [Boolean] Determines if the window is open. If this value exists within the options, a close button will appear in\n\t\t\tthe corner of the window and is updated when this button is pressed to reflect the new open state of this window.\n\t\tNoSavedSettings: [Boolean] Flag to disable saving this window's settings to the state INI file.\n\t\tConstrainPosition: [Boolean] Flag to constrain the position of the window to the bounds of the viewport.\n\t\tShowMinimize: [Boolean] Flag to show a minimize button in the title bar of the window. Default is `true`.\n\t\tShowScrollbarX: [Boolean] Flag to show the horizontal scrollbar regardless of the window and content internal state. Default is `false`\n\t\tShowScrollbarY: [Boolean] Flag to show the vertical scrollbar regardless of the window and content internal state. Default is `false`\n\n\tReturn: [Boolean] The open state of this window. Useful for simplifying API calls by storing the result in a flag instead of a table.\n\t\tEndWindow must still be called regardless of the result for this value.\n--]]\nfunction Slab.BeginWindow(Id, Options)\n\treturn Window.Begin(Id, Options)\nend\n\n--[[\n\tEndWindow\n\n\tThis function must be called after a BeginWindow and associated widget calls. If the user fails to call this, an assertion will be thrown\n\tto alert the user.\n\n\tReturn: None.\n--]]\nfunction Slab.EndWindow()\n\tWindow.End()\nend\n\n--[[\n\tGetWindowPosition\n\n\tRetrieves the active window's position.\n\n\tReturn: [Number], [Number] The X and Y position of the active window.\n--]]\nfunction Slab.GetWindowPosition()\n\treturn Window.GetPosition()\nend\n\n--[[\n\tGetWindowSize\n\n\tRetrieves the active window's size.\n\n\tReturn: [Number], [Number] The width and height of the active window.\n--]]\nfunction Slab.GetWindowSize()\n\treturn Window.GetSize()\nend\n\n--[[\n\tGetWindowContentSize\n\n\tRetrieves the active window's content size.\n\n\tReturn: [Number], [Number] The width and height of the active window content.\n--]]\nfunction Slab.GetWindowContentSize()\n\treturn Window.GetContentSize()\nend\n\n--[[\n\tGetWindowActiveSize\n\n\tRetrieves the active window's active size minus the borders.\n\n\tReturn: [Number], [Number] The width and height of the window's active bounds.\n--]]\nfunction Slab.GetWindowActiveSize()\n\treturn Window.GetBorderlessSize()\nend\n\n--[[\n\tIsWindowAppearing\n\n\tIs the current window appearing this frame. This will return true if BeginWindow has\n\tnot been called for a window over 2 or more frames.\n\n\tReturn: [Boolean] True if the window is appearing this frame. False otherwise.\n--]]\nfunction Slab.IsWindowAppearing()\n\treturn Window.IsAppearing()\nend\n\n--[[\n\tPushID\n\n\tPushes a custom ID onto a stack. This allows developers to differentiate between similar controls such as\n\ttext controls.\n\n\tID: [String] The custom ID to add.\n\n\tReturn: None.\n--]]\nfunction Slab.PushID(ID)\n\tassert(type(ID) == 'string', \"'ID' parameter must be a string value.\")\n\n\tWindow.PushID(ID)\nend\n\n--[[\n\tPopID\n\n\tPops the last custom ID from the stack.\n\n\tReturn: None.\n--]]\nfunction Slab.PopID()\n\tWindow.PopID()\nend\n\n--[[\n\tBeginMainMenuBar\n\n\tThis function begins the process for setting up the main menu bar. This should be called outside of any BeginWindow/EndWindow calls.\n\tThe user should only call EndMainMenuBar if this function returns true. Use BeginMenu/EndMenu calls to add menu items on the main menu bar.\n\n\tExample:\n\t\tif Slab.BeginMainMenuBar() then\n\t\t\tif Slab.BeginMenu(\"File\") then\n\t\t\t\tif Slab.MenuItem(\"Quit\") then\n\t\t\t\t\tlove.event.quit()\n\t\t\t\tend\n\n\t\t\t\tSlab.EndMenu()\n\t\t\tend\n\n\t\t\tSlab.EndMainMenuBar()\n\t\tend\n\n\tReturn: [Boolean] Returns true if the main menu bar process has started.\n--]]\nfunction Slab.BeginMainMenuBar()\n\tlocal X,Y = 0.0, 0.0\n\tif Utility.IsMobile() then\n\t\tX, Y = love.window.getSafeArea()\n\tend\n\tCursor.SetPosition(X, Y)\n\treturn Slab.BeginMenuBar(true)\nend\n\n--[[\n\tEndMainMenuBar\n\n\tThis function should be called if BeginMainMenuBar returns true.\n\n\tReturn: None.\n--]]\nfunction Slab.EndMainMenuBar()\n\tSlab.EndMenuBar()\nend\n\n--[[\n\tBeginMenuBar\n\n\tThis function begins the process of rendering a menu bar for a window. This should only be called within a BeginWindow/EndWindow context.\n\n\tIsMainMenuBar: [Boolean] Is this menu bar for the main viewport. Used internally. Should be ignored for all other calls.\n\n\tReturn: [Boolean] Returns true if the menu bar process has started.\n--]]\nfunction Slab.BeginMenuBar(IsMainMenuBar)\n\treturn MenuBar.Begin(IsMainMenuBar)\nend\n\n--[[\n\tEndMenuBar\n\n\tThis function should be called if BeginMenuBar returns true.\n\n\tReturn: None.\n--]]\nfunction Slab.EndMenuBar()\n\tMenuBar.End()\nend\n\n--[[\n\tBeginMenu\n\n\tAdds a menu item that when the user hovers over, opens up an additional context menu. When used within a menu bar, BeginMenu calls\n\twill be added to the bar. Within a context menu, the menu item will be added within the context menu with an additional arrow to notify\n\tthe user more options are available. If this function returns true, the user must call EndMenu.\n\n\tLabel: [String] The label to display for this menu.\n\tOptions: [Table] List of options that control how this menu behaves.\n\t\tEnabled: [Boolean] Determines if this menu is enabled. This value is true by default. Disabled items are displayed but\n\t\t\tcannot be interacted with.\n\n\tReturn: [Boolean] Returns true if the menu item is being hovered.\n--]]\nfunction Slab.BeginMenu(Label, Options)\n\treturn Menu.BeginMenu(Label, Options)\nend\n\n--[[\n\tEndMenu\n\n\tFinishes up a BeginMenu. This function must be called if BeginMenu returns true.\n\n\tReturn: None.\n--]]\nfunction Slab.EndMenu()\n\tMenu.EndMenu()\nend\n\n--[[\n\tBeginContextMenuItem\n\n\tOpens up a context menu based on if the user right clicks on the last item. This function should be placed immediately after an item\n\tcall to open up a context menu for that specific item. If this function returns true, EndContextMenu must be called.\n\n\tExample:\n\t\tif Slab.Button(\"Button!\") then\n\t\t\t-- Perform logic here when button is clicked\n\t\tend\n\n\t\t-- This will only return true if the previous button is hot and the user right-clicks.\n\t\tif Slab.BeginContextMenuItem() then\n\t\t\tSlab.MenuItem(\"Button Item 1\")\n\t\t\tSlab.MenuItem(\"Button Item 2\")\n\n\t\t\tSlab.EndContextMenu()\n\t\tend\n\n\tButton: [Number] The mouse button to use for opening up this context menu.\n\n\tReturn: [Boolean] Returns true if the user right clicks on the previous item call. EndContextMenu must be called in order for\n\t\tthis to function properly.\n--]]\nfunction Slab.BeginContextMenuItem(Button)\n\treturn Menu.BeginContextMenu({IsItem = true, Button = Button})\nend\n\n--[[\n\tBeginContextMenuWindow\n\n\tOpens up a context menu based on if the user right clicks anywhere within the window. It is recommended to place this function at the end\n\tof a window's widget calls so that Slab can catch any BeginContextMenuItem calls before this call. If this function returns true,\n\tEndContextMenu must be called.\n\n\tButton: [Number] The mouse button to use for opening up this context menu.\n\n\tReturn: [Boolean] Returns true if the user right clicks anywhere within the window. EndContextMenu must be called in order for this\n\t\tto function properly.\n--]]\nfunction Slab.BeginContextMenuWindow(Button)\n\treturn Menu.BeginContextMenu({IsWindow = true, Button = Button})\nend\n\n--[[\n\tEndContextMenu\n\n\tFinishes up any BeginContextMenuItem/BeginContextMenuWindow if they return true.\n\n\tReturn: None.\n--]]\nfunction Slab.EndContextMenu()\n\tMenu.EndContextMenu()\nend\n\n--[[\n\tMenuItem\n\n\tAdds a menu item to a given context menu.\n\n\tLabel: [String] The label to display to the user.\n\tOptions: [Table] List of options that control how this menu behaves.\n\t\tEnabled: [Boolean] Determines if this menu is enabled. This value is true by default. Disabled items are displayed but\n\t\t\tcannot be interacted with.\n\t\tHint: [String] Show an input hint to the right of the menu item\n\n\tReturn: [Boolean] Returns true if the user clicks on this menu item.\n--]]\nfunction Slab.MenuItem(Label, Options)\n\treturn Menu.MenuItem(Label, Options)\nend\n\n--[[\n\tMenuItemChecked\n\n\tAdds a menu item to a given context menu. If IsChecked is true, then a check mark will be rendered next to the\n\tlabel.\n\n\tExample:\n\t\tlocal Checked = false\n\t\tif Slab.MenuItemChecked(\"Menu Item\", Checked)\n\t\t\tChecked = not Checked\n\t\tend\n\n\tLabel: [String] The label to display to the user.\n\tIsChecked: [Boolean] Determines if a check mark should be rendered next to the label.\n\tOptions: [Table] List of options that control how this menu behaves.\n\t\tEnabled: [Boolean] Determines if this menu is enabled. This value is true by default. Disabled items are displayed but\n\t\t\tcannot be interacted with.\n\n\tReturn: [Boolean] Returns true if the user clicks on this menu item.\n--]]\nfunction Slab.MenuItemChecked(Label, IsChecked, Options)\n\treturn Menu.MenuItemChecked(Label, IsChecked, Options)\nend\n\n--[[\n\tSeparator\n\n\tThis functions renders a separator line in the window.\n\n\tOption: [Table] List of options for how this separator will be drawn.\n\t\tIncludeBorders: [Boolean] Whether to extend the separator to include the window borders. This is false by default.\n\t\tH: [Number] The height of the separator. This doesn't change the line thickness, rather, specifies the cursor advancement\n\t\t\tin the Y direction.\n\t\tThickness: [Number] The thickness of the line rendered. The default value is 1.0.\n\n\tReturn: None.\n--]]\nfunction Slab.Separator(Options)\n\tSeparator.Begin(Options)\nend\n\n--[[\n\tButton\n\n\tAdds a button to the active window.\n\n\tLabel: [String] The label to display on the button.\n\tOptions: [Table] List of options for how this button will behave.\n\t\tTooltip: [String] The tooltip to display when the user hovers over this button.\n\t\tRounding: [Number] Amount of rounding to apply to the corners of the button.\n\t\tInvisible: [Boolean] Don't render the button, but keep the behavior.\n\t\tW: [Number] Override the width of the button.\n\t\tH: [Number] Override the height of the button.\n\t\tDisabled: [Boolean] If true, the button is not interactable by the user.\n\t\tImage: [Table] A table of options used to draw an image instead of a text label. Refer to the 'Image' documentation for a list\n\t\t\tof available options.\n\t\tColor: [Table]: The background color of the button when idle. The default value is the ButtonColor property in the Style's table.\n\t\tHoverColor: [Table]: The background color of the button when a mouse is hovering the control. The default value is the ButtonHoveredColor property\n\t\t\tin the Style's table.\n\t\tPressColor: [Table]: The background color of the button when the button is pressed but not released. The default value is the ButtonPressedColor\n\t\t\tproperty in the Style's table.\n\t\tPadX: [Number] Amount of additional horizontal space the background will expand to from the center. The default value is 20.\n\t\tPadY: [Number] Amount of additional vertical space the background will expand to from the center. The default value is 5.\n\t\tVLines: [Number] Number of lines in a multiline button text. The default value is 1.\n\n\tReturn: [Boolean] Returns true if the user clicks on this button.\n--]]\nfunction Slab.Button(Label, Options)\n\treturn Button.Begin(Label, Options)\nend\n\n--[[\n\tRadioButton\n\n\tAdds a radio button entry to the active window. The grouping of radio buttons is determined by the user. An Index can\n\tbe applied to the given radio button and a SelectedIndex can be passed in to determine if this specific radio button\n\tis the selected one.\n\n\tLabel: [String] The label to display next to the button.\n\tOptions: [Table] List of options for how this radio button will behave.\n\t\tIndex: [Number] The index of this radio button. Will be 0 by default and not selectable. Assign an index to group the button.\n\t\tSelectedIndex: [Number] The index of the radio button that is selected. If this equals the Index field, then this radio button\n\t\t\twill be rendered as selected.\n\t\tTooltip: [String] The tooltip to display when the user hovers over the button or label.\n\n\tReturn: [Boolean] Returns true if the user clicks on this button.\n--]]\nfunction Slab.RadioButton(Label, Options)\n\treturn Button.BeginRadio(Label, Options)\nend\n\n--[[\n\tText\n\n\tAdds text to the active window.\n\n\tLabel: [String] The string to be displayed in the window.\n\tOptions: [Table] List of options for how this text is displayed.\n\t\tColor: [Table] The color to render the text.\n\t\tPad: [Number] How far to pad the text from the left side of the current cursor position.\n\t\tPadH: [Number] How far to pad the text vertically, will render centered in this region\n\t\tIsSelectable: [Boolean] Whether this text is selectable using the text's Y position and the window X and width as the\n\t\t\thot zone.\n\t\tIsSelectableTextOnly: [Boolean] Will use the text width instead of the window width to determine the hot zone. Will set IsSelectable\n\t\t\tto true if that option is missing.\n\t\tIsSelected: [Boolean] Forces the hover background to be rendered.\n\t\tSelectOnHover: [Boolean] Returns true if the user is hovering over the hot zone of this text.\n\t\tHoverColor: [Table] The color to render the background if the IsSelected option is true.\n\t\tURL: [String] A URL address to open when this text control is clicked.\n\n\tReturn: [Boolean] Returns true if SelectOnHover option is set to true. False otherwise.\n--]]\nfunction Slab.Text(Label, Options)\n\treturn Text.Begin(Label, Options)\nend\n\n--[[\n\tTextSelectable\n\n\tThis function is a shortcut for SlabText with the IsSelectable option set to true.\n\n\tLabel: [String] The string to be displayed in the window.\n\tOptions: [Table] List of options for how this text is displayed.\n\t\tSee Slab.Text for all options.\n\n\tReturn: [Boolean] Returns true if user clicks on this text. False otherwise.\n--]]\nfunction Slab.TextSelectable(Label, Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.IsSelectable = true\n\treturn Slab.Text(Label, Options)\nend\n\n--[[\n\tTextf\n\n\tAdds formatted text to the active window. This text will wrap to fit within the contents of\n\teither the window or a user specified width.\n\n\tLabel: [String] The text to be rendered.\n\tOptions: [Table] List of options for how this text is displayed.\n\t\tColor: [Table] The color to render the text.\n\t\tW: [Number] The width to restrict the text to. If this option is not specified, then the window\n\t\t\twidth is used.\n\t\tAlign: [String] The alignment to use for this text. For more information, refer to the love documentation\n\t\t\tat https://love2d.org/wiki/AlignMode. Below are the available options:\n\t\t\tcenter: Align text center.\n\t\t\tleft: Align text left.\n\t\t\tright: Align text right.\n\t\t\tjustify: Align text both left and right.\n\n\tReturn: None.\n--]]\nfunction Slab.Textf(Label, Options)\n\tText.BeginFormatted(Label, Options)\nend\n\n--[[\n\tGetTextSize\n\n\tRetrieves the width and height of the given text. The result is based on the current font.\n\n\tLabel: [String] The string to retrieve the size for.\n\n\tReturn: [Number], [Number] The width and height of the given text.\n--]]\nfunction Slab.GetTextSize(Label)\n\treturn Text.GetSize(Label)\nend\n\n--[[\n\tGetTextWidth\n\n\tRetrieves the width of the given text. The result is based on the current font.\n\n\tLabel: [String] The string to retrieve the width for.\n\n\tReturn: [Number] The width of the given text.\n--]]\nfunction Slab.GetTextWidth(Label)\n\tlocal W, H = Slab.GetTextSize(Label)\n\treturn W\nend\n\n--[[\n\tGetTextHeight\n\n\tRetrieves the height of the current font.\n\n\tReturn: [Number] The height of the current font.\n--]]\nfunction Slab.GetTextHeight()\n\treturn Text.GetHeight()\nend\n\n--[[\n\tCheckBox\n\n\tRenders a check box with a label. The check box when enabled will render an 'X'.\n\n\tEnabled: [Boolean] Will render an 'X' within the box if true. Will be an empty box otherwise.\n\tLabel: [String] The label to display after the check box.\n\tOptions: [Table] List of options for how this check box will behave.\n\t\tTooltip: [String] Text to be displayed if the user hovers over the check box.\n\t\tId: [String] An optional Id that can be supplied by the user. By default, the Id will be the label.\n\t\tRounding: [Number] Amount of rounding to apply to the corners of the check box.\n\t\tSize: [Number] The uniform size of the box. The default value is 16.\n\t\tDisabled: [Boolean] Dictates whether this check box is enabled for interaction.\n\n\tReturn: [Boolean] Returns true if the user clicks within the check box.\n--]]\nfunction Slab.CheckBox(Enabled, Label, Options)\n\treturn CheckBox.Begin(Enabled, Label, Options)\nend\n\n--[[\n\tInput\n\n\tThis function will render an input box for a user to input text in. This widget behaves like input boxes\n\tfound in other applications. This function will only return true if it has focus and user has either input\n\ttext or pressed the return key.\n\n\tExample:\n\t\tlocal Text = \"Hello World\"\n\t\tif Slab.Input('Example', {Text = Text}) then\n\t\t\tText = Slab.GetInputText()\n\t\tend\n\n\tId: [String] A string that uniquely identifies this Input within the context of the window.\n\tOptions: [Table] List of options for how this Input will behave.\n\t\tTooltip: [String] Text to be displayed if the user hovers over the Input box.\n\t\tReturnOnText: [Boolean] Will cause this function to return true whenever the user has input\n\t\t\ta new character into the Input box. This is true by default.\n\t\tText: [String] The text to be supplied to the input box. It is recommended to use this option\n\t\t\twhen ReturnOnText is true.\n\t\tTextColor: [Table] The color to use for the text. The default color is the color used for text, but there is also\n\t\t\ta default multiline text color defined in the Style.\n\t\tBgColor: [Table] The background color for the input box.\n\t\tSelectColor: [Table] The color used when the user is selecting text within the input box.\n\t\tSelectOnFocus: [Boolean] When this input box is focused by the user, the text contents within the input\n\t\t\twill be selected. This is true by default.\n\t\tNumbersOnly: [Boolean] When true, only numeric characters and the '.' character are allowed to be input into\n\t\t\tthe input box. If no text is input, the input box will display '0'.\n\t\tW: [Number] The width of the input box. By default, will be 150.0\n\t\tH: [Number] The height of the input box. By default, will be the height of the current font.\n\t\tReadOnly: [Boolean] Whether this input field can be editable or not.\n\t\tAlign: [String] Aligns the text within the input box. Options are:\n\t\t\tleft: Aligns the text to the left. This will be set when this Input is focused.\n\t\t\tcenter: Aligns the text in the center. This is the default for when the text is not focused.\n\t\tRounding: [Number] Amount of rounding to apply to the corners of the input box.\n\t\tMinNumber: [Number] The minimum value that can be entered into this input box. Only valid when NumbersOnly is true.\n\t\tMaxNumber: [Number] The maximum value that can be entered into this input box. Only valid when NumbersOnly is true.\n\t\tMultiLine: [Boolean] Determines whether this input control should support multiple lines. If this is true, then the\n\t\t\tSelectOnFocus flag will be false. The given text will also be sanitized to remove controls characters such as\n\t\t\t'\\r'. Also, the text will be left aligned.\n\t\tMultiLineW: [Number] The width for which the lines of text should be wrapped at.\n\t\tHighlight: [Table] A list of key-values that define what words to highlight what color. Strings should be used for\n\t\t\tthe word to highlight and the value should be a table defining the color.\n\t\tStep: [Number] The step amount for numeric controls when the user click and drags. The default value is 1.0.\n\t\tNoDrag: [Boolean] Determines whether this numberic control allows the user to click and drag to alter the value.\n\t\tUseSlider: [Boolean] If enabled, displays a slider inside the input control. This will only be drawn if the NumbersOnly\n\t\t\toption is set to true. The position of the slider inside the control determines the value based on the MinNumber\n\t\t\tand MaxNumber option.\n\t\tIsPassword: [Boolean] If enabled, mask the text with another character. Default is false.\n\t\tPasswordChar: [Char/String] Sets the character or string to use along with IsPassword flag. Default is \"*\"\n\n\tReturn: [Boolean] Returns true if the user has pressed the return key while focused on this input box. If ReturnOnText\n\t\tis set to true, then this function will return true whenever the user has input any character into the input box.\n--]]\nfunction Slab.Input(Id, Options)\n\treturn Input.Begin(Id, Options)\nend\n\n--[[\n\tInputNumberDrag\n\n\tThis is a wrapper function for calling the Input function which sets the proper options to set up the input box for\n\tdisplaying and editing numbers. The user will be able to click and drag the control to alter the value. Double-clicking\n\tinside this control will allow for manually editing the value.\n\n\tId: [String] A string that uniquely identifies this Input within the context of the window.\n\tValue: [Number] The value to display in the control.\n\tMin: [Number] The minimum value that can be set for this number control. If nil, then this value will be set to -math.huge.\n\tMax: [Number] The maximum value that can be set for this number control. If nil, then this value will be set to math.huge.\n\tStep: [Number] The amount to increase value when mouse delta reaches threshold.\n\tOptions: [Table] List of options for how this input control is displayed. See Slab.Input for all options.\n\n\tReturn: [Boolean] Returns true whenever this valued is modified.\n--]]\nfunction Slab.InputNumberDrag(Id, Value, Min, Max, Step, Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.Text = tostring(Value)\n\tOptions.MinNumber = Min\n\tOptions.MaxNumber = Max\n\tOptions.Step = Step\n\tOptions.NumbersOnly = true\n\tOptions.UseSlider = false\n\tOptions.NoDrag = false\n\treturn Slab.Input(Id, Options)\nend\n\n--[[\n\tInputNumberSlider\n\n\tThis is a wrapper function for calling the Input function which sets the proper options to set up the input box for\n\tdisplaying and editing numbers. This will also force the control to display a slider, which determines what the value\n\tstored is based on the Min and Max options. Double-clicking inside this control will allow for manually editing\n\tthe value.\n\n\tId: [String] A string that uniquely identifies this Input within the context of the window.\n\tValue: [Number] The value to display in the control.\n\tMin: [Number] The minimum value that can be set for this number control. If nil, then this value will be set to -math.huge.\n\tMax: [Number] The maximum value that can be set for this number control. If nil, then this value will be set to math.huge.\n\tOptions: [Table] List of options for how this input control is displayed. See Slab.Input for all options.\n\t\tPrecision: [Number] An integer in the range [0..5]. This will set the size of the fractional component.\n\t\tNeedDrag: [Boolean] This will determine if slider needs to be dragged before changing value, otherwise just clicking in the slider will adjust the value into the clicked value. Default is true.\n\n\tReturn: [Boolean] Returns true whenever this valued is modified.\n--]]\nfunction Slab.InputNumberSlider(Id, Value, Min, Max, Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.Text = tostring(Value)\n\tOptions.MinNumber = Min\n\tOptions.MaxNumber = Max\n\tOptions.NumbersOnly = true\n\tOptions.UseSlider = true\n\treturn Slab.Input(Id, Options)\nend\n\n--[[\n\tGetInputText\n\n\tRetrieves the text entered into the focused input box. Refer to the documentation for Slab.Input for an example on how to\n\tuse this function.\n\n\tReturn: [String] Returns the text entered into the focused input box.\n--]]\nfunction Slab.GetInputText()\n\treturn Input.GetText()\nend\n\n--[[\n\tGetInputNumber\n\n\tRetrieves the text entered into the focused input box and attempts to conver the text into a number. Will always return a valid\n\tnumber.\n\n\tReturn: [Number] Returns the text entered into the focused input box as a number.\n--]]\nfunction Slab.GetInputNumber()\n\tlocal Result = tonumber(Input.GetText())\n\tif Result == nil then\n\t\tResult = 0\n\tend\n\treturn Result\nend\n\n--[[\n\tGetInputCursorPos\n\n\tRetrieves the position of the input cursor for the focused input control. There are three values that are returned. The first one\n\tis the absolute position of the cursor with regards to the text for the control. The second is the column position of the cursor\n\ton the current line. The final value is the line number. The column will match the absolute position if the input control is not\n\tmulti line.\n\n\tReturn: [Number], [Number], [Number] The absolute position of the cursor, the column position of the cursor on the current line,\n\t\tand the line number of the cursor. These values will all be zero if no input control is focused.\n--]]\nfunction Slab.GetInputCursorPos()\n\treturn Input.GetCursorPos()\nend\n\n--[[\n\tIsInputFocused\n\n\tReturns whether the input control with the given Id is focused or not.\n\n\tId: [String] The Id of the input control to check.\n\n\tReturn: [Boolean] True if the input control with the given Id is focused. False otherwise.\n--]]\nfunction Slab.IsInputFocused(Id)\n\treturn Input.IsFocused(Id)\nend\n\n--[[\n\tIsAnyInputFocused\n\n\tReturns whether any input control is focused or not.\n\n\tReturn: [Boolean] True if there is an input control focused. False otherwise.\n--]]\nfunction Slab.IsAnyInputFocused()\n\treturn Input.IsAnyFocused()\nend\n\n--[[\n\tSetInputFocus\n\n\tSets the focus of the input control to the control with the given Id. The focus is set at the beginning\n\tof the next frame to avoid any input events from the current frame.\n\n\tId: [String] The Id of the input control to focus.\n--]]\nfunction Slab.SetInputFocus(Id)\n\tInput.SetFocused(Id)\nend\n\n--[[\n\tSetInputCursorPos\n\n\tSets the absolute text position in bytes of the focused input control. This value is applied on the next frame.\n\tThis function can be combined with the SetInputFocus function to modify the cursor positioning of the desired\n\tinput control. Note that the input control supports UTF8 characters so if the desired position is not a valid\n\tcharacter, the position will be altered to find the next closest valid character.\n\n\tPos: [Number] The absolute position in bytes of the text of the focused input control.\n--]]\nfunction Slab.SetInputCursorPos(Pos)\n\tInput.SetCursorPos(Pos)\nend\n\n--[[\n\tSetInputCursorPosLine\n\n\tSets the column and line number of the focused input control. These values are applied on the next frame. This\n\tfunction behaves the same as SetInputCursorPos, but allows for setting the cursor by column and line.\n\n\tColumn: [Number] The text position in bytes of the current line.\n\tLine: [Number] The line number to set.\n--]]\nfunction Slab.SetInputCursorPosLine(Column, Line)\n\tInput.SetCursorPosLine(Column, Line)\nend\n\n--[[\n\tBeginTree\n\n\tThis function will render a tree item with an optional label. The tree can be expanded or collapsed based on whether\n\tthe user clicked on the tree item. This function can also be nested to create a hierarchy of tree items. This function\n\twill return false when collapsed and true when expanded. If this function returns true, Slab.EndTree must be called in\n\torder for this tree item to behave properly. The hot zone of this tree item will be the height of the label and the width\n\tof the window by default.\n\n\tId: [String/Table] A string or table uniquely identifying this tree item within the context of the window. If the given Id\n\t\tis a table, then the internal Tree entry for this table will be removed once the table has been garbage collected.\n\tOptions: [Table] List of options for how this tree item will behave.\n\t\tLabel: [String] The text to be rendered for this tree item.\n\t\tTooltip: [String] The text to be rendered when the user hovers over this tree item.\n\t\tIsLeaf: [Boolean] If this is true, this tree item will not be expandable/collapsable.\n\t\tOpenWithHighlight: [Boolean] If this is true, the tree will be expanded/collapsed when the user hovers over the hot\n\t\t\tzone of this tree item. If this is false, the user must click the expand/collapse icon to interact with this tree\n\t\t\titem.\n\t\tIcon: [Table] List of options to use for drawing the icon. Refer to the 'Image' documentation for more information.\n\t\tIsSelected: [Boolean] If true, will render a highlight rectangle around the tree item.\n\t\tIsOpen: [Boolean] Will force the tree item to be expanded.\n\t\tNoSavedSettings: [Boolean] Flag to disable saving this tree's settings to the state INI file.\n\n\tReturn: [Boolean] Returns true if this tree item is expanded. Slab.EndTree must be called if this returns true.\n--]]\nfunction Slab.BeginTree(Id, Options)\n\treturn Tree.Begin(Id, Options)\nend\n\n--[[\n\tEndTree\n\n\tFinishes up any BeginTree calls if those functions return true.\n\n\tReturn: None.\n--]]\nfunction Slab.EndTree()\n\tTree.End()\nend\n\n--[[\n\tBeginComboBox\n\n\tThis function renders a non-editable input field with a drop down arrow. When the user clicks this option, a window is\n\tcreated and the user can supply their own Slab.TextSelectable calls to add possible items to select from. This function\n\twill return true if the combo box is opened. Slab.EndComboBox must be called if this function returns true.\n\n\tExample:\n\t\tlocal Options = {\"Apple\", \"Banana\", \"Orange\", \"Pear\", \"Lemon\"}\n\t\tlocal Options_Selected = \"\"\n\t\tif Slab.BeginComboBox('Fruits', {Selected = Options_Selected}) then\n\t\t\tfor K, V in pairs(Options) do\n\t\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\t\tOptions_Selected = V\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tSlab.EndComboBox()\n\t\tend\n\n\tId: [String] A string that uniquely identifies this combo box within the context of the active window.\n\tOptions: [Table] List of options that control how this combo box behaves.\n\t\tTooltip: [String] Text that is rendered when the user hovers over this combo box.\n\t\tSelected: [String] Text that is displayed in the non-editable input box for this combo box.\n\t\tW: [Number] The width of the combo box. The default value is 150.0.\n\t\tRounding: [Number] Amount of rounding to apply to the corners of the combo box.\n\n\tReturn: [Boolean] This function will return true if the combo box is open.\n--]]\nfunction Slab.BeginComboBox(Id, Options)\n\treturn ComboBox.Begin(Id, Options)\nend\n\n--[[\n\tEndComboBox\n\n\tFinishes up any BeginComboBox calls if those functions return true.\n\n\tReturn: None.\n--]]\nfunction Slab.EndComboBox()\n\tComboBox.End()\nend\n\n--[[\n\tImage\n\n\tDraws an image at the current cursor position. The Id uniquely identifies this\n\timage to manage behaviors with this image. An image can be supplied through the\n\toptions or a path can be specified which Slab will manage the loading and storing of\n\tthe image reference.\n\n\tId: [String] A string uniquely identifying this image within the context of the current window.\n\tOptions: [Table] List of options controlling how the image should be drawn.\n\t\tImage: [Object] A user supplied image. This must be a valid Love image or the call will assert.\n\t\tPath: [String] If the Image option is nil, then a path must be specified. Slab will load and\n\t\t\tmanage the image resource.\n\t\tRotation: [Number] The rotation value to apply when this image is drawn.\n\t\tScale: [Number] The scale value to apply to both the X and Y axis.\n\t\tScaleX: [Number] The scale value to apply to the X axis.\n\t\tScaleY: [Number] The scale value to apply to the Y axis.\n\t\tColor: [Table] The color to use when rendering this image.\n\t\tSubX: [Number] The X-coordinate used inside the given image.\n\t\tSubY: [Number] The Y-coordinate used inside the given image.\n\t\tSubW: [Number] The width used inside the given image.\n\t\tSubH: [Number] The height used insided the given image.\n\t\tWrapX: [String] The horizontal wrapping mode for this image. The available options are 'clamp', 'repeat',\n\t\t\t'mirroredrepeat', and 'clampzero'. For more information refer to the Love2D documentation on wrap modes at\n\t\t\thttps://love2d.org/wiki/WrapMode.\n\t\tWrapY: [String] The vertical wrapping mode for this image. The available options are 'clamp', 'repeat',\n\t\t\t'mirroredrepeat', and 'clampzero'. For more information refer to the Love2D documentation on wrap modes at\n\t\t\thttps://love2d.org/wiki/WrapMode.\n\t\tUseOutline: [Boolean] If set to true, a rectangle will be drawn around the given image. If 'SubW' or 'SubH' are specified, these\n\t\t\tvalues will be used instead of the image's dimensions.\n\t\tOutlineColor: [Table] The color used to draw the outline. Default color is black.\n\t\tOutlineW: [Number] The width used for the outline. Default value is 1.\n\t\tW: [Number] The width the image should be resized to.\n\t\tH: [Number] The height the image should be resized to.\n\n\tReturn: None.\n--]]\nfunction Slab.Image(Id, Options)\n\tImage.Begin(Id, Options)\nend\n\n--[[\n\tSameLine\n\n\tThis forces the cursor to move back up to the same line as the previous widget. By default, all Slab widgets will\n\tadvance the cursor to the next line based on the height of the current line. By using this call with other widget\n\tcalls, the user will be able to set up multiple widgets on the same line to control how a window may look.\n\n\tOptions: [Table] List of options that controls how the cursor should handle the same line.\n\t\tPad: [Number] Extra padding to apply in the X direction.\n\t\tCenterY: [Boolean] Controls whether the cursor should be centered in the Y direction on the line. By default\n\t\t\tthe line will use the NewLineSize, which is the height of the current font to center the cursor.\n\n\tReturn: None.\n--]]\nfunction Slab.SameLine(Options)\n\tLayoutManager.SameLine(Options)\nend\n\n--[[\n\tNewLine\n\n\tThis forces the cursor to advance to the next line based on the height of the current font.\n\n\tCount: [Number] Specify how many new lines to insert, defaults to 1\n\n\tReturn: None.\n--]]\nfunction Slab.NewLine(Count)\n\tCount = Count or 1\n\tfor i = 1, Count do\n\t\tLayoutManager.NewLine()\n\tend\nend\n\n--[[\n\tSetCursorPos\n\n\tSets the cursor position. The default behavior is to set the cursor position relative to\n\tthe current window. The absolute position can be set if the 'Absolute' option is set.\n\n\tControls will only be drawn within a window. If the cursor is set outside of the current\n\twindow context, the control will not be displayed.\n\n\tX: [Number] The X coordinate to place the cursor. If nil, then the X coordinate is not modified.\n\tY: [Number] The Y coordinate to place the cursor. If nil, then the Y coordinate is not modified.\n\tOptions: [Table] List of options that control how the cursor position should be set.\n\t\tAbsolute: [Boolean] If true, will place the cursor using absolute coordinates.\n\n\tReturn: None.\n--]]\nfunction Slab.SetCursorPos(X, Y, Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.Absolute = Options.Absolute == nil and false or Options.Absolute\n\n\tif Options.Absolute then\n\t\tX = X == nil and Cursor.GetX() or X\n\t\tY = Y == nil and Cursor.GetY() or Y\n\t\tCursor.SetPosition(X, Y)\n\telse\n\t\tX = X == nil and Cursor.GetX() - Cursor.GetAnchorX() or X\n\t\tY = Y == nil and Cursor.GetY() - Cursor.GetAnchorY() or Y\n\t\tCursor.SetRelativePosition(X, Y)\n\tend\nend\n\n--[[\n\tGetCursorPos\n\n\tGets the cursor position. The default behavior is to get the cursor position relative to\n\tthe current window. The absolute position can be retrieved if the 'Absolute' option is set.\n\n\tOptions: [Table] List of options that control how the cursor position should be retrieved.\n\t\tAbsolute: [Boolean] If true, will return the cursor position in absolute coordinates.\n\n\tReturn: [Number], [Number] The X and Y coordinates of the cursor.\n--]]\nfunction Slab.GetCursorPos(Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.Absolute = Options.Absolute == nil and false or Options.Absolute\n\n\tlocal X, Y = Cursor.GetPosition()\n\n\tif not Options.Absolute then\n\t\tX = X - Cursor.GetAnchorX()\n\t\tY = Y - Cursor.GetAnchorY()\n\tend\n\n\treturn X, Y\nend\n\n--[[\n\tIndent\n\n\tAdvances the anchored X position of the cursor. All subsequent lines will begin at the new cursor position. This function\n\thas no effect when columns are present.\n\n\tWidth: [Number] How far in pixels to advance the cursor. If nil, then the default value identified by the 'Indent'\n\t\tproperty in the current style is used.\n\n\tReturn: None.\n--]]\nfunction Slab.Indent(Width)\n\tWidth = Width == nil and Style.Indent or Width\n\tCursor.Indent(Width)\nend\n\n--[[\n\tUnindent\n\n\tRetreats the anchored X position of the cursor. All subsequent lines will begin at the new cursor position. This function\n\thas no effect when columns are present.\n\n\tWidth: [Number] How far in pixels to retreat the cursor. If nil, then the default value identified by the 'Indent'\n\t\tproperty in the current style is used.\n\n\tReturn: None.\n--]]\nfunction Slab.Unindent(Width)\n\tWidth = Width == nil and Style.Indent or Width\n\tCursor.Unindent(Width)\nend\n\n--[[\n\tProperties\n\n\tIterates through the table's key-value pairs and adds them to the active window. This currently only does\n\ta shallow loop and will not iterate through nested tables.\n\n\tTODO: Iterate through nested tables.\n\n\tTable: [Table] The list of properties to build widgets for.\n\tOptions: [Table] List of options that can applied to a specific property. The key should match an entry in the\n\t\t'Table' argument and will apply any additional options to the property control.\n\tFallback: [Table] List of options that can be applied to any property if an entry was not found in the 'Options'\n\t\targument.\n\n\tReturn: None.\n--]]\nfunction Slab.Properties(Table, Options, Fallback)\n\tOptions = Options or {}\n\tFallback = Fallback or {}\n\n\tif Table ~= nil then\n\t\tfor I, T in ipairs(Table) do\n\t\t\tlocal V = T.Value\n\t\t\tlocal Type = type(V)\n\t\t\tlocal ID = T.ID\n\t\t\tlocal ItemOptions = Options[ID] or Fallback\n\t\t\tif Type == \"boolean\" then\n\t\t\t\tif Slab.CheckBox(V, ID, ItemOptions) then\n\t\t\t\t\tT.Value = not T.Value\n\t\t\t\tend\n\t\t\telseif Type == \"number\" then\n\t\t\t\tSlab.Text(ID .. \": \")\n\t\t\t\tSlab.SameLine()\n\t\t\t\tItemOptions.Text = V\n\t\t\t\tItemOptions.NumbersOnly = true\n\t\t\t\tItemOptions.ReturnOnText = false\n\t\t\t\tItemOptions.UseSlider = ItemOptions.MinNumber and ItemOptions.MaxNumber\n\t\t\t\tif Slab.Input(ID, ItemOptions) then\n\t\t\t\t\tT.Value = Slab.GetInputNumber()\n\t\t\t\tend\n\t\t\telseif Type == \"string\" then\n\t\t\t\tSlab.Text(ID .. \": \")\n\t\t\t\tSlab.SameLine()\n\t\t\t\tItemOptions.Text = V\n\t\t\t\tItemOptions.NumbersOnly = false\n\t\t\t\tItemOptions.ReturnOnText = false\n\t\t\t\tif Slab.Input(ID, ItemOptions) then\n\t\t\t\t\tT.Value = Slab.GetInputText()\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\n--[[\n\tBeginListBox\n\n\tBegins the process of creating a list box. If this function is called, EndListBox must be called after all\n\titems have been added.\n\n\tId: [String] A string uniquely identifying this list box within the context of the current window.\n\tOptions: [Table] List of options controlling the behavior of the list box.\n\t\tW: [Number] The width of the list box. If nil, a default value of 150 is used.\n\t\tH: [Number] The height of the list box. If nil, a default value of 150 is used.\n\t\tClear: [Boolean] Clears out the items in the list. It is recommended to only call this if the list items\n\t\t\thas changed and should not be set to true on every frame.\n\t\tRounding: [Number] Amount of rounding to apply to the corners of the list box.\n\t\tStretchW: [Boolean] Stretch the list box to fill the remaining width of the window.\n\t\tStretchH: [Boolean] Stretch the list box to fill the remaining height of the window.\n\n\tReturn: None.\n--]]\nfunction Slab.BeginListBox(Id, Options)\n\tListBox.Begin(Id, Options)\nend\n\n--[[\n\tEndListBox\n\n\tEnds the list box container. Will close off the region and properly adjust the cursor.\n\n\tReturn: None.\n--]]\nfunction Slab.EndListBox()\n\tListBox.End()\nend\n\n--[[\n\tBeginListBoxItem\n\n\tAdds an item to the current list box with the given Id. The user can then draw controls however they see\n\tfit to display a single item. This allows the user to draw list items such as a texture with a name or just\n\ta text to represent the item. If this is called, EndListBoxItem must be called to complete the item.\n\n\tId: [String] A string uniquely identifying this item within the context of the current list box.\n\tOptions: [Table] List of options that control the behavior of the active list item.\n\t\tSelected: [Boolean] If true, will draw the item with a selection background.\n\n\tReturn: None.\n--]]\nfunction Slab.BeginListBoxItem(Id, Options)\n\tListBox.BeginItem(Id, Options)\nend\n\n--[[\n\tIsListBoxItemClicked\n\n\tChecks to see if a hot list item is clicked. This should only be called within a BeginListBoxLitem/EndListBoxItem\n\tblock.\n\n\tButton: [Number] The button to check for the click of the item.\n\tIsDoubleClick: [Boolean] Check for double-click instead of single click.\n\n\tReturn: [Boolean] Returns true if the active item is hovered with mouse and the requested mouse button is clicked.\n--]]\nfunction Slab.IsListBoxItemClicked(Button, IsDoubleClick)\n\treturn ListBox.IsItemClicked(Button, IsDoubleClick)\nend\n\n--[[\n\tEndListBoxItem\n\n\tEnds the current item and commits the bounds of the item to the list.\n\n\tReturn: None.\n--]]\nfunction Slab.EndListBoxItem()\n\tListBox.EndItem()\nend\n\n--[[\n\tOpenDialog\n\n\tOpens the dialog box with the given Id. If the dialog box was opened, then it is pushed onto the stack.\n\tCalls to the BeginDialog with this same Id will return true if opened.\n\n\tId: [String] A string uniquely identifying this dialog box.\n\n\tReturn: None.\n--]]\nfunction Slab.OpenDialog(Id)\n\tDialog.Open(Id)\nend\n\n--[[\n\tBeginDialog\n\n\tBegins the dialog window with the given Id if it is open. If this function returns true, then EndDialog must be called.\n\tDialog boxes are windows which are centered in the center of the viewport. The dialog box cannot be moved and will\n\tcapture all input from all other windows.\n\n\tId: [String] A string uniquely identifying this dialog box.\n\tOptions: [Table] List of options that control how this dialog box behaves. These are the same parameters found\n\t\tfor BeginWindow, with some caveats. Certain options are overridden by the Dialog system. They are:\n\t\t\tX, Y, Layer, AllowFocus, AllowMove, and AutoSizeWindow.\n\n\tReturn: [Boolean] Returns true if the dialog with the given Id is open.\n--]]\nfunction Slab.BeginDialog(Id, Options)\n\treturn Dialog.Begin(Id, Options)\nend\n\n--[[\n\tEndDialog\n\n\tEnds the dialog window if a call to BeginDialog returns true.\n\n\tReturn: None.\n--]]\nfunction Slab.EndDialog()\n\tDialog.End()\nend\n\n--[[\n\tCloseDialog\n\n\tCloses the currently active dialog box.\n\n\tReturn: None.\n--]]\nfunction Slab.CloseDialog()\n\tDialog.Close()\nend\n\n--[[\n\tMessageBox\n\n\tOpens a message box to be displayed to the user with a title and a message. Buttons can be specified through the options\n\ttable which when clicked, the string of the button is returned. This function should be called every frame when a message\n\tbox wants to be displayed.\n\n\tTitle: [String] The title to display for the message box.\n\tMessage: [String] The message to be displayed. The text is aligned in the center. Multi-line strings are supported.\n\tOptions: [Table] List of options to control the behavior of the message box.\n\t\tButtons: [Table] List of buttons to display with the message box. The order of the buttons are displayed from right to left.\n\n\tReturn: [String] The name of the button that was clicked. If none was clicked, an emtpy string is returned.\n--]]\nfunction Slab.MessageBox(Title, Message, Options)\n\treturn Dialog.MessageBox(Title, Message, Options)\nend\n\n--[[\n\tFileDialog\n\n\tOpens up a dialog box that displays a file explorer for opening or saving files or directories. This function does not create any file\n\thandles, it just returns the list of files selected by the user.\n\n\tOptions: [Table] List of options that control the behavior of the file dialog.\n\t\tAllowMultiSelect: [Boolean] Allows the user to select multiple items in the file dialog.\n\t\tDirectory: [String] The starting directory when the file dialog is open. If none is specified, the dialog\n\t\t\twill start at love.filesystem.getSourceBaseDirectory and the dialog will remember the last\n\t\t\tdirectory navigated to by the user between calls to this function.\n\t\tType: [String] The type of file dialog to use. The options are:\n\t\t\topenfile: This is the default method. The user will have access to both directories and files. However,\n\t\t\t\tonly file selections are returned.\n\t\t\topendirectory: This type is used to filter the file dialog for directories only. No files will appear\n\t\t\t\tin the list.\n\t\t\tsavefile: This type is used to select a name of a file to save. The user will be prompted if they wish to overwrite\n\t\t\t\tan existing file.\n\t\tFilters: [Table] A list of filters the user can select from when browsing files. The table can contain tables or strings.\n\t\t\tTable: If a table is used for a filter, it should contain two elements. The first element is the filter while the second\n\t\t\t\telement is the description of the filter e.g. {\"*.lua\", \"Lua Files\"}\n\t\t\tString: If a raw string is used, then it should just be the filter. It is recommended to use the table option since a\n\t\t\t\tdescription can be given for each filter.\n\t\tIncludeParent: [Boolean] This option will include the parent '..' directory item in the file/dialog list. This option is\n\t\t\ttrue by default.\n\n\tReturn: [Table] Returns items for how the user interacted with this file dialog.\n\t\tButton: [String] The button the user clicked. Will either be OK or Cancel.\n\t\tFiles: [Table] An array of selected file items the user selected when OK is pressed. Will be empty otherwise.\n--]]\nfunction Slab.FileDialog(Options)\n\treturn Dialog.FileDialog(Options)\nend\n\n--[[\n\tColorPicker\n\n\tDisplays a window to allow the user to pick a hue and saturation value of a color. This should be called every frame and the result\n\tshould be handled to stop displaying the color picker and store the resulting color.\n\n\tOptions: [Table] List of options that control the behavior of the color picker.\n\t\tColor: [Table] The color to modify. This should be in the format of 0-1 for each color component (RGBA).\n\n\tReturn: [Table] Returns the button and color the user has selected.\n\t\tButton: [Number] The button the user clicked. 1 - OK. 0 - No Interaction. -1 - Cancel.\n\t\tColor: [Table] The new color the user has chosen. This will always be returned.\n--]]\nfunction Slab.ColorPicker(Options)\n\treturn ColorPicker.Begin(Options)\nend\n\n--[[\n\tIsMouseDown\n\n\tDetermines if a given mouse button is down.\n\n\tButton: [Number] The button to check for. The valid numbers are: 1 - Left, 2 - Right, 3 - Middle.\n\n\tReturn: [Boolean] True if the given button is down. False otherwise.\n--]]\nfunction Slab.IsMouseDown(Button)\n\treturn Mouse.IsDown(Button and Button or 1)\nend\n\n--[[\n\tIsMouseClicked\n\n\tDetermines if a given mouse button changes state from up to down this frame.\n\n\tButton: [Number] The button to check for. The valid numbers are: 1 - Left, 2 - Right, 3 - Middle.\n\n\tReturn: [Boolean] True if the given button changes state from up to down. False otherwise.\n--]]\nfunction Slab.IsMouseClicked(Button)\n\treturn Mouse.IsClicked(Button and Button or 1)\nend\n\n--[[\n\tIsMouseReleased\n\n\tDetermines if a given mouse button changes state from down to up this frame.\n\n\tButton: [Number] The button to check for. The valid numbers are: 1 - Left, 2 - Right, 3 - Middle.\n\n\tReturn: [Boolean] True if the given button changes state from down to up. False otherwise.\n--]]\nfunction Slab.IsMouseReleased(Button)\n\treturn Mouse.IsReleased(Button and Button or 1)\nend\n\n--[[\n\tIsMouseDoubleClicked\n\n\tDetermines if a given mouse button has been clicked twice within a given time frame.\n\n\tButton: [Number] The button to check for. The valid numbers are: 1 - Left, 2 - Right, 3 - Middle.\n\n\tReturn: [Boolean] True if the given button was double clicked. False otherwise.\n--]]\nfunction Slab.IsMouseDoubleClicked(Button)\n\treturn Mouse.IsDoubleClicked(Button and Button or 1)\nend\n\n--[[\n\tIsMouseDragging\n\n\tDetermines if a given mouse button is down and there has been movement.\n\n\tButton: [Number] The button to check for. The valid numbers are: 1 - Left, 2 - Right, 3 - Middle.\n\n\tReturn: [Boolean] True if the button is held down and is moving. False otherwise.\n--]]\nfunction Slab.IsMouseDragging(Button)\n\treturn Mouse.IsDragging(Button and Button or 1)\nend\n\n--[[\n\tGetMousePosition\n\n\tRetrieves the current mouse position in the viewport.\n\n\tReturn: [Number], [Number] The X and Y coordinates of the mouse position.\n--]]\nfunction Slab.GetMousePosition()\n\treturn Mouse.Position()\nend\n\n--[[\n\tGetMousePositionWindow\n\n\tRetrieves the current mouse position within the current window. This position will include any transformations\n\tadded to the window such as scrolling.\n\n\tReturn: [Number], [Number] The X and Y coordinates of the mouse position within the window.\n--]]\nfunction Slab.GetMousePositionWindow()\n\treturn Window.GetMousePosition()\nend\n\n--[[\n\tGetMouseDelta\n\n\tRetrieves the change in mouse coordinates from the last frame.\n\n\tReturn: [Number], [Number] The X and Y coordinates of the delta from the last frame.\n--]]\nfunction Slab.GetMouseDelta()\n\treturn Mouse.GetDelta()\nend\n\n--[[\n\tSetCustomMouseCursor\n\n\tOverrides a system mouse cursor of the given type to render a custom image instead.\n\n\tType: [String] The system cursor type to replace. This can be one of the following values: 'arrow', 'sizewe', 'sizens', 'sizenesw', 'sizenwse', 'ibeam', 'hand'.\n\tImage: [Table] An 'Image' object created from love.graphics.newImage. If this is nil, then an empty image is created and is drawn when the system cursor is activated.\n\tQuad: [Table] A 'Quad' object created from love.graphics.newQuad. This allows support for setting UVs of an image to render.\n--]]\nfunction Slab.SetCustomMouseCursor(Type, Image, Quad)\n\tMouse.SetCustomCursor(Type, Image, Quad)\nend\n\n--[[\n\tClearCustomMouseCursor\n\n\tRemoves any override of a system mouse cursor with the given type and defaults to the OS specific mouse cursor.\n\n\tType: [String] The system cursor type to remove. This can be one of the following values: 'arrow', 'sizewe', 'sizens', 'sizenesw', 'sizenwse', 'ibeam', 'hand'.\n--]]\nfunction Slab.ClearCustomMouseCursor(Type)\n\tMouse.ClearCustomCursor(Type)\nend\n\n--[[\n\tIsControlHovered\n\n\tChecks to see if the last control added to the window is hovered by the mouse.\n\n\tReturn: [Boolean] True if the last control is hovered, false otherwise.\n--]]\nfunction Slab.IsControlHovered()\n\t-- Prevent hovered checks on mobile if user is not dragging a touch.\n\tif Utility.IsMobile() and not Slab.IsMouseDown() then\n\t\treturn false\n\tend\n\n\tlocal Result = Window.IsItemHot()\n\n\tif not Result and not Window.IsObstructedAtMouse() then\n\t\tlocal X, Y = Slab.GetMousePositionWindow()\n\t\tResult = Cursor.IsInItemBounds(X, Y)\n\tend\n\n\treturn Result\nend\n\n--[[\n\tIsControlClicked\n\n\tChecks to see if the previous control is hovered and clicked.\n\n\tButton: [Number] The button to check for. The valid numbers are: 1 - Left, 2 - Right, 3 - Middle.\n\n\tReturn: [Boolean] True if the previous control is hovered and clicked. False otherwise.\n--]]\nfunction Slab.IsControlClicked(Button)\n\treturn Slab.IsControlHovered() and Slab.IsMouseClicked(Button)\nend\n\n--[[\n\tGetControlSize\n\n\tRetrieves the last declared control's size.\n\n\tReturn: [Number], [Number] The width and height of the last control declared.\n--]]\nfunction Slab.GetControlSize()\n\tlocal X, Y, W, H = Cursor.GetItemBounds()\n\treturn W, H\nend\n\n--[[\n\tIsVoidHovered\n\n\tChecks to see if any non-Slab area of the viewport is hovered.\n\n\tReturn: [Boolean] True if any non-Slab area of the viewport is hovered. False otherwise.\n--]]\nfunction Slab.IsVoidHovered()\n\t-- Prevent hovered checks on mobile if user is not dragging a touch.\n\tif Utility.IsMobile() and not Slab.IsMouseDown() then\n\t\treturn false\n\tend\n\n\treturn Region.GetHotInstanceId() == '' and not Region.IsScrolling()\nend\n\n--[[\n\tIsVoidClicked\n\n\tChecks to see if any non-Slab area of the viewport is clicked.\n\n\tButton: [Number] The button to check for. The valid numbers are: 1 - Left, 2 - Right, 3 - Middle.\n\n\tReturn: [Boolean] True if any non-Slab area of the viewport is clicked. False otherwise.\n--]]\nfunction Slab.IsVoidClicked(Button)\n\treturn Slab.IsMouseClicked(Button) and Slab.IsVoidHovered()\nend\n\n--[[\n\tIsKeyDown\n\n\tChecks to see if a specific key is held down. The key should be one of the love defined Scancode which the list can\n\tbe found at https://love2d.org/wiki/Scancode.\n\n\tKey: [String] A love defined key scancode.\n\n\tReturn: [Boolean] True if the key is held down. False otherwise.\n--]]\nfunction Slab.IsKeyDown(Key)\n\treturn Keyboard.IsDown(Key)\nend\n\n--[[\n\tIsKeyPressed\n\n\tChecks to see if a specific key state went from up to down this frame. The key should be one of the love defined Scancode which the list can\n\tbe found at https://love2d.org/wiki/Scancode.\n\n\tKey: [String] A love defined scancode.\n\n\tReturn: [Boolean] True if the key state went from up to down this frame. False otherwise.\n--]]\nfunction Slab.IsKeyPressed(Key)\n\treturn Keyboard.IsPressed(Key)\nend\n\n--[[\n\tIsKeyPressed\n\n\tChecks to see if a specific key state went from down to up this frame. The key should be one of the love defined Scancode which the list can\n\tbe found at https://love2d.org/wiki/Scancode.\n\n\tKey: [String] A love defined scancode.\n\n\tReturn: [Boolean] True if the key state went from down to up this frame. False otherwise.\n--]]\nfunction Slab.IsKeyReleased(Key)\n\treturn Keyboard.IsReleased(Key)\nend\n\n--[[\n\tRectangle\n\n\tDraws a rectangle at the current cursor position for the active window.\n\n\tOptions: [Table] List of options that control how this rectangle is displayed.\n\t\tMode: [String] Whether this rectangle should be filled or outlined. The default value is 'fill'.\n\t\tW: [Number] The width of the rectangle.\n\t\tH: [Number] The height of the rectangle.\n\t\tColor: [Table] The color to use for this rectangle.\n\t\tRounding: [Number] or [Table]\n\t\t\t[Number] Amount of rounding to apply to all corners.\n\t\t\t[Table] Define the rounding for each corner. The order goes top left, top right, bottom right, and bottom left.\n\t\tOutline: [Boolean] If the Mode option is 'fill', this option will allow an outline to be drawn.\n\t\tOutlineColor: [Table] The color to use for the outline if requested.\n\t\tSegments: [Number] Number of points to add for each corner if rounding is requested.\n\n\tReturn: None.\n--]]\nfunction Slab.Rectangle(Options)\n\tShape.Rectangle(Options)\nend\n\n--[[\n\tCircle\n\n\tDraws a circle at the current cursor position plus the radius for the active window.\n\n\tOptions: [Table] List of options that control how this circle is displayed.\n\t\tMode: [String] Whether this circle should be filled or outlined. The default value is 'fill'.\n\t\tRadius: [Number] The size of the circle.\n\t\tColor: [Table] The color to use for the circle.\n\t\tSegments: [Number] The number of segments used for drawing the circle.\n\n\tReturn: None.\n--]]\nfunction Slab.Circle(Options)\n\tShape.Circle(Options)\nend\n\n--[[\n\tTriangle\n\n\tDraws a triangle at the current cursor position plus the radius for the active window.\n\n\tOption: [Table] List of options that control how this triangle is displayed.\n\t\tMode: [String] Whether this triangle should be filled or outlined. The default value is 'fill'.\n\t\tRadius: [Number] The distance from the center of the triangle.\n\t\tRotation: [Number] The rotation of the triangle in degrees.\n\t\tColor: [Table] The color to use for the triangle.\n\n\tReturn: None.\n--]]\nfunction Slab.Triangle(Options)\n\tShape.Triangle(Options)\nend\n\n--[[\n\tLine\n\n\tDraws a line starting at the current cursor position and going to the defined points in this function.\n\n\tX2: [Number] The X coordinate for the destination.\n\tY2: [Number] The Y coordinate for the destination.\n\tOption: [Table] List of options that control how this line is displayed.\n\t\tWidth: [Number] How thick the line should be.\n\t\tColor: [Table] The color to use for the line.\n\n\tReturn: None.\n--]]\nfunction Slab.Line(X2, Y2, Options)\n\tShape.Line(X2, Y2, Options)\nend\n\n--[[\n\tCurve\n\n\tDraws a bezier curve with the given points as control points. The points should be defined in local space. Slab will translate the curve to the\n\tcurrent cursor position. There should two or more points defined for a proper curve.\n\n\tPoints: [Table] List of points to define the control points of the curve.\n\tOptions: [Table] List of options that control how this curve is displayed.\n\t\tColor: [Table] The color to use for this curve.\n\t\tDepth: [Number] The number of recursive subdivision steps to use when rendering the curve. If nil, the default LÖVE 2D value is used which is 5.\n\n\tReturn: None.\n--]]\nfunction Slab.Curve(Points, Options)\n\tShape.Curve(Points, Options)\nend\n\n--[[\n\tGetCurveControlPointCount\n\n\tReturns the number of control points defined with the last call to Curve.\n\n\tReturn: [Number] The number of control points defined for the previous curve.\n--]]\nfunction Slab.GetCurveControlPointCount()\n\treturn Shape.GetCurveControlPointCount()\nend\n\n--[[\n\tGetCurveControlPoint\n\n\tReturns the point for the given control point index. This point by default will be in local space defined by the points given in the Curve function.\n\tThe translated position can be requested by setting the LocalSpace option to false.\n\n\tIndex: [Number] The index of the control point to retrieve.\n\tOptions: [Table] A list of options that control what is returned by this function.\n\t\tLocalSpace: [Boolean] Returns either the translated or untranslated control point. This is true by default.\n\n\tReturn: [Number], [Number] The translated X, Y coordinates of the given control point.\n--]]\nfunction Slab.GetCurveControlPoint(Index, Options)\n\treturn Shape.GetCurveControlPoint(Index, Options)\nend\n\n--[[\n\tEvaluateCurve\n\n\tReturns the point at the given time. The time value should be between 0 and 1 inclusive. The point returned will be in local space. For the translated\n\tposition, set the LocalSpace option to false.\n\n\tTime: [Number] The time on the curve between 0 and 1.\n\tOptions: [Table] A list of options that control what is returned by this function.\n\t\tLocalSpace: [Boolean] Returnes either the translated or untranslated control point. This is true by default.\n\n\tReturn: [Number], [Number] The X and Y coordinates at the given time on the curve.\n--]]\nfunction Slab.EvaluateCurve(Time, Options)\n\treturn Shape.EvaluateCurve(Time, Options)\nend\n\n--[[\n\tEvaluateCurveMouse\n\n\tReturns the point on the curve at the given X-coordinate of the mouse relative to the end points of the curve.\n\n\tOptions: [Table] A list of options that control what is returned by this function.\n\t\tRefer to the documentation for EvaluateCurve for the list of options.\n\n\tReturn: [Number], [Number] The X and Y coordinates at the given X mouse position on the curve.\n--]]\nfunction Slab.EvaluateCurveMouse(Options)\n\tlocal X1, Y1 = Slab.GetCurveControlPoint(1, {LocalSpace = false})\n\tlocal X2, Y2 = Slab.GetCurveControlPoint(Slab.GetCurveControlPointCount(), {LocalSpace = false})\n\tlocal Left = math.min(X1, X2)\n\tlocal W = math.abs(X2 - X1)\n\tlocal X, Y = Slab.GetMousePositionWindow()\n\tlocal Offset = math.max(X - Left, 0.0)\n\tOffset = math.min(Offset, W)\n\n\treturn Slab.EvaluateCurve(Offset / W, Options)\nend\n\n--[[\n\tPolygon\n\n\tRenders a polygon with the given points. The points should be defined in local space. Slab will translate the position to the current cursor position.\n\n\tPoints: [Table] List of points that define this polygon.\n\tOptions: [Table] List of options that control how this polygon is drawn.\n\t\tColor: [Table] The color to render this polygon.\n\t\tMode: [String] Whether to use 'fill' or 'line' to draw this polygon. The default is 'fill'.\n\n\tReturn: None.\n--]]\nfunction Slab.Polygon(Points, Options)\n\tShape.Polygon(Points, Options)\nend\n\n--[[\n\tBeginStat\n\n\tStarts the timer for the specific stat in the given category.\n\n\tName: [String] The name of the stat to capture.\n\tCategory: [String] The category this stat belongs to.\n\n\tReturn: [Number] The handle identifying this stat capture.\n--]]\nfunction Slab.BeginStat(Name, Category)\n\treturn Stats.Begin(Name, Category)\nend\n\n--[[\n\tEndStat\n\n\tEnds the timer for the stat assigned to the given handle.\n\n\tHandle: [Number] The handle identifying a BeginStat call.\n\n\tReturn: None.\n--]]\nfunction Slab.EndStat(Handle)\n\tStats.End(Handle)\nend\n\n--[[\n\tEnableStats\n\n\tSets the enabled state of the stats system. The system is disabled by default.\n\n\tEnable: [Boolean] The new state of the states system.\n\n\tReturn: None.\n--]]\nfunction Slab.EnableStats(Enable)\n\tStats.SetEnabled(Enable)\nend\n\n--[[\n\tIsStatsEnabled\n\n\tQuery whether the stats system is enabled or disabled.\n\n\tReturn: [Boolean] Returns whether the stats system is enabled or disabled.\n--]]\nfunction Slab.IsStatsEnabled()\n\treturn Stats.IsEnabled()\nend\n\n--[[\n\tFlushStats\n\n\tResets the stats system to an empty state.\n\n\tReturn: None.\n--]]\nfunction Slab.FlushStats()\n\tStats.Flush()\nend\n\n--[[\n\tGetStats\n\n\tGet the love.graphics.getStats of the Slab.\n\tStats.SetEnabled(true) must be previously set to enable this.\n\tMust be called in love.draw (recommended at the end of draw)\n\n\tReturn: Table.\n--]]\n\nfunction Slab.GetStats()\n\treturn StatsData\nend\n\n--[[\n\tCalculateStats\n\n\tCalculate the passed love.graphics.getStats table of love by subtracting\n\tthe stats of Slab.\n\tStats.SetEnabled(true) must be previously set to enable this.\n\tMust be called in love.draw (recommended at the end of draw)\n\n\tReturn: Table.\n--]]\n\nfunction Slab.CalculateStats(LoveStats)\n\tfor k, v in pairs(LoveStats) do\n\t\tif StatsData[k] then\n\t\t\tLoveStats[k] = v - StatsData[k]\n\t\tend\n\tend\n\treturn LoveStats\nend\n\n--[[\n\tBeginLayout\n\n\tEnables the layout manager and positions the controls between this call and EndLayout based on the given options. The anchor\n\tposition for the layout is determined by the current cursor position on the Y axis. The horizontal position is not anchored.\n\tLayouts are stacked, so there can be layouts within parent layouts.\n\n\tId: [String] The Id of this layout.\n\tOptions: [Table] List of options that control how this layout behaves.\n\t\tAlignX: [String] Defines how the controls should be positioned horizontally in the window. The available options are\n\t\t\t'left', 'center', or 'right'. The default option is 'left'.\n\t\tAlignY: [String] Defines how the controls should be positioned vertically in the window. The available options are\n\t\t\t'top', 'center', or 'bottom'. The default option is 'top'. The top is determined by the current cursor position.\n\t\tAlignRowY: [String] Defines how the controls should be positioned vertically within a row. The available options are\n\t\t\t'top', 'center', or 'bottom'. The default option is 'top'.\n\t\tIgnore: [Boolean] Should this layout ignore positioning of controls. This is useful if certain controls need custom\n\t\t\tpositioning within a layout.\n\t\tExpandW: [Boolean] If true, will expand all controls' width within the row to the size of the window.\n\t\tExpandH: [Boolean] If true, will expand all controls' height within the row and the size of the window.\n\t\tAnchorX: [Boolean] Anchors the layout management at the current X cursor position. The size is calculated using this position.\n\t\t\tThe default value for this is false.\n\t\tAnchorY: [Boolean] Anchors the layout management at the current Y cursor position. The size is calculated using this position.\n\t\t\tThe default value for this is true.\n\t\tColumns: [Number] The number of columns to use for this layout. The default value is 1.\n\n\tReturn: None.\n--]]\nfunction Slab.BeginLayout(Id, Options)\n\tLayoutManager.Begin(Id, Options)\nend\n\n--[[\n\tEndLayout\n\n\tEnds the currently active layout. Each BeginLayout call must have a matching EndLayout. Failure to do so will result in\n\tan assertion.\n\n\tReturn: None.\n--]]\nfunction Slab.EndLayout()\n\tLayoutManager.End()\nend\n\n--[[\n\tSetLayoutColumn\n\n\tSets the current active column.\n\n\tIndex: [Number] The index of the column to be active.\n\n\tReturn: None.\n--]]\nfunction Slab.SetLayoutColumn(Index)\n\tLayoutManager.SetColumn(Index)\nend\n\n--[[\n\tGetLayoutSize\n\n\tRetrieves the size of the active layout. If there are columns, then the size of the column is returned.\n\n\tReturn: [Number], [Number] The width and height of the active layout. 0 is returned if no layout is active.\n--]]\nfunction Slab.GetLayoutSize()\n\treturn LayoutManager.GetActiveSize()\nend\n\n--[[\n\tGetCurrentColumnIndex\n\n\tRetrieves the current index of the active column.\n\n\tReturn: [Number] The current index of the active column of the active layout. 0 is returned if no layout or column is active.\n--]]\nfunction Slab.GetCurrentColumnIndex()\n\treturn LayoutManager.GetCurrentColumnIndex()\nend\n\n--[[\n\tSetScrollSpeed\n\n\tSets the speed of scrolling when using the mouse wheel.\n\n\tReturn: None.\n--]]\nfunction Slab.SetScrollSpeed(Speed)\n\tRegion.SetWheelSpeed(Speed)\nend\n\n--[[\n\tGetScrollSpeed\n\n\tRetrieves the speed of scrolling for the mouse wheel.\n\n\tReturn: [Number] The current wheel scroll speed.\n--]]\nfunction Slab.GetScrollSpeed()\n\treturn Region.GetWheelSpeed()\nend\n\n--[[\n\tPushShader\n\n\tPushes a shader effect to be applied to any following controls before a call to PopShader. Any shader effect that is still active\n\twill be cleared at the end of Slab's draw call.\n\n\tShader: [Object] The shader object created with the love.graphics.newShader function. This object should be managed by the caller.\n\n\tReturn: None.\n--]]\nfunction Slab.PushShader(Shader)\n\tDrawCommands.PushShader(Shader)\nend\n\n--[[\n\tPopShader\n\n\tPops the currently active shader effect. Will enable the next active shader on the stack. If none exists, no shader is applied.\n\n\tReturn: None.\n--]]\nfunction Slab.PopShader()\n\tDrawCommands.PopShader()\nend\n\n--[[\n\tEnableDocks\n\n\tEnables the docking functionality for a particular side of the viewport.\n\n\tList: [String/Table] A single item or list of items to enable for docking. The valid options are 'Left', 'Right', or 'Bottom'.\n\n\tReturn: None.\n--]]\nfunction Slab.EnableDocks(List)\n\tDock.Toggle(List, true)\nend\n\n--[[\n\tDisableDocks\n\n\tDisables the docking functionality for a particular side of the viewport.\n\n\tList: [String/Table] A single item or list of items to disable for docking. The valid options are 'Left', 'Right', or 'Bottom'.\n\n\tReturn: None.\n--]]\nfunction Slab.DisableDocks(List)\n\tDock.Toggle(List, false)\nend\n\n--[[\n\tSetDockOptions\n\n\tSet options for a dock type.\n\n\tType: [String] The type of dock to set options for. This can be 'Left', 'Right', or 'Bottom'.\n\tOptions: [Table] List of options that control how a dock behaves.\n\t\tNoSavedSettings: [Boolean] Flag to disable saving a dock's settings to the state INI file.\n--]]\nfunction Slab.SetDockOptions(Type, Options)\n\tDock.SetOptions(Type, Options)\nend\n\n--[[\n\tWindowToDoc\n\n\tProgramatically set a window to dock.\n\n\tType: [String] The type of dock to set options for. This can be 'Left', 'Right', or 'Bottom'.\n--]]\nfunction Slab.WindowToDock(Type)\n\tWindow.ToDock(Type)\nend\n\n--[[\n\tToLoveFile\n\n\tMoves a file to a temporary location and returns a Love2D friendly way to access the file. The returned string can be used in\n\tany Love2D function that takes a Filename\n\n\tSource: [String] An absolute path to a file on the disk, can take a value from FileDialog\n\n\tReturn: [String] A Love2D Filename\n]]\nfunction Slab.ToLoveFile(Source)\n\treturn FileSystem.ToLove(Source)\nend\n\nreturn Slab\n"
  },
  {
    "path": "Internal/Core/Config.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal FileSystem = require(SLAB_PATH .. '.Internal.Core.FileSystem')\n\nlocal Config = {}\nlocal DecodeValueFn = nil\nlocal Section = nil\n\nlocal function IsBasicType(Value)\n\tif Value ~= nil then\n\t\tlocal Type = type(Value)\n\n\t\treturn Type == \"number\" or Type == \"boolean\" or Type == \"string\"\n\tend\n\n\treturn false\nend\n\nlocal function IsArray(Table)\n\tif Table ~= nil and type(Table) == \"table\" then\n\t\tlocal N = 0\n\t\tfor K, V in pairs(Table) do\n\t\t\tif type(K) ~= \"number\" then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tif not IsBasicType(V) then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tN = N + 1\n\t\tend\n\n\t\treturn #Table == N\n\tend\n\n\treturn false\nend\n\nlocal function EncodeValue(Value)\n\tlocal Result = \"\"\n\n\tif Value ~= nil then\n\t\tlocal Type = type(Value)\n\t\tif Type == \"boolean\" then\n\t\t\tResult = Value == true and \"true\" or \"false\"\n\t\telseif Type == \"number\" or Type == \"string\" then\n\t\t\tResult = tostring(Value)\n\t\tend\n\tend\n\n\treturn Result\nend\n\nlocal function EncodePair(Key, Value)\n\tlocal Result = tostring(Key) .. \" = \"\n\n\tif Value ~= nil then\n\t\tif type(Value) == \"table\" then\n\t\t\tif IsArray(Value) then\n\t\t\t\tResult = Result .. \"(\" .. table.concat(Value, \",\") .. \")\\n\"\n\t\t\telse\n\t\t\t\tResult = Result .. \"{\"\n\t\t\t\tlocal First = true\n\t\t\t\tfor K, V in pairs(Value) do\n\t\t\t\t\tif not First then\n\t\t\t\t\t\tResult = Result .. \",\"\n\t\t\t\t\tend\n\t\t\t\t\tResult = Result .. K .. \"=\" .. EncodeValue(V)\n\t\t\t\t\tFirst = false\n\t\t\t\tend\n\t\t\t\tResult = Result .. \"}\\n\"\n\t\t\tend\n\t\telseif IsBasicType(Value) then\n\t\t\tResult = Result .. tostring(Value) .. \"\\n\"\n\t\tend\n\tend\n\n\treturn Result\nend\n\nlocal function EncodeSection(Section, Values)\n\tlocal Result = \"[\" .. Section .. \"]\\n\"\n\n\tfor K, V in pairs(Values) do\n\t\tResult = Result .. EncodePair(K, V)\n\tend\n\n\treturn Result .. \"\\n\"\nend\n\nlocal function DecodeBoolean(Value)\n\tlocal Lower = string.lower(Value)\n\n\tif Lower == \"true\" then\n\t\treturn true\n\telseif Lower == \"false\" then\n\t\treturn false\n\tend\n\n\treturn nil\nend\n\nlocal function DecodeArray(Value)\n\tlocal Result = nil\n\n\tif string.sub(Value, 1, 1) == \"(\" then\n\t\tResult = {}\n\t\tlocal Index = 1\n\t\tlocal Buffer = \"\"\n\n\t\twhile Index <= #Value do\n\t\t\tlocal Ch = string.sub(Value, Index, Index)\n\n\t\t\tif Ch == ',' or Ch == ')' then\n\t\t\t\tlocal Item = DecodeValueFn(Buffer)\n\t\t\t\tif Item ~= nil then\n\t\t\t\t\ttable.insert(Result, Item)\n\t\t\t\tend\n\t\t\t\tBuffer = \"\"\n\t\t\telseif Ch ~= \"(\" and Ch ~= \" \" then\n\t\t\t\tBuffer = Buffer .. Ch\n\t\t\tend\n\n\t\t\tIndex = Index + 1\n\t\tend\n\tend\n\n\treturn Result\nend\n\nlocal function DecodeTable(Value)\n\tlocal Result = nil\n\n\tif string.sub(Value, 1, 1) == \"{\" then\n\t\tResult = {}\n\t\tfor K, V in string.gmatch(Value, \"(%w+)=(%-?%w+)\") do\n\t\t\tResult[K] = DecodeValueFn(V)\n\t\tend\n\tend\n\n\treturn Result\nend\n\nlocal function DecodeValue(Value)\n\tif Value ~= nil and Value ~= \"\" then\n\t\tlocal Number = tonumber(Value)\n\t\tif Number ~= nil then\n\t\t\treturn Number\n\t\tend\n\n\t\tlocal Boolean = DecodeBoolean(Value)\n\t\tif Boolean ~= nil then\n\t\t\treturn Boolean\n\t\tend\n\n\t\tif Value == \"nil\" then\n\t\t\treturn nil\n\t\tend\n\n\t\tlocal Array = DecodeArray(Value)\n\t\tif Array ~= nil then\n\t\t\treturn Array\n\t\tend\n\n\t\tlocal Table = DecodeTable(Value)\n\t\tif Table ~= nil then\n\t\t\treturn Table\n\t\tend\n\n\t\treturn Value\n\tend\n\n\treturn nil\nend\n\nDecodeValueFn = DecodeValue\n\nlocal function DecodeLine(Line, Result)\n\tif string.sub(Line, 1, 1) == \";\" then\n\t\treturn\n\tend\n\n\tif string.sub(Line, 1, 1) == \"[\" and string.sub(Line, #Line, #Line) == \"]\" then\n\t\tlocal Key = string.sub(Line, 2, #Line - 1)\n\t\tResult[Key] = {}\n\t\tSection = Result[Key]\n\tend\n\n\tlocal Index = string.find(Line, \"=\", 1, true)\n\n\tif Index ~= nil then\n\t\tlocal Key = string.sub(Line, 1, Index - 1)\n\t\tKey = string.gsub(Key, \" \", \"\")\n\n\t\tlocal Value = string.sub(Line, Index + 1)\n\t\tValue = string.gsub(Value, \" \", \"\")\n\n\t\tif string.sub(Value, #Value, #Value) == \",\" then\n\t\t\tValue = string.sub(Value, 1, #Value - 1)\n\t\tend\n\n\t\tif Section ~= nil then\n\t\t\tSection[Key] = DecodeValue(Value)\n\t\telse\n\t\t\tResult[Key] = DecodeValue(Value)\n\t\tend\n\tend\nend\n\nfunction Config.Encode(Table)\n\tlocal Result = \"\"\n\n\tif type(Table) == \"table\" and not IsArray(Table) then\n\t\tlocal Sections = {}\n\t\tfor K, V in pairs(Table) do\n\t\t\tif type(V) == \"table\" and not IsArray(V) then\n\t\t\t\tSections[K] = V\n\t\t\telse\n\t\t\t\tResult = Result .. EncodePair(K, V)\n\t\t\tend\n\t\tend\n\n\t\tif string.len(Result) > 0 then\n\t\t\tResult = Result .. \"\\n\"\n\t\tend\n\n\t\tfor K, V in pairs(Sections) do\n\t\t\tResult = Result .. EncodeSection(K, V)\n\t\tend\n\tend\n\n\treturn Result\nend\n\nfunction Config.Decode(Stream)\n\tlocal Result = nil\n\tlocal Error = \"\"\n\n\tif Stream ~= nil then\n\t\tif type(Stream) == \"string\" then\n\t\t\tResult = {}\n\n\t\t\tlocal Start = 1\n\t\t\tlocal End = string.find(Stream, \"\\n\", Start, true)\n\t\t\tlocal Line = \"\"\n\n\t\t\twhile End ~= nil do\n\t\t\t\tLine = string.sub(Stream, Start, End - 1)\n\n\t\t\t\tDecodeLine(Line, Result)\n\n\t\t\t\tStart = End + 1\n\t\t\t\tEnd = string.find(Stream, \"\\n\", Start, true)\n\t\t\tend\n\n\t\t\tLine = string.sub(Stream, Start)\n\n\t\t\tDecodeLine(Line, Result)\n\t\telse\n\t\t\tError = \"Invalid type given for Stream. Type given is \" .. type(Stream) .. \".\"\n\t\tend\n\telse\n\t\tError = \"Invalid stream given to Config.Decode!\"\n\tend\n\n\treturn Result, Error\nend\n\nfunction Config.LoadFile(Path, IsDefault)\n\tlocal Result = nil\n\tlocal Contents, Error = FileSystem.ReadContents(Path, nil, IsDefault)\n\tif Contents ~= nil then\n\t\tResult, Error = Config.Decode(Contents)\n\tend\n\n\treturn Result, Error\nend\n\nfunction Config.Save(Path, Table, IsDefault)\n\tlocal Result, Error = false\n\tif Table ~= nil then\n\t\tlocal Contents = Config.Encode(Table)\n\t\tResult, Error = FileSystem.SaveContents(Path, Contents, IsDefault)\n\telse\n\t\tError = \"Invalid table given to Config.Save!\"\n\tend\n\n\treturn Result, Error\nend\n\nreturn Config\n"
  },
  {
    "path": "Internal/Core/Cursor.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Utility = require(SLAB_PATH .. '.Internal.Core.Utility')\n\nlocal Cursor = {}\n\nlocal min = math.min\nlocal max = math.max\n\nlocal State =\n{\n\tX = 0.0,\n\tY = 0.0,\n\tPrevX = 0.0,\n\tPrevY = 0.0,\n\tAnchorX = 0.0,\n\tAnchorY = 0.0,\n\tItemX = 0.0,\n\tItemY = 0.0,\n\tItemW = 0.0,\n\tItemH = 0.0,\n\tPadX = 4.0,\n\tPadY = 4.0,\n\tNewLineSize = 16.0,\n\tLineY = 0.0,\n\tLineH = 0.0,\n\tPrevLineY = 0.0,\n\tPrevLineH = 0.0\n}\n\nlocal Stack = {}\n\nfunction Cursor.SetPosition(X, Y)\n\tState.PrevX = State.X\n\tState.PrevY = State.Y\n\tState.X = X\n\tState.Y = Y\nend\n\nfunction Cursor.SetX(X)\n\tState.PrevX = State.X\n\tState.X = X\nend\n\nfunction Cursor.SetY(Y)\n\tState.PrevY = State.Y\n\tState.Y = Y\nend\n\nfunction Cursor.SetRelativePosition(X, Y)\n\tState.PrevX = State.X\n\tState.PrevY = State.Y\n\tState.X = State.AnchorX + X\n\tState.Y = State.AnchorY + Y\nend\n\nfunction Cursor.SetRelativeX(X)\n\tState.PrevX = State.X\n\tState.X = State.AnchorX + X\nend\n\nfunction Cursor.SetRelativeY(Y)\n\tState.PrevY = State.Y\n\tState.Y = State.AnchorY + Y\nend\n\nfunction Cursor.AdvanceX(X)\n\tState.PrevX = State.X\n\tState.X = State.X + X + State.PadX\nend\n\nfunction Cursor.AdvanceY(Y)\n\tState.X = State.AnchorX\n\tState.PrevY = State.Y\n\tState.Y = State.Y + Y + State.PadY\n\tState.PrevLineY = State.LineY\n\tState.PrevLineH = State.LineH\n\tState.LineY = 0.0\n\tState.LineH = 0.0\nend\n\nfunction Cursor.SetAnchor(X, Y)\n\tState.AnchorX = X\n\tState.AnchorY = Y\nend\n\nfunction Cursor.SetAnchorX(X)\n\tState.AnchorX = X\nend\n\nfunction Cursor.SetAnchorY(Y)\n\tState.AnchorY = Y\nend\n\nfunction Cursor.GetAnchor()\n\treturn State.AnchorX, State.AnchorY\nend\n\nfunction Cursor.GetAnchorX()\n\treturn State.AnchorX\nend\n\nfunction Cursor.GetAnchorY()\n\treturn State.AnchorY\nend\n\nfunction Cursor.GetPosition()\n\treturn State.X, State.Y\nend\n\nfunction Cursor.GetX()\n\treturn State.X\nend\n\nfunction Cursor.GetY()\n\treturn State.Y\nend\n\nfunction Cursor.GetRelativePosition()\n\treturn Cursor.GetRelativeX(), Cursor.GetRelativeY()\nend\n\nfunction Cursor.GetRelativeX()\n\treturn State.X - State.AnchorX\nend\n\nfunction Cursor.GetRelativeY()\n\treturn State.Y - State.AnchorY\nend\n\nfunction Cursor.SetItemBounds(X, Y, W, H)\n\tState.ItemX = X\n\tState.ItemY = Y\n\tState.ItemW = W\n\tState.ItemH = H\n\tif State.LineY == 0.0 then\n\t\tState.LineY = Y\n\tend\n\tState.LineY = min(State.LineY, Y)\n\tState.LineH = max(State.LineH, H)\nend\n\nfunction Cursor.GetItemBounds()\n\treturn State.ItemX, State.ItemY, State.ItemW, State.ItemH\nend\n\nfunction Cursor.IsInItemBounds(X, Y)\n\treturn State.ItemX <= X and X <= State.ItemX + State.ItemW and State.ItemY <= Y and Y <= State.ItemY + State.ItemH\nend\n\nfunction Cursor.SameLine(Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.Pad = Options.Pad == nil and 0.0 or Options.Pad\n\tOptions.CenterY = Options.CenterY == nil and false or Options.CenterY\n\n\tState.LineY = State.PrevLineY\n\tState.LineH = State.PrevLineH\n\tState.X = State.ItemX + State.ItemW + State.PadX + Options.Pad\n\tState.Y = State.PrevY\n\n\tif Options.CenterY then\n\t\tState.Y = State.Y + (State.LineH * 0.5) - (State.NewLineSize * 0.5)\n\tend\nend\n\nfunction Cursor.SetNewLineSize(NewLineSize)\n\tState.NewLineSize = NewLineSize\nend\n\nfunction Cursor.GetNewLineSize()\n\treturn State.NewLineSize\nend\n\nfunction Cursor.NewLine()\n\tCursor.AdvanceY(State.NewLineSize)\nend\n\nfunction Cursor.GetLineHeight()\n\treturn State.PrevLineH\nend\n\nfunction Cursor.PadX()\n\treturn State.PadX\nend\n\nfunction Cursor.PadY()\n\treturn State.PadY\nend\n\nfunction Cursor.Indent(Width)\n\tState.AnchorX = State.AnchorX + Width\n\tState.X = State.AnchorX\nend\n\nfunction Cursor.Unindent(Width)\n\tCursor.Indent(-Width)\nend\n\nfunction Cursor.PushContext()\n\ttable.insert(Stack, 1, Utility.Copy(State))\nend\n\nfunction Cursor.PopContext()\n\tif #Stack == 0 then\n\t\treturn\n\tend\n\n\tState = table.remove(Stack, 1)\nend\n\nreturn Cursor\n"
  },
  {
    "path": "Internal/Core/DrawCommands.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Stats = require(SLAB_PATH .. \".Internal.Core.Stats\")\nlocal TablePool = require(SLAB_PATH .. \".Internal.Core.TablePool\")\nlocal Scale = require(SLAB_PATH .. \".Internal.Core.Scale\")\n\nlocal insert = table.insert\nlocal remove = table.remove\nlocal sin = math.sin\nlocal cos = math.cos\nlocal rad = math.rad\nlocal max = math.max\nlocal min = math.min\nlocal graphics = love.graphics\n\nlocal DrawCommands = {}\n\nlocal PendingBatches = {}\nlocal ActiveBatch = nil\nlocal Shaders = {}\n\nlocal EMPTY = {}\nlocal BLACK = { 0, 0, 0, 1 }\nlocal WHITE = { 1, 1, 1, 1 }\n\nlocal TypeRect = 1\nlocal TypeTriangle = 2\nlocal TypeText = 3\nlocal TypeScissor = 4\nlocal TypeTransformPush = 5\nlocal TypeTransformPop = 6\nlocal TypeApplyTransform = 7\nlocal TypeCheck = 8\nlocal TypeLine = 9\nlocal TypeTextFormatted = 10\nlocal TypeIntersectScissor = 11\nlocal TypeCross = 12\nlocal TypeImage = 13\nlocal TypeSubImage = 14\nlocal TypeCircle = 15\nlocal TypeDrawCanvas = 16\nlocal TypeMesh = 17\nlocal TypeTextObject = 18\nlocal TypeCurve = 19\nlocal TypePolygon = 20\nlocal TypeShaderPush = 21\nlocal TypeShaderPop = 22\n\nlocal LayerNormal = 1\nlocal LayerDock = 2\nlocal LayerContextMenu = 3\nlocal LayerMainMenuBar = 4\nlocal LayerDialog = 5\nlocal LayerDebug = 6\nlocal LayerMouse = 7\n\nlocal LayerNames = {\n\tNormal = LayerNormal,\n\tDock = LayerDock,\n\tContextMenu = LayerContextMenu,\n\tMainMenuBar = LayerMainMenuBar,\n\tDialog = LayerDialog,\n\tDebug = LayerDebug,\n\tMouse = LayerMouse,\n}\n\nlocal LayerTable = { {}, {}, {}, {}, {}, {}, {} }\n\nlocal ActiveLayer = LayerNormal\nlocal StatsCategory = 'Slab Draw'\n\nlocal pool = {}\nfor i = TypeRect, TypeShaderPop do\n\tpool[i] = TablePool()\nend\n\nlocal function AddArc(Verts, CenterX, CenterY, Radius, Angle1, Angle2, Segments, X, Y)\n\tif Radius == 0 then\n\t\tinsert(Verts, CenterX + X)\n\t\tinsert(Verts, CenterY + Y)\n\t\treturn\n\tend\n\n\tlocal Step = (Angle2 - Angle1) / Segments\n\n\tfor Theta = Angle1, Angle2, Step do\n\t\tlocal Radians = rad(Theta)\n\t\tinsert(Verts, sin(Radians) * Radius + CenterX + X)\n\t\tinsert(Verts, cos(Radians) * Radius + CenterY + Y)\n\tend\nend\n\nlocal function DrawRect(Rect)\n\tlocal StatHandle = Stats.Begin('DrawRect', StatsCategory)\n\n\tlocal LineW = graphics.getLineWidth()\n\tgraphics.setLineWidth(Rect.LineW)\n\tgraphics.setColor(Rect.Color)\n\tlocal pixelOffset = Rect.Mode == 'line' and .5 or 0\n\tgraphics.rectangle(Rect.Mode, Rect.X + pixelOffset, Rect.Y + pixelOffset, Rect.Width, Rect.Height, Rect.Radius, Rect.Radius)\n\tgraphics.setLineWidth(LineW)\n\n\tStats.End(StatHandle)\nend\n\nlocal function GetTriangleVertices(X, Y, Radius, Rotation)\n\tlocal Radians = rad(Rotation)\n\n\tlocal cs, sn = cos(Radians), sin(Radians)\n\n\tlocal X1, Y1 = 0, -Radius\n\tlocal X2, Y2 = -Radius, Radius\n\tlocal X3, Y3 = Radius, Radius\n\n\tlocal PX1 = X1 * cs - Y1 * sn\n\tlocal PY1 = Y1 * cs + X1 * sn\n\n\tlocal PX2 = X2 * cs - Y2 * sn\n\tlocal PY2 = Y2 * cs + X2 * sn\n\n\tlocal PX3 = X3 * cs - Y3 * sn\n\tlocal PY3 = Y3 * cs + X3 * sn\n\n\treturn X + PX1, Y + PY1, X + PX2, Y + PY2, X + PX3, Y + PY3\nend\n\nlocal function DrawTriangle(Triangle)\n\tlocal StatHandle = Stats.Begin('DrawTriangle', StatsCategory)\n\n\tgraphics.setColor(Triangle.Color)\n\tgraphics.polygon(Triangle.Mode, GetTriangleVertices(Triangle.X, Triangle.Y, Triangle.Radius, Triangle.Rotation))\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawCheck(Check)\n\tlocal StatHandle = Stats.Begin('DrawCheck', StatsCategory)\n\n\tgraphics.setColor(Check.Color)\n\tgraphics.line(\n\t\tCheck.X - Check.Radius * 0.5, Check.Y,\n\t\tCheck.X, Check.Y + Check.Radius,\n\t\tCheck.X + Check.Radius, Check.Y - Check.Radius\n\t)\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawText(Text)\n\tlocal StatHandle = Stats.Begin('DrawText', StatsCategory)\n\n\tgraphics.setFont(Text.Font)\n\tgraphics.setColor(Text.Color)\n\tgraphics.print(Text.Text, Text.X, Text.Y)\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawTextFormatted(Text)\n\tlocal StatHandle = Stats.Begin('DrawTextFormatted', StatsCategory)\n\n\tgraphics.setFont(Text.Font)\n\tgraphics.setColor(Text.Color)\n\tgraphics.printf(Text.Text, Text.X, Text.Y, Text.W, Text.Align)\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawTextObject(Text)\n\tlocal StatHandle = Stats.Begin('DrawTextObject', StatsCategory)\n\n\tgraphics.setColor(1, 1, 1, 1)\n\tgraphics.draw(Text.Text, Text.X, Text.Y)\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawLine(Line)\n\tlocal StatHandle = Stats.Begin('DrawLine', StatsCategory)\n\n\tgraphics.setColor(Line.Color)\n\tlocal LineW = graphics.getLineWidth()\n\tgraphics.setLineWidth(Line.Width)\n\tgraphics.line(Line.X1, Line.Y1, Line.X2, Line.Y2)\n\tgraphics.setLineWidth(LineW)\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawCross(Cross)\n\tlocal StatHandle = Stats.Begin('DrawCross', StatsCategory)\n\n\tlocal X, Y = Cross.X, Cross.Y\n\tlocal R = Cross.Radius\n\tgraphics.setColor(Cross.Color)\n\tgraphics.line(X - R, Y - R, X + R, Y + R)\n\tgraphics.line(X - R, Y + R, X + R, Y - R)\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawImage(Image)\n\tlocal StatHandle = Stats.Begin('DrawImage', StatsCategory)\n\n\tgraphics.setColor(Image.Color)\n\tgraphics.draw(Image.Image, Image.X, Image.Y, Image.Rotation, Image.ScaleX, Image.ScaleY)\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawSubImage(Image)\n\tlocal StatHandle = Stats.Begin('DrawSubImage', StatsCategory)\n\n\tgraphics.setColor(Image.Color)\n\tgraphics.draw(Image.Image, Image.Quad, Image.Transform)\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawCircle(Circle)\n\tlocal StatHandle = Stats.Begin('DrawCircle', StatsCategory)\n\n\tgraphics.setColor(Circle.Color)\n\tgraphics.circle(Circle.Mode, Circle.X, Circle.Y, Circle.Radius, Circle.Segments)\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawCurve(Curve)\n\tlocal StatHandle = Stats.Begin('DrawCurve', StatsCategory)\n\n\tgraphics.setColor(Curve.Color)\n\tgraphics.line(Curve.Points)\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawPolygon(Polygon)\n\tlocal StatHandle = Stats.Begin('DrawPolygon', StatsCategory)\n\n\tgraphics.setColor(Polygon.Color)\n\tgraphics.polygon(Polygon.Mode, Polygon.Points)\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawCanvas(Canvas)\n\tlocal StatHandle = Stats.Begin('DrawCanvas', StatsCategory)\n\n\tgraphics.setBlendMode('alpha', 'premultiplied')\n\tgraphics.setColor(1.0, 1.0, 1.0, 1.0)\n\tgraphics.draw(Canvas.Canvas, Canvas.X, Canvas.Y)\n\tgraphics.setBlendMode('alpha')\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawMesh(Mesh)\n\tlocal StatHandle = Stats.Begin('DrawMesh', StatsCategory)\n\n\tgraphics.setColor(1.0, 1.0, 1.0, 1.0)\n\tgraphics.draw(Mesh.Mesh, Mesh.X, Mesh.Y)\n\n\tStats.End(StatHandle)\nend\n\nlocal function ShaderPush(shader)\n\tinsert(Shaders, 1, shader.Shader)\n\tgraphics.setShader(shader.Shader)\nend\n\nlocal function ShaderPop()\n\tremove(Shaders, 1)\n\tgraphics.setShader(Shaders[1])\nend\n\nlocal function SetScissor(rect)\n\tgraphics.setScissor(rect.X, rect.Y, rect.W, rect.H)\nend\n\nlocal function TransformPush()\n\tgraphics.push()\nend\n\nlocal function TransformPop()\n\tgraphics.pop()\nend\n\nlocal function ApplyTransform(transform)\n\tgraphics.applyTransform(transform.Transform)\nend\n\nlocal function IntersectScissor(rect)\n\tgraphics.intersectScissor(rect.X, rect.Y, rect.W, rect.H)\nend\n\nlocal DRAWTYPES = {\n\tDrawRect,\n\tDrawTriangle,\n\tDrawText,\n\tSetScissor,\n\tTransformPush,\n\tTransformPop,\n\tApplyTransform,\n\tDrawCheck,\n\tDrawLine,\n\tDrawTextFormatted,\n\tIntersectScissor,\n\tDrawCross,\n\tDrawImage,\n\tDrawSubImage,\n\tDrawCircle,\n\tDrawCanvas,\n\tDrawMesh,\n\tDrawTextObject,\n\tDrawCurve,\n\tDrawPolygon,\n\tShaderPush,\n\tShaderPop,\n}\n\nlocal function DrawElements(Elements)\n\tlocal StatHandle = Stats.Begin('Draw Elements', StatsCategory)\n\n\tfor i = 1, #Elements do\n\t\tlocal element = Elements[i]\n\t\tDRAWTYPES[element.Type](element)\n\tend\n\n\tStats.End(StatHandle)\nend\n\nlocal function DrawChannel(channel)\n\tfor i = 1, #channel do\n\t\tDrawElements(channel[i])\n\tend\nend\n\nlocal function ClearBatch(batch)\n\tfor i = 1, #batch do\n\t\tpool[batch[i].Type]:push(batch[i])\n\t\tbatch[i] = nil\n\tend\nend\n\nlocal function AssertActiveBatch()\n\tassert(ActiveBatch ~= nil, \"DrawCommands.Begin was not called before commands were issued!\")\nend\n\nlocal function DrawLayer(Layer, Name)\n\tif Layer == nil then\n\t\treturn\n\tend\n\n\tlocal StatHandle = Stats.Begin('Draw Layer ' .. Name, StatsCategory)\n\n\tlocal minChannel, maxChannel = 1e9, 0\n\tfor i in pairs(Layer) do\n\t\tminChannel, maxChannel = min(minChannel, i), max(maxChannel, i)\n\tend\n\n\tfor i = minChannel, maxChannel do\n\t\tif Layer[i] then\n\t\t\tDrawChannel(Layer[i])\n\t\tend\n\tend\n\n\tStats.End(StatHandle)\nend\n\nfunction DrawCommands.Reset()\n\tfor i = LayerNormal, LayerMouse do\n\t\tlocal layer = LayerTable[i]\n\t\tfor j, channel in pairs(layer) do\n\t\t\tfor i, batch in ipairs(channel) do\n\t\t\t\tClearBatch(batch)\n\t\t\tend\n\t\t\tlayer[j] = nil\n\t\tend\n\tend\n\n\tActiveLayer = LayerNormal\n\tActiveBatch = nil\n\tfor i in ipairs(Shaders) do\n\t\tShaders[i] = nil\n\tend\nend\n\nfunction DrawCommands.Begin(channel)\n\tlocal layer = LayerTable[ActiveLayer]\n\tchannel = channel or 1\n\n\tif layer[channel] == nil then\n\t\tlayer[channel] = {}\n\tend\n\n\tActiveBatch = {}\n\tinsert(layer[channel], ActiveBatch)\n\tinsert(PendingBatches, ActiveBatch)\nend\n\nfunction DrawCommands.End(clearElements)\n\tif ActiveBatch == nil then return end\n\n\tif clearElements then\n\t\tClearBatch(ActiveBatch)\n\tend\n\n\tgraphics.setScissor()\n\tremove(PendingBatches)\n\n\tActiveBatch = PendingBatches[#PendingBatches]\nend\n\nfunction DrawCommands.SetLayer(Layer)\n\tActiveLayer = LayerNames[Layer]\nend\n\nfunction DrawCommands.Rectangle(Mode, X, Y, Width, Height, Color, Radius, Segments, LineW)\n\tAssertActiveBatch()\n\tif type(Radius) == 'table' then\n\t\tSegments = Segments == nil and 10 or Segments\n\n\t\tlocal Verts = {}\n\t\tlocal TL = Radius[1]\n\t\tlocal TR = Radius[2]\n\t\tlocal BR = Radius[3]\n\t\tlocal BL = Radius[4]\n\n\t\tTL = TL == nil and 0 or TL\n\t\tTR = TR == nil and 0 or TR\n\t\tBR = BR == nil and 0 or BR\n\t\tBL = BL == nil and 0 or BL\n\n\t\tAddArc(Verts, Width - BR, Height - BR, BR, 0, 90, Segments, X, Y)\n\t\tAddArc(Verts, Width - TR, TR, TR, 90, 180, Segments, X, Y)\n\t\tAddArc(Verts, TL, TL, TL, 180, 270, Segments, X, Y)\n\t\tAddArc(Verts, BL, Height - BL, BL, 270, 360, Segments, X, Y)\n\n\t\tDrawCommands.Polygon(Mode, Verts, Color)\n\telse\n\t\tlocal Item = pool[TypeRect]:pull()\n\t\tItem.Type = TypeRect\n\t\tItem.Mode = Mode\n\t\tItem.X = X\n\t\tItem.Y = Y\n\t\tItem.Width = Width\n\t\tItem.Height = Height\n\t\tItem.Color = Color or BLACK\n\t\tItem.Radius = Radius or 0\n\t\tItem.LineW = LineW or graphics.getLineWidth()\n\t\tinsert(ActiveBatch, Item)\n\tend\nend\n\nfunction DrawCommands.Triangle(Mode, X, Y, Radius, Rotation, Color)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeTriangle]:pull()\n\tItem.Type = TypeTriangle\n\tItem.Mode = Mode\n\tItem.X = X\n\tItem.Y = Y\n\tItem.Radius = Radius\n\tItem.Rotation = Rotation\n\tItem.Color = Color or BLACK\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Print(Text, X, Y, Color, Font)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeText]:pull()\n\tItem.Type = TypeText\n\tItem.Text = Text\n\tItem.X = X\n\tItem.Y = Y\n\tItem.Color = Color or WHITE\n\tItem.Font = Font\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Printf(Text, X, Y, W, Align, Color, Font)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeTextFormatted]:pull()\n\tItem.Type = TypeTextFormatted\n\tItem.Text = Text\n\tItem.X = X\n\tItem.Y = Y\n\tItem.W = W\n\tItem.Align = Align or 'left'\n\tItem.Color = Color or WHITE\n\tItem.Font = Font\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Scissor(X, Y, W, H)\n\tAssertActiveBatch()\n\tif W ~= nil then\n\t\tW = max(W, 0.0)\n\tend\n\tif H ~= nil then\n\t\tH = max(H, 0.0)\n\tend\n\tlocal SF = Scale.GetScale()\n\tlocal Item = pool[TypeScissor]:pull()\n\tItem.Type = TypeScissor\n\tif X then X = X * SF end\n\tif Y then Y = Y * SF end\n\tif W then W = W * SF end\n\tif H then H = H * SF end\n\tItem.X = X\n\tItem.Y = Y\n\tItem.W = W\n\tItem.H = H\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.IntersectScissor(X, Y, W, H)\n\tAssertActiveBatch()\n\tif W ~= nil then\n\t\tW = max(W, 0.0)\n\tend\n\tif H ~= nil then\n\t\tH = max(H, 0.0)\n\tend\n\tlocal SF = Scale.GetScale()\n\tlocal Item = pool[TypeIntersectScissor]:pull()\n\tItem.Type = TypeIntersectScissor\n\tItem.X = (X or 0.0) * SF\n\tItem.Y = (Y or 0.0) * SF\n\tItem.W = (W or 0.0) * SF\n\tItem.H = (H or 0.0) * SF\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.TransformPush()\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeTransformPush]:pull()\n\tItem.Type = TypeTransformPush\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.TransformPop()\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeTransformPop]:pull()\n\tItem.Type = TypeTransformPop\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.ApplyTransform(Transform)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeApplyTransform]:pull()\n\tItem.Type = TypeApplyTransform\n\tItem.Transform = Transform\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Check(X, Y, Radius, Color)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeCheck]:pull()\n\tItem.Type = TypeCheck\n\tItem.X = X\n\tItem.Y = Y\n\tItem.Radius = Radius\n\tItem.Color = Color or BLACK\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Line(X1, Y1, X2, Y2, Width, Color)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeLine]:pull()\n\tItem.Type = TypeLine\n\tItem.X1 = X1\n\tItem.Y1 = Y1\n\tItem.X2 = X2\n\tItem.Y2 = Y2\n\tItem.Width = Width\n\tItem.Color = Color or BLACK\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Cross(X, Y, Radius, Color)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeCross]:pull()\n\tItem.Type = TypeCross\n\tItem.X = X\n\tItem.Y = Y\n\tItem.Radius = Radius\n\tItem.Color = Color or BLACK\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Image(X, Y, Image, Rotation, ScaleX, ScaleY, Color)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeImage]:pull()\n\tItem.Type = TypeImage\n\tItem.X = X\n\tItem.Y = Y\n\tItem.Image = Image\n\tItem.Rotation = Rotation\n\tItem.ScaleX = ScaleX\n\tItem.ScaleY = ScaleY\n\tItem.Color = Color or WHITE\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.SubImage(X, Y, Image, SX, SY, SW, SH, Rotation, ScaleX, ScaleY, Color)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeSubImage]:pull()\n\tItem.Type = TypeSubImage\n\tItem.Transform = love.math.newTransform(X, Y, Rotation, ScaleX, ScaleY)\n\tItem.Image = Image\n\tItem.Quad = graphics.newQuad(SX, SY, SW, SH, Image:getWidth(), Image:getHeight())\n\tItem.Color = Color or WHITE\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Circle(Mode, X, Y, Radius, Color, Segments)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeCircle]:pull()\n\tItem.Type = TypeCircle\n\tItem.Mode = Mode\n\tItem.X = X\n\tItem.Y = Y\n\tItem.Radius = Radius\n\tItem.Color = Color or BLACK\n\tItem.Segments = Segments and Segments or 24\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.DrawCanvas(Canvas, X, Y)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeDrawCanvas]:pull()\n\tItem.Type = TypeDrawCanvas\n\tItem.Canvas = Canvas\n\tItem.X = X\n\tItem.Y = Y\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Mesh(Mesh, X, Y)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeMesh]:pull()\n\tItem.Type = TypeMesh\n\tItem.Mesh = Mesh\n\tItem.X = X\n\tItem.Y = Y\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Text(Text, X, Y)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeTextObject]:pull()\n\tItem.Type = TypeTextObject\n\tItem.Text = Text\n\tItem.X = X\n\tItem.Y = Y\n\tItem.Color = BLACK\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Curve(Points, Color)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeCurve]:pull()\n\tItem.Type = TypeCurve\n\tItem.Points = Points\n\tItem.Color = Color or BLACK\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Polygon(Mode, Points, Color)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypePolygon]:pull()\n\tItem.Type = TypePolygon\n\tItem.Mode = Mode\n\tItem.Points = Points\n\tItem.Color = Color or BLACK\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.PushShader(Shader)\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeShaderPush]:pull()\n\tItem.Type = TypeShaderPush\n\tItem.Shader = Shader\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.PopShader()\n\tAssertActiveBatch()\n\tlocal Item = pool[TypeShaderPop]:pull()\n\tItem.Type = TypeShaderPop\n\tinsert(ActiveBatch, Item)\nend\n\nfunction DrawCommands.Execute()\n\tlocal StatHandle = Stats.Begin('Execute', StatsCategory)\n\n\tgraphics.scale(Scale.GetScale())\n\n\tDrawLayer(LayerTable[LayerNormal], 'Normal')\n\tDrawLayer(LayerTable[LayerDock], 'Dock')\n\tDrawLayer(LayerTable[LayerContextMenu], 'ContextMenu')\n\tDrawLayer(LayerTable[LayerMainMenuBar], 'MainMenuBar')\n\tDrawLayer(LayerTable[LayerDialog], 'Dialog')\n\tDrawLayer(LayerTable[LayerDebug], 'Debug')\n\tDrawLayer(LayerTable[LayerMouse], 'Mouse')\n\n\tgraphics.setShader()\n\n\tStats.End(StatHandle)\nend\n\nlocal function GetLayerDebugInfo(Layer)\n\tlocal Result = {}\n\n\tResult['Channel Count'] = #Layer\n\n\tlocal Channels = {}\n\tfor K, Channel in pairs(Layer) do\n\t\tlocal Collection = {}\n\t\tCollection['Batch Count'] = #Channel\n\t\tinsert(Channels, Collection)\n\tend\n\n\tResult['Channels'] = Channels\n\n\treturn Result\nend\n\nfunction DrawCommands.GetDebugInfo()\n\tlocal Result = {}\n\n\tResult['Normal'] = GetLayerDebugInfo(LayerTable[LayerNormal])\n\tResult['Dock'] = GetLayerDebugInfo(LayerTable[LayerDock])\n\tResult['ContextMenu'] = GetLayerDebugInfo(LayerTable[LayerContextMenu])\n\tResult['MainMenuBar'] = GetLayerDebugInfo(LayerTable[LayerMainMenuBar])\n\tResult['Dialog'] = GetLayerDebugInfo(LayerTable[LayerDialog])\n\tResult['Debug'] = GetLayerDebugInfo(LayerTable[LayerDebug])\n\tResult['Mouse'] = GetLayerDebugInfo(LayerTable[LayerMouse])\n\n\treturn Result\nend\n\nreturn DrawCommands\n"
  },
  {
    "path": "Internal/Core/FileSystem.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal FileSystem = {}\n\nlocal Syscalls = {}\nlocal Bit = require('bit')\nlocal FFI = require('ffi')\n\nlocal function ShouldFilter(Name, Filter)\n\tFilter = Filter == nil and \"*.*\" or Filter\n\n\tlocal Extension = FileSystem.GetExtension(Name)\n\n\tif Filter ~= \"*.*\" then\n\t\tlocal FilterExt = FileSystem.GetExtension(Filter)\n\n\t\tif Extension ~= FilterExt then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal GetDirectoryItems = nil\nlocal Exists = nil\nlocal IsDirectory = nil\nlocal Copy = nil\n\nlocal function Access(Table, Param)\n\treturn Table[Param];\nend\n\nlocal function ErrorAtAccess(Table, Param)\n\treturn not pcall(Access, Table, Param);\nend\n\n--[[\n\tThe following code is based on the following sources:\n\n\tLoveFS v1.1\n\thttps://github.com/linux-man/lovefs\n\tPure Lua FileSystem Access\n\tUnder the MIT license.\n\tcopyright(c) 2016 Caldas Lopes aka linux-man\n\n\tluapower/fs_posix\n\thttps://github.com/luapower/fs\n\tportable filesystem API for LuaJIT / Linux & OSX backend\n\tWritten by Cosmin Apreutesei. Public Domain.\n--]]\n\nif FFI.os == \"Windows\" then\n\tFFI.cdef[[\n\t\t#pragma pack(push)\n\t\t#pragma pack(1)\n\t\tstruct WIN32_FIND_DATAW {\n\t\t\tuint32_t dwFileAttributes;\n\t\t\tuint64_t ftCreationTime;\n\t\t\tuint64_t ftLastAccessTime;\n\t\t\tuint64_t ftLastWriteTime;\n\t\t\tuint32_t dwReserved[4];\n\t\t\twchar_t cFileName[520];\n\t\t\twchar_t cAlternateFileName[28];\n\t\t};\n\t\t#pragma pack(pop)\n\n\t\ttypedef unsigned long DWORD;\n\t\tstatic const DWORD FILE_ATTRIBUTE_DIRECTORY = 0x10;\n\t\tstatic const DWORD INVALID_FILE_ATTRIBUTES = -1;\n\n\t\tvoid* FindFirstFileW(const wchar_t* pattern, struct WIN32_FIND_DATAW* fd);\n\t\tbool FindNextFileW(void* ff, struct WIN32_FIND_DATAW* fd);\n\t\tbool FindClose(void* ff);\n\t\tDWORD GetFileAttributesW(const wchar_t* Path);\n\t\tbool CopyFileW(const wchar_t* src, const wchar_t* dst, bool bFailIfExists);\n\n\t\tint MultiByteToWideChar(unsigned int CodePage, uint32_t dwFlags, const char* lpMultiByteStr,\n\t\t\tint cbMultiByte, const wchar_t* lpWideCharStr, int cchWideChar);\n\t\tint WideCharToMultiByte(unsigned int CodePage, uint32_t dwFlags, const wchar_t* lpWideCharStr,\n\t\t\tint cchWideChar, const char* lpMultiByteStr, int cchMultiByte,\n\t\t\tconst char* default, int* used);\n\t]]\n\n\tlocal WIN32_FIND_DATA = FFI.typeof('struct WIN32_FIND_DATAW')\n\tlocal INVALID_HANDLE = FFI.cast('void*', -1)\n\n\tlocal function u2w(str, code)\n\t\tlocal size = FFI.C.MultiByteToWideChar(code or 65001, 0, str, #str, nil, 0)\n\t\tlocal buf = FFI.new(\"wchar_t[?]\", size * 2 + 2)\n\t\tFFI.C.MultiByteToWideChar(code or 65001, 0, str, #str, buf, size * 2)\n\t\treturn buf\n\tend\n\n\tlocal function w2u(wstr, code)\n\t\tlocal size = FFI.C.WideCharToMultiByte(code or 65001, 0, wstr, -1, nil, 0, nil, nil)\n\t\tlocal buf = FFI.new(\"char[?]\", size + 1)\n\t\tsize = FFI.C.WideCharToMultiByte(code or 65001, 0, wstr, -1, buf, size, nil, nil)\n\t\treturn FFI.string(buf)\n\tend\n\n\tGetDirectoryItems = function(Directory, Options)\n\t\tlocal Result = {}\n\n\t\tlocal FindData = FFI.new(WIN32_FIND_DATA)\n\t\tlocal Handle = FFI.C.FindFirstFileW(u2w(Directory .. \"\\\\*\"), FindData)\n\t\tFFI.gc(Handle, FFI.C.FindClose)\n\n\t\tif Handle ~= nil then\n\t\t\trepeat\n\t\t\t\tlocal Name = w2u(FindData.cFileName)\n\n\t\t\t\tif Name ~= \".\" and Name ~= \"..\" then\n\t\t\t\t\tlocal AddDirectory = (FindData.dwFileAttributes == 16 or FindData.dwFileAttributes == 17) and Options.Directories\n\t\t\t\t\tlocal AddFile = FindData.dwFileAttributes == 32 and Options.Files\n\n\t\t\t\t\tif (AddDirectory or AddFile) and not ShouldFilter(Name, Options.Filter) then\n\t\t\t\t\t\ttable.insert(Result, Name)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\tuntil not FFI.C.FindNextFileW(Handle, FindData)\n\t\tend\n\n\t\tFFI.C.FindClose(FFI.gc(Handle, nil))\n\n\t\treturn Result\n\tend\n\n\tExists = function(Path)\n\t\tlocal Attributes = FFI.C.GetFileAttributesW(u2w(Path))\n\t\treturn Attributes ~= FFI.C.INVALID_FILE_ATTRIBUTES\n\tend\n\n\tIsDirectory = function(Path)\n\t\tlocal Attributes = FFI.C.GetFileAttributesW(u2w(Path))\n\t\treturn Attributes ~= FFI.C.INVALID_FILE_ATTRIBUTES and Bit.band(Attributes, FFI.C.FILE_ATTRIBUTE_DIRECTORY) ~= 0\n\tend\n\n\tCopy = function(Source, Dest)\n\t\tFFI.C.CopyFileW(u2w(Source), u2w(Dest), false)\n\tend\nelse\n\tFFI.cdef[[\n\t\ttypedef struct DIR DIR;\n\t\ttypedef size_t time_t;\n\t\tstatic const int S_IFREG = 0x8000;\n\t\tstatic const int S_IFDIR = 0x4000;\n\n\t\tDIR* opendir(const char* name);\n\t\tint closedir(DIR* dirp);\n\t]]\n\n\tif FFI.os == \"OSX\" then\n\t\tFFI.cdef[[\n\t\t\tstruct dirent {\n\t\t\t\tuint64_t\td_ino;\n\t\t\t\tuint64_t\td_off;\n\t\t\t\tuint16_t\td_reclen;\n\t\t\t\tuint16_t\td_namlen;\n\t\t\t\tuint8_t\t\td_type;\n\t\t\t\tchar\t\td_name[1024];\n\t\t\t};\n\n\t\t\tstruct stat {\n\t\t\t\tuint32_t\tst_dev;\n\t\t\t\tuint16_t\tst_mode;\n\t\t\t\tuint16_t\tst_nlink;\n\t\t\t\tuint64_t\tst_ino;\n\t\t\t\tuint32_t\tst_uid;\n\t\t\t\tuint32_t\tst_gid;\n\t\t\t\tuint32_t\tst_rdev;\n\t\t\t\ttime_t\t\tst_atime;\n\t\t\t\tlong\t\tst_atime_nsec;\n\t\t\t\ttime_t\t\tst_mtime;\n\t\t\t\tlong\t\tst_mtime_nsec;\n\t\t\t\ttime_t\t\tst_ctime;\n\t\t\t\tlong\t\tst_ctime_nsec;\n\t\t\t\ttime_t\t\tst_btime;\n\t\t\t\tlong\t\tst_btime_nsec;\n\t\t\t\tint64_t\t\tst_size;\n\t\t\t\tint64_t\t\tst_blocks;\n\t\t\t\tint32_t\t\tst_blksize;\n\t\t\t\tuint32_t\tst_flags;\n\t\t\t\tuint32_t\tst_gen;\n\t\t\t\tint32_t\t\tst_lspare;\n\t\t\t\tint64_t\t\tst_qspare[2];\n\t\t\t};\n\n\t\t\tstruct dirent* readdir(DIR* dirp) asm(\"readdir$INODE64\");\n\t\t\tint stat64(const char* path, struct stat* buf);\n\t\t]]\n\telse\n\t\tFFI.cdef[[\n\t\t\tstruct dirent {\n\t\t\t\tuint64_t\t\td_ino;\n\t\t\t\tint64_t\t\t\td_off;\n\t\t\t\tunsigned short\td_reclen;\n\t\t\t\tunsigned char\td_type;\n\t\t\t\tchar\t\t\td_name[256];\n\t\t\t};\n\n\t\t\tstruct stat {\n\t\t\t\tuint64_t\tst_dev;\n\t\t\t\tuint64_t\tst_ino;\n\t\t\t\tuint64_t\tst_nlink;\n\t\t\t\tuint32_t\tst_mode;\n\t\t\t\tuint32_t\tst_uid;\n\t\t\t\tuint32_t\tst_gid;\n\t\t\t\tuint32_t\t__pad0;\n\t\t\t\tuint64_t\tst_rdev;\n\t\t\t\tint64_t\t\tst_size;\n\t\t\t\tint64_t\t\tst_blksize;\n\t\t\t\tint64_t\t\tst_blocks;\n\t\t\t\tuint64_t\tst_atime;\n\t\t\t\tuint64_t\tst_atime_nsec;\n\t\t\t\tuint64_t\tst_mtime;\n\t\t\t\tuint64_t\tst_mtime_nsec;\n\t\t\t\tuint64_t\tst_ctime;\n\t\t\t\tuint64_t\tst_ctime_nsec;\n\t\t\t\tint64_t\t\t__unused[3];\n\t\t\t};\n\n\t\t\tstruct dirent* readdir(DIR* dirp) asm(\"readdir64\");\n\t\t\tint syscall(int number, ...);\n\t\t\tint stat64(const char* path, struct stat* buf);\n\t\t]]\n\tend\n\n\tlocal Stat = FFI.typeof('struct stat');\n\n\tif FFI.arch == \"x86\" then\n\t\tSyscalls.SYS_stat = 106\n\telseif FFI.arch == \"arm\" and FFI.abi(\"eabi\") then\n\t\tSyscalls.SYS_stat = 106\n\telseif FFI.arch == \"x64\" then\n\t\tSyscalls.SYS_stat = 4\n\tend\n\n\n\tif(ErrorAtAccess(FFI.C, \"stat64\")) then\n\n\t\tlocal function SysStat(Path, Buffer)\n\t\t\treturn FFI.C.syscall(Syscalls.SYS_stat, Path, Buffer)\n\t\tend\n\n\t\tExists = function(Path)\n\t\t\tlocal Buffer = Stat()\n\t\t\treturn SysStat(Path, Buffer) == 0\n\t\tend\n\n\t\tIsDirectory = function(Path)\n\t\t\tlocal Buffer = Stat()\n\n\t\t\tif SysStat(Path, Buffer) == 0 then\n\t\t\t\treturn Bit.band(Buffer.st_mode, 0xf000) == FFI.C.S_IFDIR\n\t\t\tend\n\t\t\treturn false\n\t\tend\n\telse\n\t\tExists = function(Path)\n\t\t\tlocal Buffer = Stat()\n\t\t\treturn FFI.C.stat64(Path, Buffer) == 0\n\t\tend\n\n\t\tIsDirectory = function(Path)\n\t\t\tlocal Buffer = Stat()\n\n\t\t\tif FFI.C.stat64(Path, Buffer) == 0 then\n\t\t\t\treturn Bit.band(Buffer.st_mode, 0xf000) == FFI.C.S_IFDIR\n\t\t\tend\n\n\t\t\treturn false\n\t\tend\n\tend\n\n\n\tGetDirectoryItems = function(Directory, Options)\n\t\tlocal Result = {}\n\n\t\tlocal DIR = FFI.C.opendir(Directory)\n\n\t\tif DIR ~= nil then\n\t\t\tlocal Entry = FFI.C.readdir(DIR)\n\n\t\t\twhile Entry ~= nil do\n\t\t\t\tlocal Name = FFI.string(Entry.d_name)\n\n\t\t\t\tif Name ~= \".\" and Name ~= \"..\" and string.sub(Name, 1, 1) ~= \".\" then\n\t\t\t\t\tlocal AddDirectory = Entry.d_type == 4 and Options.Directories\n\t\t\t\t\tlocal AddFile = Entry.d_type == 8 and Options.Files\n\n\t\t\t\t\tif (AddDirectory or AddFile) and not ShouldFilter(Name, Options.Filter) then\n\t\t\t\t\t\ttable.insert(Result, Name)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tEntry = FFI.C.readdir(DIR)\n\t\t\tend\n\n\t\t\tFFI.C.closedir(DIR)\n\t\tend\n\n\t\treturn Result\n\tend\n\n\tCopy = function(Source, Dest)\n\t\tlocal inp = assert(io.open(Source, \"rb\"))\n\t\tlocal out = assert(io.open(Dest, \"wb\"))\n\t\tlocal data = inp:read(\"*all\")\n\t\tout:write(data)\n\t\tassert(out:close())\n\tend\n\nend\n\nfunction FileSystem.Separator()\n\t-- Lua/Love2D returns all paths with back slashes.\n\treturn \"/\"\nend\n\nfunction FileSystem.GetDirectoryItems(Directory, Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.Files = Options.Files == nil and true or Options.Files\n\tOptions.Directories = Options.Directories == nil and true or Options.Directories\n\tOptions.Filter = Options.Filter == nil and \"*.*\" or Options.Filter\n\n\tif string.sub(Directory, #Directory, #Directory) ~= FileSystem.Separator() then\n\t\tDirectory = Directory .. FileSystem.Separator()\n\tend\n\n\tlocal Result = GetDirectoryItems(Directory, Options)\n\n\ttable.sort(Result)\n\n\treturn Result\nend\n\nfunction FileSystem.Exists(Path)\n\treturn Exists(Path)\nend\n\nfunction FileSystem.IsDirectory(Path)\n\treturn IsDirectory(Path)\nend\n\nfunction FileSystem.Parent(Path)\n\tlocal Result = Path\n\n\tlocal Index = 1\n\tlocal I = Index\n\trepeat\n\t\tIndex = I\n\t\tI = string.find(Path, FileSystem.Separator(), Index + 1, true)\n\tuntil I == nil\n\n\tif Index > 1 then\n\t\tResult = string.sub(Path, 1, Index - 1)\n\tend\n\n\treturn Result\nend\n\n--[[\n\tIsAbsolute\n\n\tDetermines if the given path is an absolute path or a relative path. This is determined by checking if the\n\tpath starts with a drive letter on Windows, or the Unix root character '/'.\n\n\tPath: [String] The path to check.\n\n\tReturn: [Boolean] True if the path is absolute, false if it is relative.\n--]]\nfunction FileSystem.IsAbsolute(Path)\n\tif Path == nil or Path == \"\" then\n\t\treturn false\n\tend\n\n\tif FFI.os == \"Windows\" then\n\t\treturn string.match(Path, \"(.:-)\\\\\") ~= nil\n\tend\n\n\treturn string.sub(Path, 1, 1) == FileSystem.Separator()\nend\n\n--[[\n\tGetDrive\n\n\tAttempts to retrieve the drive letter from the given absolute path. This function is targeted for\n\tpaths on Windows. Unix style paths will just return the root '/'.\n\n\tPath: [String] The absolute path containing the drive letter.\n\n\tReturn: [String] The drive letter, colon, and path separator are returned. On Unix platforms, just the '/'\n\t\tcharacter is returned.\n--]]\nfunction FileSystem.GetDrive(Path)\n\tif not FileSystem.IsAbsolute(Path) then\n\t\treturn \"\"\n\tend\n\n\tif FFI.os == \"Windows\" then\n\t\tlocal Result = string.match(Path, \"(.:-)\\\\\")\n\n\t\tif Result == nil then\n\t\t\tResult = string.match(Path, \"(.:-)\" .. FileSystem.Separator())\n\t\tend\n\n\t\tif Result ~= nil then\n\t\t\treturn Result .. FileSystem.Separator()\n\t\tend\n\tend\n\n\treturn FileSystem.Separator()\nend\n\n--[[\n\tDetermines if the given path is a drive letter on Windows or the root directory on Unix.\n\n\tPath: [String] The absolute path containing the drive letter.\n\n\tReturn: [Boolean] True if the given path is a drive.\n--]]\nfunction FileSystem.IsDrive(Path)\n\tif Path == nil then\n\t\treturn false\n\tend\n\n\treturn FileSystem.GetDrive(Path) == Path\nend\n\n--[[\n\tSanitize\n\n\tThis function will attempt to remove any '.' or '..' components in the path and will appropriately modify\n\tthe result to represent changes to the path based on if a '..' component is found. This function will keep\n\tthe path's scope (relative/absolute) during sanitization.\n\n\tPath: [String] The path to be sanitized.\n\n\tReturn: [String] The sanitized path string.\n--]]\nfunction FileSystem.Sanitize(Path)\n\tlocal Result = \"\"\n\n\tlocal Items = {}\n\tfor Item in string.gmatch(Path, \"([^\" .. FileSystem.Separator() .. \"]+)\") do\n\t\t-- Always add the first item. If the given path is relative, then this will help preserve that.\n\t\tif #Items == 0 then\n\t\t\ttable.insert(Items, Item)\n\t\telse\n\t\t\t-- If the parent directory item is found, pop the last item off of the stack.\n\t\t\tif Item == \"..\" then\n\t\t\t\ttable.remove(Items, #Items)\n\t\t\t-- Ignore same directory item and push the item to the stack.\n\t\t\telseif Item ~= \".\" then\n\t\t\t\ttable.insert(Items, Item)\n\t\t\tend\n\t\tend\n\tend\n\n\tfor I, Item in ipairs(Items) do\n\t\tif Result == \"\" then\n\t\t\tif Item == \".\" or Item == \"..\" then\n\t\t\t\tResult = Item\n\t\t\telse\n\t\t\t\tif FileSystem.IsAbsolute(Path) then\n\t\t\t\t\tResult = FileSystem.GetDrive(Path) .. Item\n\t\t\t\telse\n\t\t\t\t\tResult = Item\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\tResult = Result .. FileSystem.Separator() .. Item\n\t\tend\n\tend\n\n\treturn Result\nend\n\nfunction FileSystem.GetBaseName(Path, RemoveExtension)\n\tlocal Result = string.match(Path, \"^.+/(.+)$\")\n\n\tif Result == nil then\n\t\tResult = Path\n\tend\n\n\tif RemoveExtension then\n\t\tResult = FileSystem.RemoveExtension(Result)\n\tend\n\n\treturn Result\nend\n\nfunction FileSystem.GetDirectory(Path)\n\tlocal Result = string.match(Path, \"(.+)/\")\n\n\tif Result == nil then\n\t\tResult = Path\n\tend\n\n\treturn Result\nend\n\nfunction FileSystem.GetRootDirectory(Path)\n\tlocal Result = Path\n\n\tlocal Index = string.find(Path, FileSystem.Separator(), 1, true)\n\n\tif Index ~= nil then\n\t\tResult = string.sub(Path, 1, Index - 1)\n\tend\n\n\treturn Result\nend\n\nfunction FileSystem.GetSlabPath()\n\tlocal Path = love.filesystem.getSource()\n\tif not FileSystem.IsDirectory(Path) then\n\t\tPath = love.filesystem.getSourceBaseDirectory()\n\tend\n\treturn Path .. \"/Slab\"\nend\n\nfunction FileSystem.GetExtension(Path)\n\tlocal Result = string.match(Path, \"[^.]+$\")\n\n\tif Result == nil then\n\t\tResult = \"\"\n\tend\n\n\treturn Result\nend\n\nfunction FileSystem.RemoveExtension(Path)\n\tlocal Result = string.match(Path, \"(.+)%.\")\n\n\tif Result == nil then\n\t\tResult = Path\n\tend\n\n\treturn Result\nend\n\nfunction FileSystem.ReadContents(Path, IsBinary, IsDefault)\n\tlocal Result, Error\n\n\tif IsDefault then\n\t\tResult, Error = love.filesystem.read(Path)\n\telse\n\t\tlocal Handle\n\t\tlocal Mode = IsBinary and \"rb\" or \"r\"\n\t\tHandle, Error = io.open(Path, Mode)\n\t\tif Handle ~= nil then\n\t\t\tResult = Handle:read(\"*a\")\n\t\t\tHandle:close()\n\t\tend\n\tend\n\n\treturn Result, Error\nend\n\nfunction FileSystem.SaveContents(Path, Contents, IsDefault)\n\tlocal Result, Error\n\n\tif IsDefault then\n\t\tResult, Error = love.filesystem.write(Path, Contents)\n\telse\n\t\tlocal Handle, Error = io.open(Path, \"w\")\n\t\tif Handle ~= nil then\n\t\t\tHandle:write(Contents)\n\t\t\tHandle:close()\n\t\t\tResult = true\n\t\tend\n\tend\n\n\treturn Result, Error\nend\n\nfunction FileSystem.GetClipboard()\n\tlocal Contents = love.system.getClipboardText()\n\n\tif Contents ~= nil then\n\t\t-- Remove Windows style newlines.\n\t\tContents = string.gsub(Contents, \"\\r\\n\", \"\\n\")\n\tend\n\n\treturn Contents\nend\n\nfunction FileSystem.ToLove(Source)\n\tlocal ext = FileSystem.GetExtension(Source)\n\n\tlove.filesystem.createDirectory('tmp')\n\tCopy(Source, love.filesystem.getSaveDirectory() .. \"/tmp/temp.\" .. ext)\n\n\treturn \"tmp/temp.\" .. ext\nend\n\nreturn FileSystem\n"
  },
  {
    "path": "Internal/Core/IdCache.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal IdCache = {}\nIdCache.__index = IdCache\n\nfunction IdCache:get(parentId, id)\n\tlocal pId = self._ids[parentId]\n\tlocal resultId = pId and pId[id]\n\n\tif resultId then return resultId end\n\n\tresultId = (\"%s.%s\"):format(parentId, id)\n\tif pId then\n\t\tpId[id] = resultId\n\telse\n\t\tself._ids[parentId] = { [id] = resultId }\n\tend\n\n\treturn resultId\nend\n\nreturn function()\n\treturn setmetatable({\n\t\t_ids = {}\n\t}, IdCache)\nend\n"
  },
  {
    "path": "Internal/Core/Messages.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\n--[[\n\tThe messages system is a system for Slab to notify developers of issues or suggestions on aspects\n\tof the API. Functions or options may be deprecated or Slab may offer an alternative usage. The system\n\tis designed to notify the user only once to prevent any repeated output to the console if enabled. This\n\tsystem can be enabled at startup and the developer will have the ability to gather the messages to\n\tbe displayed in a control if desired.\n--]]\n\nlocal insert = table.insert\n\nlocal Messages = {}\n\nlocal Enabled = true\nlocal Cache = {}\n\nfunction Messages.Broadcast(Id, Message)\n\tif not Enabled then\n\t\treturn\n\tend\n\n\tassert(Id ~= nil, \"Id is invalid.\")\n\tassert(type(Id) == 'string', \"Id is not a string type.\")\n\tassert(Message ~= nil, \"Message is invalid.\")\n\tassert(type(Message) == 'string', \"Message is not a string type.\")\n\n\tif Cache[Id] == nil then\n\t\tCache[Id] = Message\n\t\tprint(Message)\n\tend\nend\n\nfunction Messages.Get()\n\tlocal Result = {}\n\n\tfor K, V in pairs(Cache) do\n\t\tinsert(Result, V)\n\tend\n\n\treturn Result\nend\n\nfunction Messages.SetEnabled(InEnabled)\n\tEnabled = InEnabled\nend\n\nreturn Messages\n"
  },
  {
    "path": "Internal/Core/Scale.lua",
    "content": "\n--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\n\nlocal CurrentScale = 1\n\n\nlocal Scale = {}\n\nfunction Scale.SetScale(newScale)\n\tassert(type(newScale) == \"number\", \"Scale needs to be a number!\")\n\tCurrentScale = newScale\nend\n\nfunction Scale.GetScale()\n\treturn CurrentScale or 1\nend\n\nfunction Scale.GetScreenWidth()\n\treturn love.graphics.getWidth() / Scale.GetScale()\nend\n\nfunction Scale.GetScreenHeight()\n\treturn love.graphics.getHeight() / Scale.GetScale()\nend\n\nfunction Scale.GetScreenDimensions()\n\treturn Scale.GetScreenWidth(), Scale.GetScreenHeight()\nend\n\n\nreturn Scale\n\n"
  },
  {
    "path": "Internal/Core/Stats.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Stats = {}\n\nlocal insert = table.insert\nlocal max = math.max\n\nlocal Data = {}\nlocal Pending = {}\nlocal Enabled = false\nlocal QueueEnabled = false\nlocal QueueDisable = false\nlocal Id = 1\nlocal QueueFlush = false\nlocal FrameNumber = 0\n\nlocal function GetCategory(Category)\n\tassert(Category ~= nil, \"Nil category given to Stats system.\")\n\tassert(Category ~= '', \"Empty category given to Stats system.\")\n\tassert(type(Category) == 'string', \"Category given is not of type string. Type given is '\" .. type(Category) .. \"'.\")\n\n\tif Data[Category] == nil then\n\t\tData[Category] = {}\n\tend\n\n\treturn Data[Category]\nend\n\nlocal function ResetCategory(Category)\n\tlocal Instance = Data[Category]\n\n\tif Instance ~= nil then\n\t\tfor K, V in pairs(Instance) do\n\t\t\tV.LastTime = V.Time\n\t\t\tV.LastCallCount = V.CallCount\n\t\t\tV.MaxTime = max(V.MaxTime, V.Time)\n\t\t\tV.Time = 0.0\n\t\t\tV.CallCount = 0\n\t\tend\n\tend\nend\n\nlocal function GetItem(Name, Category)\n\tassert(Name ~= nil, \"Nil name given to Stats system.\")\n\tassert(Name ~= '', \"Empty name given to Stats system.\")\n\n\tlocal Cat = GetCategory(Category)\n\n\tif Cat[Name] == nil then\n\t\tlocal Instance = {}\n\t\tInstance.Time = 0.0\n\t\tInstance.MaxTime = 0.0\n\t\tInstance.CallCount = 0\n\t\tInstance.LastTime = 0.0\n\t\tInstance.LastCallCount = 0.0\n\t\tCat[Name] = Instance\n\tend\n\n\treturn Cat[Name]\nend\n\nfunction Stats.Begin(Name, Category)\n\tif not Enabled then\n\t\treturn\n\tend\n\n\tlocal Handle = Id\n\tId = Id + 1\n\n\tlocal Instance = {StartTime = love.timer.getTime(), Name = Name, Category = Category}\n\tPending[Handle] = Instance\n\n\treturn Handle\nend\n\nfunction Stats.End(Handle)\n\tif not Enabled then\n\t\treturn\n\tend\n\n\tassert(Handle ~= nil, \"Nil handle given to Stats.End.\")\n\n\tlocal Instance = Pending[Handle]\n\tassert(Instance ~= nil, \"Invalid handle given to Stats.End.\")\n\tPending[Handle] = nil\n\n\tlocal Elapsed = love.timer.getTime() - Instance.StartTime\n\n\tlocal Item = GetItem(Instance.Name, Instance.Category)\n\tItem.CallCount = Item.CallCount + 1\n\tItem.Time = Item.Time + Elapsed\nend\n\nfunction Stats.GetTime(Name, Category)\n\tif not Enabled then\n\t\treturn 0.0\n\tend\n\n\tlocal Item = GetItem(Name, Category)\n\n\treturn Item.Time > 0.0 and Item.Time or Item.LastTime\nend\n\nfunction Stats.GetMaxTime(Name, Category)\n\tif not Enabled then\n\t\treturn 0.0\n\tend\n\n\tlocal Item = GetItem(Name, Category)\n\n\treturn Item.MaxTime\nend\n\nfunction Stats.GetCallCount(Name, Category)\n\tif not Enabled then\n\t\treturn 0\n\tend\n\n\tlocal Item = GetItem(Name, Category)\n\n\treturn Item.CallCount > 0 and Item.CallCount or Item.LastCallCount\nend\n\nfunction Stats.Reset(Strict)\n\tFrameNumber = FrameNumber + 1\n\n\tif QueueEnabled then\n\t\tEnabled = true\n\t\tQueueEnabled = false\n\tend\n\n\tif QueueDisable then\n\t\tEnabled = false\n\t\tQueueDisable = false\n\tend\n\n\tif QueueFlush then\n\t\tData = {}\n\t\tPending = {}\n\t\tId = 1\n\t\tQueueFlush = false\n\tend\n\n\tif not Enabled then\n\t\treturn\n\tend\n\n\tlocal Message = nil\n\tfor K, V in pairs(Pending) do\n\t\tif Message == nil then\n\t\t\tMessage = \"Stats.End were not called for the given stats: \\n\"\n\t\tend\n\n\t\tMessage = Message .. \"\\t\" .. tostring(V.Name) .. \" in \" .. tostring(V.Category) .. \"\\n\"\n\tend\n\n\tif Strict then\n\t\tassert(Message == nil, Message)\n\tend\n\n\tfor K, V in pairs(Data) do\n\t\tResetCategory(K)\n\tend\nend\n\nfunction Stats.SetEnabled(IsEnabled)\n\tQueueEnabled = IsEnabled\n\n\tif not QueueEnabled then\n\t\tQueueDisable = true\n\tend\nend\n\nfunction Stats.IsEnabled()\n\treturn Enabled\nend\n\nfunction Stats.GetCategories()\n\tlocal Result = {}\n\n\tfor K, V in pairs(Data) do\n\t\tinsert(Result, K)\n\tend\n\n\treturn Result\nend\n\nfunction Stats.GetItems(Category)\n\tlocal Result = {}\n\n\tlocal Instance = GetCategory(Category)\n\n\tfor K, V in pairs(Instance) do\n\t\tinsert(Result, K)\n\tend\n\n\treturn Result\nend\n\nfunction Stats.Flush()\n\tQueueFlush = true\nend\n\nfunction Stats.GetFrameNumber()\n\treturn FrameNumber\nend\n\nreturn Stats\n"
  },
  {
    "path": "Internal/Core/TablePool.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal TablePool = {}\nTablePool.__index = TablePool\n\nfunction TablePool:pull()\n\tlocal count = self[0]\n\tif count == 0 then return {} end\n\n\tlocal result = self[count]\n\tself[count], self[0] = nil, count - 1\n\n\treturn result\nend\n\nfunction TablePool:pullClean()\n\tlocal result = self:pull()\n\tfor k in pairs(result) do\n\t\tresult[k] = nil\n\tend\n\treturn result\nend\n\nfunction TablePool:push(t)\n\tlocal count = self[0] + 1\n\tself[count], self[0] = t, count\nend\n\nreturn function()\n\treturn setmetatable({ [0] = 0 }, TablePool)\nend\n"
  },
  {
    "path": "Internal/Core/Utility.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Utility = {}\n\nlocal abs = math.abs\nlocal remove = table.remove\n\nfunction Utility.MakeColor(color, target)\n\ttarget = target or {}\n\tif color then\n\t\ttarget[1] = color[1]\n\t\ttarget[2] = color[2]\n\t\ttarget[3] = color[3]\n\t\ttarget[4] = color[4]\n\telse\n\t\ttarget[1] = 0\n\t\ttarget[2] = 0\n\t\ttarget[3] = 0\n\t\ttarget[4] = 1\n\tend\n\treturn target\nend\n\nfunction Utility.HSVtoRGB(H, S, V)\n\tif S == 0.0 then\n\t\treturn V, V, V\n\tend\n\n\tH = math.fmod(H, 1.0) / (60.0/360.0)\n\tlocal I = math.floor(H)\n\tlocal F = H - I\n\tlocal P = V * (1.0 - S)\n\tlocal Q = V * (1.0 - S * F)\n\tlocal T = V * (1.0 - S * (1.0 - F))\n\n\tlocal R, G, B = 0, 0, 0\n\n\tif I == 0 then\n\t\tR, G, B = V, T, P\n\telseif I == 1 then\n\t\tR, G, B = Q, V, P\n\telseif I == 2 then\n\t\tR, G, B = P, V, T\n\telseif I == 3 then\n\t\tR, G, B = P, Q, V\n\telseif I == 4 then\n\t\tR, G, B = T, P, V\n\telse\n\t\tR, G, B = V, P, Q\n\tend\n\n\treturn R, G, B\nend\n\nfunction Utility.RGBtoHSV(R, G, B)\n\tlocal K = 0.0\n\n\tif G < B then\n\t\tlocal T = G\n\t\tG = B\n\t\tB = T\n\t\tK = -1.0\n\tend\n\n\tif R < G then\n\t\tlocal T = R\n\t\tR = G\n\t\tG = T\n\t\tK = -2.0 / 6.0 - K\n\tend\n\n\tlocal Chroma = R - (G < B and G or B)\n\tlocal H = abs(K + (G - B) / (6.0 * Chroma + 1e-20))\n\tlocal S = Chroma / (R + 1e-20)\n\tlocal V = R\n\n\treturn H, S, V\nend\n\nfunction Utility.HasValue(Table, Value)\n\tfor I, V in ipairs(Table) do\n\t\tif V == Value then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction Utility.Remove(Table, Value)\n\tfor I, V in ipairs(Table) do\n\t\tif V == Value then\n\t\t\tremove(Table, I)\n\t\t\tbreak\n\t\tend\n\tend\nend\n\nfunction Utility.CopyValues(A, B)\n\tif type(A) ~= \"table\" or type(B) ~= \"table\" then\n\t\treturn\n\tend\n\n\tfor K, V in pairs(A, B) do\n\t\tlocal Other = B[K]\n\n\t\tif Other ~= nil then\n\t\t\tA[K] = Utility.Copy(Other)\n\t\tend\n\tend\nend\n\nfunction Utility.Copy(Original)\n\tlocal Copy = nil\n\n\tif type(Original) == \"table\" then\n\t\tCopy = {}\n\n\t\tfor K, V in next, Original, nil do\n\t\t\tCopy[Utility.Copy(K)] = Utility.Copy(V)\n\t\tend\n\telse\n\t\tCopy = Original\n\tend\n\n\treturn Copy\nend\n\nfunction Utility.Contains(Table, Value)\n\tif Table == nil then\n\t\treturn false\n\tend\n\n\tfor I, V in ipairs(Table) do\n\t\tif Value == V then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction Utility.TableCount(Table)\n\tlocal Result = 0\n\n\tif Table ~= nil then\n\t\tfor K, V in pairs(Table) do\n\t\t\tResult = Result + 1\n\t\tend\n\tend\n\n\treturn Result\nend\n\nlocal OS = love.system.getOS()\nfunction Utility.IsWindows()\n\treturn OS == \"Windows\"\nend\n\nfunction Utility.IsOSX()\n\treturn OS == \"OS X\"\nend\n\nfunction Utility.IsMobile()\n\treturn (OS == \"Android\") or (OS == \"iOS\")\nend\n\nfunction Utility.Clamp(Value, Min, Max)\n\treturn Value < Min and Min or (Value > Max and Max or Value)\nend\n\nreturn Utility\n"
  },
  {
    "path": "Internal/Input/Common.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Common = {}\n\nCommon.Event =\n{\n\tNone = 0,\n\tPressed = 1,\n\tReleased = 2\n}\n\nreturn Common\n"
  },
  {
    "path": "Internal/Input/Keyboard.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal insert = table.insert\n\nlocal Common = require(SLAB_PATH .. '.Internal.Input.Common')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Utility = require(SLAB_PATH .. '.Internal.Core.Utility')\n\nlocal Keyboard = {}\n\nlocal KeyPressedFn = nil\nlocal KeyReleasedFn = nil\nlocal Events = {}\nlocal Keys = {}\n\nlocal function PushEvent(Type, Key, Scancode, IsRepeat)\n\tinsert(Events, {\n\t\tType = Type,\n\t\tKey = Key,\n\t\tScancode = Scancode,\n\t\tIsRepeat = IsRepeat,\n\t\tFrame = Stats.GetFrameNumber()\n\t})\nend\n\nlocal function OnKeyPressed(Key, Scancode, IsRepeat)\n\tPushEvent(Common.Event.Pressed, Key, Scancode, IsRepeat)\n\n\tif KeyPressedFn ~= nil then\n\t\tKeyPressedFn(Key, Scancode, IsRepeat)\n\tend\nend\n\nlocal function OnKeyReleased(Key, Scancode)\n\tPushEvent(Common.Event.Released, Key, Scancode, false)\n\n\tif KeyReleasedFn ~= nil then\n\t\tKeyReleasedFn(Key, Scancode)\n\tend\nend\n\nlocal function ProcessEvents()\n\tKeys = {}\n\n\t-- Soft keyboards found on mobile/tablet devices will push keypressed/keyreleased events when the user\n\t-- releases from the pressed key. All released events pushed as the same frame as the pressed events will be\n\t-- pushed to the events table for the next frame to process.\n\tlocal NextEvents = {}\n\n\tfor I, V in ipairs(Events) do\n\t\tif Keys[V.Scancode] == nil then\n\t\t\tKeys[V.Scancode] = {}\n\t\tend\n\n\t\tlocal Key = Keys[V.Scancode]\n\n\t\tif Utility.IsMobile() and V.Type == Common.Event.Released and Key.Frame == V.Frame then\n\t\t\tV.Frame = V.Frame + 1\n\t\t\tinsert(NextEvents, V)\n\t\telse\n\t\t\tKey.Type = V.Type\n\t\t\tKey.Key = V.Key\n\t\t\tKey.Scancode = V.Scancode\n\t\t\tKey.IsRepeat = V.IsRepeat\n\t\t\tKey.Frame = V.Frame\n\t\tend\n\tend\n\n\tEvents = NextEvents\nend\n\nKeyboard.OnKeyPressed = OnKeyPressed;\nKeyboard.OnKeyReleased = OnKeyReleased;\n\nfunction Keyboard.Initialize(Args, dontInterceptEventHandlers)\n\tif not dontInterceptEventHandlers then\n\t\tKeyPressedFn = love.handlers['keypressed']\n\t\tKeyReleasedFn = love.handlers['keyreleased']\n\t\tlove.handlers['keypressed'] = OnKeyPressed\n\t\tlove.handlers['keyreleased'] = OnKeyReleased\n\tend\nend\n\nfunction Keyboard.Update()\n\tProcessEvents()\nend\n\nfunction Keyboard.IsPressed(Key)\n\tlocal Item = Keys[Key]\n\n\tif Item == nil then\n\t\treturn false\n\tend\n\n\treturn Item.Type == Common.Event.Pressed\nend\n\nfunction Keyboard.IsReleased(Key)\n\tlocal Item = Keys[Key]\n\n\tif Item == nil then\n\t\treturn false\n\tend\n\n\treturn Item.Type == Common.Event.Released\nend\n\nfunction Keyboard.IsDown(Key)\n\treturn love.keyboard.isScancodeDown(Key)\nend\n\nreturn Keyboard\n"
  },
  {
    "path": "Internal/Input/Mouse.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal insert = table.insert\n\nlocal Common = require(SLAB_PATH .. '.Internal.Input.Common')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal TablePool = require(SLAB_PATH .. '.Internal.Core.TablePool')\nlocal Scale = require(SLAB_PATH .. \".Internal.Core.Scale\")\nlocal Utility = require(SLAB_PATH .. \".Internal.Core.Utility\")\n\n\nlocal Mouse = {}\n\nlocal State =\n{\n\tX = 0.0,\n\tY = 0.0,\n\tDeltaX = 0.0,\n\tDeltaY = 0.0,\n\tAsyncDeltaX = 0.0,\n\tAsyncDeltaY = 0.0,\n\tButtons = {}\n}\n\nlocal Cursors = nil\nlocal CurrentCursor = \"arrow\"\nlocal PendingCursor = \"\"\nlocal MouseMovedFn = nil\nlocal MousePressedFn = nil\nlocal MouseReleasedFn = nil\nlocal Events = {}\nlocal eventPool = TablePool()\n\n-- Custom cursors allow the developer to override any specific system cursor used. This system will also\n-- allow developers to set an empty image to hide the cursor for specific states, such as mouse resize.\n-- For more information, refer to the SetCustomCursor/ClearCustomCursor functions.\nlocal CustomCursors = {}\n\nlocal function ScaleMouseXY(X, Y)\n\tlocal scale = Scale.GetScale()\n\treturn X / scale, Y / scale\nend\n\n\nlocal function TransformPoint(X,Y)\n\treturn ScaleMouseXY(X, Y)\nend\n\n\nlocal function OnMouseMoved(X, Y, DX, DY, IsTouch)\n\tlocal tX, tY = TransformPoint(X, Y)\n\tState.X = tX\n\tState.Y = tY\n\n\tState.AsyncDeltaX = State.AsyncDeltaX + DX\n\tState.AsyncDeltaY = State.AsyncDeltaY + DY\n\n\tif MouseMovedFn ~= nil then\n\t\tMouseMovedFn(X, Y, DX, DY, IsTouch)\n\tend\nend\n\nlocal function PushEvent(Type, X, Y, Button, IsTouch, Presses)\n\tlocal ev = eventPool:pull()\n\tev.Type = Type\n\tev.X = X\n\tev.Y = Y\n\tev.Button = Button\n\tev.IsTouch = IsTouch\n\tev.Presses = Presses\n\tinsert(Events, 1, ev)\nend\n\nlocal function OnMousePressed(X, Y, Button, IsTouch, Presses)\n\tlocal tX, tY = TransformPoint(X, Y)\n\tPushEvent(Common.Event.Pressed, tX, tY, Button, IsTouch, Presses)\n\n\tif MousePressedFn ~= nil then\n\t\tMousePressedFn(X, Y, Button, IsTouch, Presses)\n\tend\nend\n\nlocal function OnMouseReleased(X, Y, Button, IsTouch, Presses)\n\tlocal tX, tY = TransformPoint(X, Y)\n\tPushEvent(Common.Event.Released, tX, tY, Button, IsTouch, Presses)\n\n\tif MouseReleasedFn ~= nil then\n\t\tMouseReleasedFn(X, Y, Button, IsTouch, Presses)\n\tend\nend\n\nlocal function ProcessEvents()\n\tfor k, v in pairs(State.Buttons) do\n\t\tv.Type = Common.Event.None\n\tend\n\tlocal wasPressed = false\n\n\tfor i = #Events, 1, -1 do\n\t\tlocal ev = Events[i]\n\n\t\t-- delay release events until next frame\n\t\tif ev.Type == Common.Event.Released and wasPressed then break end\n\t\twasPressed = ev.Type == Common.Event.Pressed\n\n\t\tif State.Buttons[ev.Button] == nil then\n\t\t\tState.Buttons[ev.Button] = {}\n\t\tend\n\n\t\tlocal button = State.Buttons[ev.Button]\n\t\tbutton.Type = ev.Type\n\t\tbutton.IsTouch = ev.IsTouch\n\t\tbutton.Presses = ev.Presses\n\n\t\teventPool:push(Events[i])\n\t\tEvents[i] = nil\n\tend\nend\n\nMouse.OnMouseMoved = OnMouseMoved;\nMouse.OnMousePressed = OnMousePressed;\nMouse.OnMouseReleased = OnMouseReleased;\n\nfunction Mouse.Initialize(Args, dontInterceptEventHandlers)\n\tTransformPoint = Args.TransformPointToSlab or TransformPoint\n\n\tif not dontInterceptEventHandlers then\n\t\tMouseMovedFn = love.handlers['mousemoved']\n\t\tMousePressedFn = love.handlers['mousepressed']\n\t\tMouseReleasedFn = love.handlers['mousereleased']\n\t\tlove.handlers['mousemoved'] = OnMouseMoved\n\t\tlove.handlers['mousepressed'] = OnMousePressed\n\t\tlove.handlers['mousereleased'] = OnMouseReleased\n\tend\nend\n\nfunction Mouse.Update()\n\tProcessEvents()\n\n\tState.DeltaX = State.AsyncDeltaX\n\tState.DeltaY = State.AsyncDeltaY\n\tState.AsyncDeltaX = 0\n\tState.AsyncDeltaY = 0\n\n\tif Cursors == nil and (not Utility.IsMobile()) then\n\t\tCursors = {}\n\t\tCursors.Arrow = love.mouse.getSystemCursor('arrow')\n\t\tCursors.SizeWE = love.mouse.getSystemCursor('sizewe')\n\t\tCursors.SizeNS = love.mouse.getSystemCursor('sizens')\n\t\tCursors.SizeNESW = love.mouse.getSystemCursor('sizenesw')\n\t\tCursors.SizeNWSE = love.mouse.getSystemCursor('sizenwse')\n\t\tCursors.IBeam = love.mouse.getSystemCursor('ibeam')\n\t\tCursors.Hand = love.mouse.getSystemCursor('hand')\n\tend\n\n\tMouse.SetCursor('arrow')\nend\n\nfunction Mouse.Draw()\n\tMouse.UpdateCursor()\n\n\tlocal CustomCursor = CustomCursors[CurrentCursor]\n\tif CustomCursor ~= nil then\n\t\tDrawCommands.SetLayer('Mouse')\n\t\tDrawCommands.Begin()\n\n\t\tif CustomCursor.Quad ~= nil then\n\t\t\tlocal X, Y, W, H = CustomCursor.Quad:getViewport()\n\t\t\tDrawCommands.SubImage(State.X, State.Y, CustomCursor.Image, X, Y, W, H)\n\t\telse\n\t\t\tDrawCommands.Image(State.X, State.Y, CustomCursor.Image)\n\t\tend\n\n\t\tDrawCommands.End()\n\tend\nend\n\nfunction Mouse.IsDown(Button)\n\treturn love.mouse.isDown(Button)\nend\n\nfunction Mouse.IsClicked(Button)\n\tlocal Item = State.Buttons[Button]\n\n\tif Item == nil or Item.Presses == 0 then\n\t\treturn false\n\tend\n\n\treturn Item.Type == Common.Event.Pressed\nend\n\nfunction Mouse.IsDoubleClicked(Button)\n\tlocal Item = State.Buttons[Button]\n\n\tif Item == nil or Item.Presses < 2 then\n\t\treturn false\n\tend\n\n\treturn Item.Type == Common.Event.Pressed and Item.Presses % 2 == 0\nend\n\nfunction Mouse.IsReleased(Button)\n\tlocal Item = State.Buttons[Button]\n\n\tif Item == nil then\n\t\treturn false\n\tend\n\n\treturn Item.Type == Common.Event.Released\nend\n\nfunction Mouse.Position()\n\treturn State.X, State.Y\nend\n\nfunction Mouse.HasDelta()\n\treturn State.DeltaX ~= 0.0 or State.DeltaY ~= 0.0\nend\n\nfunction Mouse.GetDelta()\n\treturn State.DeltaX, State.DeltaY\nend\n\nfunction Mouse.IsDragging(Button)\n\treturn Mouse.IsDown(Button) and Mouse.HasDelta()\nend\n\nfunction Mouse.SetCursor(Type)\n\tif Cursors == nil then\n\t\treturn\n\tend\n\n\tPendingCursor = Type\nend\n\nfunction Mouse.UpdateCursor()\n\tif PendingCursor ~= \"\" and PendingCursor ~= CurrentCursor then\n\t\tCurrentCursor = PendingCursor\n\t\tPendingCursor = \"\"\n\n\t\tif CustomCursors[CurrentCursor] ~= nil then\n\t\t\tlove.mouse.setVisible(false)\n\t\telse\n\t\t\tlove.mouse.setVisible(true)\n\t\t\tlocal Type = CurrentCursor\n\t\t\tif Type == 'arrow' then\n\t\t\t\tlove.mouse.setCursor(Cursors.Arrow)\n\t\t\telseif Type == 'sizewe' then\n\t\t\t\tlove.mouse.setCursor(Cursors.SizeWE)\n\t\t\telseif Type == 'sizens' then\n\t\t\t\tlove.mouse.setCursor(Cursors.SizeNS)\n\t\t\telseif Type == 'sizenesw' then\n\t\t\t\tlove.mouse.setCursor(Cursors.SizeNESW)\n\t\t\telseif Type == 'sizenwse' then\n\t\t\t\tlove.mouse.setCursor(Cursors.SizeNWSE)\n\t\t\telseif Type == 'ibeam' then\n\t\t\t\tlove.mouse.setCursor(Cursors.IBeam)\n\t\t\telseif Type == 'hand' then\n\t\t\t\tlove.mouse.setCursor(Cursors.Hand)\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction Mouse.SetCustomCursor(Type, Image, Quad)\n\t-- If no image is supplied, then create a 1x1 image with no alpha. This is a way to disable certain system cursors.\n\tif Image == nil then\n\t\tlocal Data = love.image.newImageData(1, 1)\n\t\tImage = love.graphics.newImage(Data)\n\tend\n\n\tif CustomCursors[Type] == nil then\n\t\tCustomCursors[Type] = {}\n\tend\n\n\tCustomCursors[Type].Image = Image\n\tCustomCursors[Type].Quad = Quad\n\tPendingCursor = CurrentCursor\n\tCurrentCursor = \"\"\nend\n\nfunction Mouse.ClearCustomCursor(Type)\n\tCustomCursors[Type] = nil\n\tPendingCursor = CurrentCursor\n\tCurrentCursor = \"\"\nend\n\nreturn Mouse\n"
  },
  {
    "path": "Internal/Resources/Styles/Dark.style",
    "content": "FontSize = 14\nMenuColor = (0.2, 0.2, 0.2, 1.0)\nScrollBarColor = (0.4, 0.4, 0.4, 1.0)\nScrollBarHoveredColor = (0.8, 0.8, 0.8, 1.0)\nSeparatorColor = (0.5, 0.5, 0.5, 0.7)\nWindowBackgroundColor = (0.2, 0.2, 0.2, 1.0)\nWindowTitleFocusedColor = (0.26, 0.53, 0.96, 1.0)\nWindowCloseBgColor = (0.64, 0.64, 0.64, 1.0)\nWindowCloseColor = (0.0, 0.0, 0.0, 1.0)\nButtonColor = (0.55, 0.55, 0.55, 1.0)\nRadioButtonSelectedColor = (0.2, 0.2, 0.2, 1.0)\nButtonHoveredColor = (0.7, 0.7, 0.7, 1.0)\nButtonPressedColor = (0.8, 0.8, 0.8, 1.0)\nButtonDisabledTextColor = (0.35, 0.35, 0.35, 1.0)\nCheckBoxSelectedColor = (0.0, 0.0, 0.0, 1.0)\nCheckBoxDisabledColor = (0.35, 0.35, 0.35, 1.0)\nTextColor = (0.875, 0.875, 0.875, 1.0)\nTextDisabledColor = (0.45, 0.45, 0.45, 1.0)\nTextHoverBgColor = (0.5, 0.5, 0.5, 1.0)\nTextURLColor = (0.2, 0.2, 1.0, 1.0)\nComboBoxColor = (0.4, 0.4, 0.4, 1.0)\nComboBoxHoveredColor = (0.55, 0.55, 0.55, 1.0)\nComboBoxDropDownColor = (0.4, 0.4, 0.4, 1.0)\nComboBoxDropDownHoveredColor = (0.55, 0.55, 0.55, 1.0)\nComboBoxArrowColor = (1.0, 1.0, 1.0, 1.0)\nInputBgColor = (0.4, 0.4, 0.4, 1.0)\nInputEditBgColor = (0.6, 0.6, 0.6, 1.0)\nInputSelectColor = (0.14, 0.29, 0.53, 0.4)\nInputSliderColor = (0.1, 0.1, 0.1, 1.0)\nMultilineTextColor = (0.0, 0.0, 0.0, 1.0)\nListBoxBgColor = (0.0, 0.0, 0.0, 0.0)\n\nWindowRounding = 2.0\nWindowBorder = 4.0\nWindowTitleH = 0.0\nButtonRounding = 2.0\nCheckBoxRounding = 2.0\nComboBoxRounding = 2.0\nInputBgRounding = 2.0\nScrollBarRounding = 2.0\nIndent = 14.0\nMenuPadH = 0.0\nMenuItemPadH = 0.0\n"
  },
  {
    "path": "Internal/Resources/Styles/Light.style",
    "content": "FontSize = 14\nMenuColor = (0.74, 0.74, 0.94, 1.0)\nScrollBarColor = (0.4, 0.62, 0.80, 0.3)\nScrollBarHoveredColor = (0.28, 0.67, 0.8, 0.59)\nSeparatorColor = (0.5, 0.5, 0.5, 0.7)\nWindowBackgroundColor = (0.94, 0.94, 0.94, 1.0)\nWindowTitleFocusedColor = (0.42, 0.75, 1.0, 1.0)\nWindowCloseBgColor = (0.64, 0.64, 0.64, 1.0)\nWindowCloseColor = (0.0, 0.0, 0.0, 1.0)\nButtonColor = (1.0, 0.79, 0.18, 0.78)\nRadioButtonSelectedColor = (0.31, 0.25, 0.24, 1.0)\nButtonHoveredColor = (0.42, 0.82, 1.0, 0.81)\nButtonPressedColor = (0.72, 1.0, 1.0, 0.86)\nButtonDisabledTextColor = (0.55, 0.55, 0.55, 1.0)\nCheckBoxSelectedColor = (0.31, 0.25, 0.24, 1.0)\nCheckBoxDisabledColor = (0.55, 0.55, 0.55, 1.0)\nTextColor = (0.31, 0.25, 0.24, 1.0)\nTextDisabledColor = (0.55, 0.55, 0.55, 1.0)\nTextHoverBgColor = (1.0, 0.99, 0.54, 0.43)\nTextURLColor = (0.2, 0.2, 1.0, 1.0)\nComboBoxColor = (0.89, 0.98, 1.0, 1.0)\nComboBoxHoveredColor = (0.14, 0.29, 0.53, 0.4)\nComboBoxDropDownColor = (0.89, 0.98, 1.0, 1.0)\nComboBoxDropDownHoveredColor = (0.14, 0.29, 0.53, 0.4)\nComboBoxArrowColor = (0.31, 0.25, 0.24, 1.0)\nInputBgColor = (0.89, 0.98, 1.0, 1.0)\nInputEditBgColor = (0.89, 0.98, 1.0, 1.0)\nInputSelectColor = (0.14, 0.29, 0.53, 0.4)\nInputSliderColor = (1.0, 0.79, 0.18, 1.0)\nMultilineTextColor = (0.0, 0.0, 0.0, 1.0)\nListBoxBgColor = (0.0, 0.0, 0.0, 0.0)\n\nWindowRounding = 2.0\nWindowBorder = 4.0\nWindowTitleH = 0.0\nButtonRounding = 2.0\nCheckBoxRounding = 2.0\nComboBoxRounding = 2.0\nInputBgRounding = 2.0\nScrollBarRounding = 2.0\nIndent = 14.0\nMenuPadH = 0.0\nMenuItemPadH = 0.0\n"
  },
  {
    "path": "Internal/UI/Button.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal floor = math.floor\nlocal max = math.max\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal Image = require(SLAB_PATH .. '.Internal.UI.Image')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Text = require(SLAB_PATH .. '.Internal.UI.Text')\nlocal Tooltip = require(SLAB_PATH .. '.Internal.UI.Tooltip')\nlocal Utility = require(SLAB_PATH .. '.Internal.Core.Utility')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\n\nlocal Button = {}\n\nlocal PAD = 10.0\nlocal MINWIDTH = 75.0\nlocal RADIUS = 8.0\nlocal EMPTY = {}\nlocal IGNORE = { Ignore = true }\n\nlocal clickedId = nil\nlocal labelColor = {}\n\nfunction Button.Begin(label, options)\n\tlocal statHandle = Stats.Begin('Button', 'Slab')\n\n\toptions = options or EMPTY\n\tlocal width, height = options.W, options.H\n\tlocal disabled = options.Disabled\n\tlocal image = options.Image\n\tlocal color = options.Color or Style.ButtonColor\n\tlocal hoverColor = options.HoverColor or Style.ButtonHoveredColor\n\tlocal pressColor = options.PressColor or Style.ButtonPressedColor\n\tlocal padX = options.PadX or PAD * 2.0\n\tlocal padY = options.PadY or PAD * 0.5\n\tlocal vLines = options.VLines or 1\n\n\tif options.Active then\n\t\tcolor = pressColor\n\tend\n\n\tlocal id = Window.GetItemId(label)\n\tlocal w, h = Button.GetSize(label)\n\th = h * vLines\n\n\t-- If a valid image was specified, then adjust the button size to match the requested image size. Also takes into account any sub UVs.\n\tlocal imageW, imageH = w, h\n\tif image ~= nil then\n\t\timageW, imageH = Image.GetSize(image.Image or image.Path)\n\n\t\timageW = image.SubW or imageW\n\t\timageH = image.SubH or imageH\n\n\t\timageW = width or imageW\n\t\timageH = height or imageH\n\n\t\timage.W = imageW\n\t\timage.H = imageH\n\n\t\tif imageW > 0 and imageH > 0 then\n\t\t\tw = imageW + padX\n\t\t\th = imageH + padY\n\t\tend\n\tend\n\n\tw, h = LayoutManager.ComputeSize(width or w, height or h)\n\tLayoutManager.AddControl(w, h, 'Button')\n\n\tlocal x, y = Cursor.GetPosition()\n\n\tlocal result = false\n\n\tdo\n\t\tlocal mouseX, mouseY = Window.GetMousePosition()\n\t\tif not Window.IsObstructedAtMouse() and x <= mouseX and mouseX <= x + w and y <= mouseY and mouseY <= y + h then\n\t\t\tTooltip.Begin(options.Tooltip or \"\")\n\t\t\tWindow.SetHotItem(id)\n\n\t\t\tif not disabled then\n\t\t\t\tif not Utility.IsMobile() then\n\t\t\t\t\tcolor = hoverColor\n\t\t\t\tend\n\n\t\t\t\tif clickedId == id then\n\t\t\t\t\tcolor = pressColor\n\t\t\t\tend\n\n\t\t\t\tif Mouse.IsClicked(1) then\n\t\t\t\t\tclickedId = id\n\t\t\t\tend\n\n\t\t\t\tif Mouse.IsReleased(1) and clickedId == id then\n\t\t\t\t\tresult = true\n\t\t\t\t\tclickedId = nil\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tif not options.Invisible then\n\t\t-- Draw the background.\n\t\tDrawCommands.Rectangle('fill', x, y, w, h, color, options.Rounding or Style.ButtonRounding)\n\n\t\t-- Draw the label or image. The layout of this control was already computed above. Ignore when adding sub-controls\n\t\t-- such as text or an image.\n\t\tlocal cursorX, cursorY = Cursor.GetPosition()\n\t\tLayoutManager.Begin('Ignore', IGNORE)\n\t\tif image ~= nil then\n\t\t\tCursor.SetX(x + w * 0.5 - imageW * 0.5)\n\t\t\tCursor.SetY(y + h * 0.5 - imageH * 0.5)\n\t\t\tImage.Begin(id .. '_Image', image)\n\t\telse\n\t\t\tlocal labelX = x + (w * 0.5) - (Style.Font:getWidth(label) * 0.5)\n\t\t\tlocal fontHeight = Style.Font:getHeight() * vLines\n\t\t\tCursor.SetX(floor(labelX))\n\t\t\tCursor.SetY(floor(y + (h * 0.5) - (fontHeight * 0.5)))\n\t\t\tlabelColor.color = disabled and Style.ButtonDisabledTextColor or nil\n\t\t\tText.Begin(label, labelColor)\n\t\tend\n\t\tLayoutManager.End()\n\n\t\tCursor.SetPosition(cursorX, cursorY)\n\tend\n\n\tCursor.SetItemBounds(x, y, w, h)\n\tCursor.AdvanceY(h)\n\n\tWindow.AddItem(x, y, w, h, id)\n\n\tStats.End(statHandle)\n\n\treturn result\nend\n\nfunction Button.BeginRadio(label, options)\n\tlocal statHandle = Stats.Begin('RadioButton', 'Slab')\n\n\tlabel = label or \"\"\n\n\toptions = options or EMPTY\n\tlocal index = options.Index or 0\n\tlocal selectedIndex = options.SelectedIndex or 0\n\n\tlocal result = false\n\tlocal id = Window.GetItemId(label)\n\tlocal w, h = RADIUS * 2.0, RADIUS * 2.0\n\tlocal isObstructed = Window.IsObstructedAtMouse()\n\tlocal color = Style.ButtonColor\n\tlocal mouseX, mouseY = Window.GetMousePosition()\n\n\tif label ~= \"\" then\n\t\tlocal TextW, TextH = Text.GetSize(label)\n\t\tw = w + Cursor.PadX() + TextW\n\t\th = max(h, TextH)\n\tend\n\n\tLayoutManager.AddControl(w, h, 'Radio')\n\n\tlocal x, y = Cursor.GetPosition()\n\tlocal centerX, centerY = x + RADIUS, y + RADIUS\n\tlocal dx = mouseX - centerX\n\tlocal dy = mouseY - centerY\n\tif not isObstructed and (dx * dx) + (dy * dy) <= RADIUS * RADIUS then\n\t\tcolor = Style.ButtonHoveredColor\n\n\t\tif clickedId == id then\n\t\t\tcolor = Style.ButtonPressedColor\n\t\tend\n\n\t\tif Mouse.IsClicked(1) then\n\t\t\tclickedId = id\n\t\tend\n\n\t\tif Mouse.IsReleased(1) and clickedId == id then\n\t\t\tresult = true\n\t\t\tclickedId = nil\n\t\tend\n\tend\n\n\tDrawCommands.Circle('fill', centerX, centerY, RADIUS, color)\n\n\tif index > 0 and index == selectedIndex then\n\t\tDrawCommands.Circle('fill', centerX, centerY, RADIUS * 0.7, Style.RadioButtonSelectedColor)\n\tend\n\n\tif label ~= \"\" then\n\t\tlocal cursorY = Cursor.GetY()\n\t\tCursor.AdvanceX(RADIUS * 2.0)\n\t\tLayoutManager.Begin('Ignore', IGNORE)\n\t\tText.Begin(label)\n\t\tLayoutManager.End()\n\t\tCursor.SetY(cursorY)\n\tend\n\n\tif not isObstructed and x <= mouseX and mouseX <= x + w and y <= mouseY and mouseY <= y + h then\n\t\tTooltip.Begin(options.Tooltip or \"\")\n\t\tWindow.SetHotItem(id)\n\tend\n\n\tCursor.SetItemBounds(x, y, w, h)\n\tCursor.AdvanceY(h)\n\n\tWindow.AddItem(x, y, w, h)\n\n\tStats.End(statHandle)\n\n\treturn result\nend\n\nfunction Button.GetSize(label)\n\tlocal w = Style.Font:getWidth(label)\n\tlocal h = Style.Font:getHeight()\n\treturn max(w, MINWIDTH) + PAD * 2.0, h + PAD * 0.5\nend\n\nfunction Button.ClearClicked()\n\tclickedId = nil\nend\n\nreturn Button\n"
  },
  {
    "path": "Internal/UI/CheckBox.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal max = math.max\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Text = require(SLAB_PATH .. '.Internal.UI.Text')\nlocal Tooltip = require(SLAB_PATH .. '.Internal.UI.Tooltip')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\n\nlocal CheckBox = {}\nlocal EMPTY = {}\nlocal IGNORE = { Ignore = true }\nlocal labelColor = {}\n\nfunction CheckBox.Begin(checked, label, options)\n\tlocal statHandle = Stats.Begin('CheckBox', 'Slab')\n\n\tlabel = label or \"\"\n\n\toptions = options or EMPTY\n\tlocal id = options.Id or label\n\tlocal rounding = options.Rounding or Style.CheckBoxRounding\n\tlocal size = options.Size or Style.Font:getHeight()\n\tlocal disabled = options.Disabled\n\n\tlocal itemId = Window.GetItemId(id)\n\tlocal boxW, boxH = size, size\n\tlocal textW, textH = Text.GetSize(label)\n\tlocal w = boxW + Cursor.PadX() + 2.0 + textW\n\tlocal h = max(boxH, textH)\n\tlocal radius = size * 0.5\n\n\tLayoutManager.AddControl(w, h, 'CheckBox')\n\n\tlocal result = false\n\tlocal color = disabled and Style.CheckBoxDisabledColor or Style.ButtonColor\n\n\tlocal x, y = Cursor.GetPosition()\n\tlocal mouseX, mouseY = Window.GetMousePosition()\n\tlocal isObstructed = Window.IsObstructedAtMouse()\n\tif not isObstructed and not disabled and x <= mouseX and mouseX <= x + w and y <= mouseY and mouseY <= y + h then\n\t\tcolor = Style.ButtonHoveredColor\n\n\t\tif Mouse.IsDown(1) then\n\t\t\tcolor = Style.ButtonPressedColor\n\t\telseif Mouse.IsReleased(1) then\n\t\t\tresult = true\n\t\tend\n\tend\n\n\tDrawCommands.Rectangle('fill', x, y, boxW, boxH, color, rounding)\n\tif checked then\n\t\tDrawCommands.Cross(x + radius, y + radius, radius - 1.0, Style.CheckBoxSelectedColor)\n\tend\n\tif label ~= \"\" then\n\t\tlocal cursorY = Cursor.GetY()\n\t\tCursor.AdvanceX(boxW + 2.0)\n\t\tLayoutManager.Begin('Ignore', IGNORE)\n\t\tlabelColor.Color = disabled and Style.TextDisabledColor or nil\n\t\tText.Begin(label, labelColor)\n\t\tLayoutManager.End()\n\t\tCursor.SetY(cursorY)\n\tend\n\n\tif not isObstructed and x <= mouseX and mouseX <= x + w and y <= mouseY and mouseY <= y + h then\n\t\tTooltip.Begin(options.Tooltip or \"\")\n\t\tWindow.SetHotItem(itemId)\n\tend\n\n\tCursor.SetItemBounds(x, y, w, h)\n\tCursor.AdvanceY(h)\n\n\tWindow.AddItem(x, y, w, h, itemId)\n\n\tStats.End(statHandle)\n\n\treturn result\nend\n\nreturn CheckBox\n"
  },
  {
    "path": "Internal/UI/ColorPicker.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal ceil = math.ceil\nlocal max = math.max\nlocal min = math.min\nlocal insert = table.insert\nlocal unpack = table.unpack\n\nlocal Button = require(SLAB_PATH .. '.Internal.UI.Button')\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal Image = require(SLAB_PATH .. '.Internal.UI.Image')\nlocal Input = require(SLAB_PATH .. '.Internal.UI.Input')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Text = require(SLAB_PATH .. '.Internal.UI.Text')\nlocal Utility = require(SLAB_PATH .. '.Internal.Core.Utility')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\n\nlocal ColorPicker = {}\n\nlocal SaturationMeshes = nil\nlocal SaturationSize = 200.0\nlocal SaturationStep = 5\nlocal SaturationFocused = false\n\nlocal TintMeshes = nil\nlocal TintW = 30.0\nlocal TintH = SaturationSize\nlocal TintFocused = false\n\nlocal AlphaMesh = nil\nlocal AlphaW = TintW\nlocal AlphaH = TintH\nlocal AlphaFocused = false\n\nlocal CurrentColor = {1.0, 1.0, 1.0, 1.0}\nlocal ColorH = 25.0\n\nlocal function IsEqual(A, B)\n\tfor I, V in ipairs(A) do\n\t\tif V ~= B[I] then\n\t\t\treturn false\n\t\tend\n\tend\n\n\treturn true\nend\n\nlocal function InputColor(Component, Value, OffsetX)\n\tlocal Changed = false\n\tText.Begin(string.format(\"%s \", Component))\n\tCursor.SameLine()\n\tCursor.SetRelativeX(OffsetX)\n\tif Input.Begin('ColorPicker_' .. Component, {W = 40.0, NumbersOnly = true, Text = tostring(ceil(Value * 255)), ReturnOnText = false}) then\n\t\tlocal NewValue = tonumber(Input.GetText())\n\t\tif NewValue ~= nil then\n\t\t\tNewValue = max(NewValue, 0)\n\t\t\tNewValue = min(NewValue, 255)\n\t\t\tValue = NewValue / 255\n\t\t\tChanged = true\n\t\tend\n\tend\n\treturn Value, Changed\nend\n\nlocal function UpdateSaturationColors()\n\tif SaturationMeshes ~= nil then\n\t\tlocal MeshIndex = 1\n\t\tlocal Step = SaturationStep\n\t\tlocal C00 = {1.0, 1.0, 1.0, 1.0}\n\t\tlocal C10 = {1.0, 1.0, 1.0, 1.0}\n\t\tlocal C01 = {1.0, 1.0, 1.0, 1.0}\n\t\tlocal C11 = {1.0, 1.0, 1.0, 1.0}\n\t\tlocal StepX, StepY = 0, 0\n\t\tlocal Hue, Sat, Val = Utility.RGBtoHSV(CurrentColor[1], CurrentColor[2], CurrentColor[3])\n\n\t\tfor I = 1, Step, 1 do\n\t\t\tfor J = 1, Step, 1 do\n\t\t\t\tlocal S0 = StepX / Step\n\t\t\t\tlocal S1 = (StepX + 1) / Step\n\t\t\t\tlocal V0 = 1.0 - (StepY / Step)\n\t\t\t\tlocal V1 = 1.0 - ((StepY + 1) / Step)\n\n\t\t\t\tC00[1], C00[2], C00[3] = Utility.HSVtoRGB(Hue, S0, V0)\n\t\t\t\tC10[1], C10[2], C10[3] = Utility.HSVtoRGB(Hue, S1, V0)\n\t\t\t\tC01[1], C01[2], C01[3] = Utility.HSVtoRGB(Hue, S0, V1)\n\t\t\t\tC11[1], C11[2], C11[3] = Utility.HSVtoRGB(Hue, S1, V1)\n\n\t\t\t\tlocal Mesh = SaturationMeshes[MeshIndex]\n\t\t\t\tMeshIndex = MeshIndex + 1\n\n\t\t\t\tMesh:setVertexAttribute(1, 3, C00[1], C00[2], C00[3], C00[4])\n\t\t\t\tMesh:setVertexAttribute(2, 3, C10[1], C10[2], C10[3], C10[4])\n\t\t\t\tMesh:setVertexAttribute(3, 3, C11[1], C11[2], C11[3], C11[4])\n\t\t\t\tMesh:setVertexAttribute(4, 3, C01[1], C01[2], C01[3], C01[4])\n\n\t\t\t\tStepX = StepX + 1\n\t\t\tend\n\n\t\t\tStepX = 0\n\t\t\tStepY = StepY + 1\n\t\tend\n\tend\nend\n\nlocal function InitializeSaturationMeshes()\n\tif SaturationMeshes == nil then\n\t\tSaturationMeshes = {}\n\t\tlocal Step = SaturationStep\n\t\tlocal X, Y = 0.0, 0.0\n\t\tlocal Size = SaturationSize / Step\n\n\t\tfor I = 1, Step, 1 do\n\t\t\tfor J = 1, Step, 1 do\n\t\t\t\tlocal Verts = {\n\t\t\t\t\t{\n\t\t\t\t\t\tX, Y,\n\t\t\t\t\t\t0.0, 0.0\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tX + Size, Y,\n\t\t\t\t\t\t1.0, 0.0\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tX + Size, Y + Size,\n\t\t\t\t\t\t1.0, 1.0\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tX, Y + Size,\n\t\t\t\t\t\t0.0, 1.0\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlocal NewMesh = love.graphics.newMesh(Verts)\n\t\t\t\tinsert(SaturationMeshes, NewMesh)\n\n\t\t\t\tX = X + Size\n\t\t\tend\n\n\t\t\tX = 0.0\n\t\t\tY = Y + Size\n\t\tend\n\tend\n\n\tUpdateSaturationColors()\nend\n\nlocal function InitializeTintMeshes()\n\tif TintMeshes == nil then\n\t\tTintMeshes = {}\n\t\tlocal Step = 6\n\t\tlocal X, Y = 0.0, 0.0\n\t\tlocal C0 = {1.0, 1.0, 1.0, 1.0}\n\t\tlocal C1 = {1.0, 1.0, 1.0, 1.0}\n\t\tlocal I = 0\n\t\tlocal Colors = {\n\t\t\t{1.0, 0.0, 0.0, 1.0},\n\t\t\t{1.0, 1.0, 0.0, 1.0},\n\t\t\t{0.0, 1.0, 0.0, 1.0},\n\t\t\t{0.0, 1.0, 1.0, 1.0},\n\t\t\t{0.0, 0.0, 1.0, 1.0},\n\t\t\t{1.0, 0.0, 1.0, 1.0},\n\t\t\t{1.0, 0.0, 0.0, 1.0}\n\t\t}\n\n\t\tfor Index = 1, Step, 1 do\n\t\t\tC0 = Colors[Index]\n\t\t\tC1 = Colors[Index + 1]\n\t\t\tlocal Verts = {\n\t\t\t\t{\n\t\t\t\t\tX, Y,\n\t\t\t\t\t0.0, 0.0,\n\t\t\t\t\tC0[1], C0[2], C0[3], C0[4]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tTintW, Y,\n\t\t\t\t\t1.0, 0.0,\n\t\t\t\t\tC0[1], C0[2], C0[3], C0[4]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tTintW, Y + TintH / Step,\n\t\t\t\t\t1.0, 1.0,\n\t\t\t\t\tC1[1], C1[2], C1[3], C1[4]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tX, Y + TintH / Step,\n\t\t\t\t\t0.0, 1.0,\n\t\t\t\t\tC1[1], C1[2], C1[3], C1[4]\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlocal NewMesh = love.graphics.newMesh(Verts)\n\t\t\tinsert(TintMeshes, NewMesh)\n\n\t\t\tY = Y + TintH / Step\n\t\tend\n\tend\nend\n\nlocal function InitializeAlphaMesh()\n\tif AlphaMesh == nil then\n\t\tlocal Verts = {\n\t\t\t{\n\t\t\t\t0.0, 0.0,\n\t\t\t\t0.0, 0.0,\n\t\t\t\t1.0, 1.0, 1.0, 1.0\n\t\t\t},\n\t\t\t{\n\t\t\t\tAlphaW, 0.0,\n\t\t\t\t1.0, 0.0,\n\t\t\t\t1.0, 1.0, 1.0, 1.0\n\t\t\t},\n\t\t\t{\n\t\t\t\tAlphaW, AlphaH,\n\t\t\t\t1.0, 1.0,\n\t\t\t\t0.0, 0.0, 0.0, 1.0\n\t\t\t},\n\t\t\t{\n\t\t\t\t0.0, AlphaH,\n\t\t\t\t0.0, 1.0,\n\t\t\t\t0.0, 0.0, 0.0, 1.0\n\t\t\t}\n\t\t}\n\n\t\tAlphaMesh = love.graphics.newMesh(Verts)\n\tend\nend\n\nfunction ColorPicker.Begin(Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.Color = Options.Color == nil and {1.0, 1.0, 1.0, 1.0} or Options.Color\n\tOptions.Refresh = Options.Refresh == nil and false or Options.Refresh\n\tOptions.X = Options.X == nil and nil or Options.X\n\tOptions.Y = Options.Y == nil and nil or Options.Y\n\n\tif SaturationMeshes == nil then\n\t\tInitializeSaturationMeshes()\n\tend\n\n\tif TintMeshes == nil then\n\t\tInitializeTintMeshes()\n\tend\n\n\tif AlphaMesh == nil then\n\t\tInitializeAlphaMesh()\n\tend\n\n\tWindow.Begin('ColorPicker', {Title = \"Color Picker\", X = Options.X, Y = Options.Y})\n\n\tif Window.IsAppearing() or Options.Refresh then\n\t\tCurrentColor[1] = Options.Color[1] or 0.0\n\t\tCurrentColor[2] = Options.Color[2] or 0.0\n\t\tCurrentColor[3] = Options.Color[3] or 0.0\n\t\tCurrentColor[4] = Options.Color[4] or 1.0\n\t\tUpdateSaturationColors()\n\tend\n\n\tlocal X, Y = Cursor.GetPosition()\n\tlocal MouseX, MouseY = Window.GetMousePosition()\n\tlocal H, S, V = Utility.RGBtoHSV(CurrentColor[1], CurrentColor[2], CurrentColor[3])\n\tlocal UpdateColor = false\n\tlocal MouseClicked = Mouse.IsClicked(1) and not Window.IsObstructedAtMouse()\n\n\tif SaturationMeshes ~= nil then\n\t\tfor I, V in ipairs(SaturationMeshes) do\n\t\t\tDrawCommands.Mesh(V, X, Y)\n\t\tend\n\n\t\tWindow.AddItem(X, Y, SaturationSize, SaturationSize)\n\n\t\tlocal UpdateSaturation = false\n\t\tif X <= MouseX and MouseX < X + SaturationSize and Y <= MouseY and MouseY < Y + SaturationSize then\n\t\t\tif MouseClicked then\n\t\t\t\tSaturationFocused = true\n\t\t\t\tUpdateSaturation = true\n\t\t\tend\n\t\tend\n\n\t\tif SaturationFocused and Mouse.IsDragging(1) then\n\t\t\tUpdateSaturation = true\n\t\tend\n\n\t\tif UpdateSaturation then\n\t\t\tlocal CanvasX = max(MouseX - X, 0)\n\t\t\tCanvasX = min(CanvasX, SaturationSize)\n\n\t\t\tlocal CanvasY = max(MouseY - Y, 0)\n\t\t\tCanvasY = min(CanvasY, SaturationSize)\n\n\t\t\tS = CanvasX / SaturationSize\n\t\t\tV = 1 - (CanvasY / SaturationSize)\n\n\t\t\tUpdateColor = true\n\t\tend\n\n\t\tlocal SaturationX = S * SaturationSize\n\t\tlocal SaturationY = (1.0 - V) * SaturationSize\n\t\tDrawCommands.Circle('line', X + SaturationX, Y + SaturationY, 4.0, {1.0, 1.0, 1.0, 1.0})\n\n\t\tX = X + SaturationSize + Cursor.PadX()\n\tend\n\n\tif TintMeshes ~= nil then\n\t\tfor I, V in ipairs(TintMeshes) do\n\t\t\tDrawCommands.Mesh(V, X, Y)\n\t\tend\n\n\t\tWindow.AddItem(X, Y, TintW, TintH)\n\n\t\tlocal UpdateTint = false\n\t\tif X <= MouseX and MouseX < X + TintW and Y <= MouseY and MouseY < Y + TintH then\n\t\t\tif MouseClicked then\n\t\t\t\tTintFocused = true\n\t\t\t\tUpdateTint = true\n\t\t\tend\n\t\tend\n\n\t\tif TintFocused and Mouse.IsDragging(1) then\n\t\t\tUpdateTint = true\n\t\tend\n\n\t\tif UpdateTint then\n\t\t\tlocal CanvasY = max(MouseY - Y, 0)\n\t\t\tCanvasY = min(CanvasY, TintH)\n\n\t\t\tH = CanvasY / TintH\n\n\t\t\tUpdateColor = true\n\t\tend\n\n\t\tlocal TintY = H * TintH\n\t\tDrawCommands.Line(X, Y + TintY, X + TintW, Y + TintY, 2.0, {1.0, 1.0, 1.0, 1.0})\n\n\t\tX = X + TintW + Cursor.PadX()\n\t\tDrawCommands.Mesh(AlphaMesh, X, Y)\n\t\tWindow.AddItem(X, Y, AlphaW, AlphaH)\n\n\t\tlocal UpdateAlpha = false\n\t\tif X <= MouseX and MouseX < X + AlphaW and Y <= MouseY and MouseY < Y + AlphaH then\n\t\t\tif MouseClicked then\n\t\t\t\tAlphaFocused = true\n\t\t\t\tUpdateAlpha = true\n\t\t\tend\n\t\tend\n\n\t\tif AlphaFocused and Mouse.IsDragging(1) then\n\t\t\tUpdateAlpha = true\n\t\tend\n\n\t\tif UpdateAlpha then\n\t\t\tlocal CanvasY = max(MouseY - Y, 0)\n\t\t\tCanvasY = min(CanvasY, AlphaH)\n\n\t\t\tCurrentColor[4] = 1.0 - CanvasY / AlphaH\n\n\t\t\tUpdateColor = true\n\t\tend\n\n\t\tlocal A = 1.0 - CurrentColor[4]\n\t\tlocal AlphaY = A * AlphaH\n\t\tDrawCommands.Line(X, Y + AlphaY, X + AlphaW, Y + AlphaY, 2.0, {A, A, A, 1.0})\n\n\t\tY = Y + AlphaH + Cursor.PadY()\n\tend\n\n\tif UpdateColor then\n\t\tCurrentColor[1], CurrentColor[2], CurrentColor[3] = Utility.HSVtoRGB(H, S, V)\n\t\tUpdateSaturationColors()\n\tend\n\n\tlocal OffsetX = Text.GetWidth(\"##\")\n\tCursor.AdvanceY(SaturationSize)\n\tX, Y = Cursor.GetPosition()\n\tlocal R = CurrentColor[1]\n\tlocal G = CurrentColor[2]\n\tlocal B = CurrentColor[3]\n\tlocal A = CurrentColor[4]\n\n\tCurrentColor[1], R = InputColor(\"R\", R, OffsetX)\n\tCurrentColor[2], G = InputColor(\"G\", G, OffsetX)\n\tCurrentColor[3], B = InputColor(\"B\", B, OffsetX)\n\tCurrentColor[4], A = InputColor(\"A\", A, OffsetX)\n\n\tif R or G or B or A then\n\t\tUpdateSaturationColors()\n\tend\n\n\tlocal InputX, InputY = Cursor.GetPosition()\n\tCursor.SameLine()\n\tX = Cursor.GetX()\n\tCursor.SetY(Y)\n\n\tlocal WinX, WinY, WinW, WinH = Window.GetBounds()\n\tWinW, WinH = Window.GetBorderlessSize()\n\n\tOffsetX = Text.GetWidth(\"####\")\n\tlocal ColorX = X + OffsetX\n\n\tlocal ColorW = (WinX + WinW) - ColorX\n\tCursor.SetPosition(ColorX, Y)\n\tImage.Begin('ColorPicker_CurrentAlpha', {\n\t\tPath = SLAB_FILE_PATH .. \"/Internal/Resources/Textures/Transparency.png\",\n\t\tSubW = ColorW,\n\t\tSubH = ColorH,\n\t\tWrapH = \"repeat\",\n\t\tWrapV = \"repeat\"\n\t})\n\tDrawCommands.Rectangle('fill', ColorX, Y, ColorW, ColorH, CurrentColor, Style.ButtonRounding)\n\n\tlocal LabelW, LabelH = Text.GetSize(\"New\")\n\tCursor.SetPosition(ColorX - LabelW - Cursor.PadX(), Y + (ColorH * 0.5) - (LabelH * 0.5))\n\tText.Begin(\"New\")\n\n\tY = Y + ColorH + Cursor.PadY()\n\n\tCursor.SetPosition(ColorX, Y)\n\tImage.Begin('ColorPicker_CurrentAlpha', {\n\t\tPath = SLAB_FILE_PATH .. \"/Internal/Resources/Textures/Transparency.png\",\n\t\tSubW = ColorW,\n\t\tSubH = ColorH,\n\t\tWrapH = \"repeat\",\n\t\tWrapV = \"repeat\"\n\t})\n\tDrawCommands.Rectangle('fill', ColorX, Y, ColorW, ColorH, Options.Color, Style.ButtonRounding)\n\n\tlocal LabelW, LabelH = Text.GetSize(\"Old\")\n\tCursor.SetPosition(ColorX - LabelW - Cursor.PadX(), Y + (ColorH * 0.5) - (LabelH * 0.5))\n\tText.Begin(\"Old\")\n\n\tif Mouse.IsReleased(1) then\n\t\tSaturationFocused = false\n\t\tTintFocused = false\n\t\tAlphaFocused = false\n\tend\n\n\tCursor.SetPosition(InputX, InputY)\n\tCursor.NewLine()\n\n\tLayoutManager.Begin('ColorPicker_Buttons_Layout', {AlignX = 'right'})\n\tlocal Result = {Button = 0, Color = Utility.MakeColor(CurrentColor)}\n\tif Button.Begin(\"OK\") then\n\t\tResult.Button = 1\n\tend\n\n\tLayoutManager.SameLine()\n\n\tif Button.Begin(\"Cancel\") then\n\t\tResult.Button = -1\n\t\tResult.Color = Utility.MakeColor(Options.Color)\n\tend\n\tLayoutManager.End()\n\n\tWindow.End()\n\n\treturn Result\nend\n\nreturn ColorPicker\n"
  },
  {
    "path": "Internal/UI/ComboBox.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal min = math.min\nlocal max = math.max\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal Input = require(SLAB_PATH .. '.Internal.UI.Input')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Region = require(SLAB_PATH .. '.Internal.UI.Region')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Tooltip = require(SLAB_PATH .. '.Internal.UI.Tooltip')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\n\nlocal ComboBox = {}\nlocal Instances = {}\nlocal Active = nil\n\nlocal MIN_WIDTH = 150.0\nlocal MIN_HEIGHT = 150.0\n\nlocal EMPTY = {}\nlocal IGNORE = { Ignore = true }\nlocal inputRounding = { 0, 0, 0, 0 }\nlocal dropDownRounding = { 0, 0, 0, 0}\n\nlocal function GetInstance(id)\n\tif Instances[id] == nil then\n\t\tlocal instance = {\n\t\t\tIsOpen = false,\n\t\t\tWasOpened = false,\n\t\t\tWinW = 0.0,\n\t\t\tWinH = 0.0,\n\t\t\tStatHandle = nil,\n\t\t\tInputId = id .. '_Input',\n\t\t\tWinId = id .. '_combobox',\n\n\t\t\tInputOptions = {\n\t\t\t\tReadOnly = true,\n\t\t\t\tAlign = 'left',\n\t\t\t\tRounding = inputRounding,\n\t\t\t},\n\n\t\t\tWindowOptions = {\n\t\t\t\tAllowResize = false,\n\t\t\t\tAutoSizeWindow = false,\n\t\t\t\tAllowFocus = false,\n\t\t\t\tAutoSizeContent = true,\n\t\t\t\tNoSavedSettings = true,\n\t\t\t}\n\t\t}\n\t\tInstances[id] = instance\n\tend\n\treturn Instances[id]\nend\n\nfunction ComboBox.Begin(id, options)\n\tlocal StatHandle = Stats.Begin('ComboBox', 'Slab')\n\n\toptions = options or EMPTY\n\tlocal w = options.W or MIN_WIDTH\n\tlocal winH = options.WinH or MIN_HEIGHT\n\tlocal selected = options.Selected or \"\"\n\tlocal rounding = options.Rounding or Style.ComboBoxRounding\n\n\tlocal instance = GetInstance(id)\n\tlocal winItemId = Window.GetItemId(id)\n\tlocal h = Style.Font:getHeight()\n\n\tw = LayoutManager.ComputeSize(w, h)\n\tLayoutManager.AddControl(w, h, 'ComboBox')\n\n\tlocal x, y = Cursor.GetPosition()\n\tlocal radius = h * 0.35\n\tlocal inputBgColor = Style.ComboBoxColor\n\tlocal dropDownW = radius * 4.0\n\tlocal dropDownX = x + w - dropDownW\n\tlocal dropDownColor = Style.ComboBoxDropDownColor\n\n\tinputRounding[1], inputRounding[4] = rounding, rounding\n\tdropDownRounding[2], dropDownRounding[3] = rounding, rounding\n\n\tinstance.X = x\n\tinstance.Y = y\n\tinstance.W = w\n\tinstance.H = h\n\tinstance.WinH = min(instance.WinH, winH)\n\tinstance.StatHandle = StatHandle\n\n\tlocal mouseX, mouseY = Window.GetMousePosition()\n\n\tinstance.WasOpened = instance.IsOpen\n\n\tlocal hovered = not Window.IsObstructedAtMouse() and x <= mouseX and mouseX <= x + w and y <= mouseY and mouseY <= y + h\n\n\tif hovered then\n\t\tinputBgColor = Style.ComboBoxHoveredColor\n\t\tdropDownColor = Style.ComboBoxDropDownHoveredColor\n\n\t\tif Mouse.IsClicked(1) then\n\t\t\tinstance.IsOpen = not instance.IsOpen\n\n\t\t\tif instance.IsOpen then\n\t\t\t\tWindow.SetStackLock(instance.WinId)\n\t\t\tend\n\t\tend\n\tend\n\n\tdo\n\t\tLayoutManager.Begin('Ignore', IGNORE)\n\t\tlocal inputOpts = instance.InputOptions\n\t\tinputOpts.Text = selected\n\t\tinputOpts.W = max(w - dropDownW, dropDownW)\n\t\tinputOpts.H = h\n\t\tinputOpts.BgColor = inputBgColor\n\t\tInput.Begin(instance.InputId, inputOpts)\n\t\tLayoutManager.End()\n\tend\n\n\tCursor.SameLine()\n\n\tDrawCommands.Rectangle('fill', dropDownX, y, dropDownW, h, dropDownColor, dropDownRounding)\n\tDrawCommands.Triangle('fill', dropDownX + radius * 2.0, y + h - radius * 1.35, radius, 180, Style.ComboBoxArrowColor)\n\n\tCursor.SetItemBounds(x, y, w, h)\n\tCursor.AdvanceY(h)\n\n\tif hovered then\n\t\tTooltip.Begin(options.Tooltip or \"\")\n\t\tWindow.SetHotItem(winItemId)\n\tend\n\n\tWindow.AddItem(x, y, w, h, winItemId)\n\n\tlocal winX, winY = Window.TransformPoint(x, y)\n\n\tif instance.IsOpen then\n\t\tLayoutManager.Begin('ComboBox', IGNORE)\n\t\tlocal winOpts = instance.WindowOptions\n\n\t\twinOpts.X = winX - 1.0\n\t\twinOpts.Y = winY + h\n\t\twinOpts.W = max(w, instance.WinW)\n\t\twinOpts.H = instance.WinH\n\t\twinOpts.Layer = Window.GetLayer()\n\t\twinOpts.ContentW = max(w, instance.WinW)\n\t\twinOpts.Border = 4\n\n\t\tWindow.Begin(instance.WinId, winOpts)\n\t\tActive = instance\n\telse\n\t\tStats.End(instance.StatHandle)\n\tend\n\n\treturn instance.IsOpen\nend\n\nfunction ComboBox.End()\n\tlocal y, h = 0, 0\n\tlocal statHandle = Active and Active.StatHandle or nil\n\n\tif Active ~= nil then\n\t\tCursor.SetItemBounds(Active.X, Active.Y, Active.W, Active.H)\n\t\ty, h = Active.Y, Active.H\n\t\tlocal contentW, contentH = Window.GetContentSize()\n\t\tActive.WinH = contentH\n\t\tActive.WinW = max(contentW, Active.W)\n\t\tif Mouse.IsClicked(1) and Active.WasOpened and not Region.IsHoverScrollBar(Window.GetId()) then\n\t\t\tActive.IsOpen = false\n\t\t\tActive = nil\n\t\t\tWindow.SetStackLock(nil)\n\t\tend\n\tend\n\n\tWindow.End()\n\tDrawCommands.SetLayer('Normal')\n\tLayoutManager.End()\n\n\tif y ~= 0 and h ~= 0 then\n\t\tCursor.SetY(y)\n\t\tCursor.AdvanceY(h)\n\tend\n\n\tStats.End(statHandle)\nend\n\nreturn ComboBox\n"
  },
  {
    "path": "Internal/UI/Dialog.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal insert = table.insert\nlocal remove = table.remove\nlocal min = math.min\nlocal max = math.max\nlocal floor = math.floor\nlocal gmatch = string.gmatch\n\nlocal Button = require(SLAB_PATH .. '.Internal.UI.Button')\nlocal ComboBox = require(SLAB_PATH .. '.Internal.UI.ComboBox')\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal FileSystem = require(SLAB_PATH .. '.Internal.Core.FileSystem')\nlocal Image = require(SLAB_PATH .. '.Internal.UI.Image')\nlocal Input = require(SLAB_PATH .. '.Internal.UI.Input')\nlocal Keyboard = require(SLAB_PATH .. '.Internal.Input.Keyboard')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal ListBox = require(SLAB_PATH .. '.Internal.UI.ListBox')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Region = require(SLAB_PATH .. '.Internal.UI.Region')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Text = require(SLAB_PATH .. '.Internal.UI.Text')\nlocal Tree = require(SLAB_PATH .. '.Internal.UI.Tree')\nlocal Utility = require(SLAB_PATH .. '.Internal.Core.Utility')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\nlocal Scale = require(SLAB_PATH .. \".Internal.Core.Scale\")\n\n\nlocal Dialog = {}\nlocal Instances = {}\nlocal ActiveInstance = nil\nlocal Stack = {}\nlocal InstanceStack = {}\nlocal FileDialog_AskOverwrite = false\nlocal FilterW = 0.0\n\nlocal function ValidateSaveFile(Files, Extension)\n\tif Extension == nil or Extension == \"\" then\n\t\treturn\n\tend\n\n\tif Files ~= nil and #Files == 1 then\n\t\tlocal Index = string.find(Files[1], \".\", 1, true)\n\n\t\tif Index ~= nil then\n\t\t\tFiles[1] = string.sub(Files[1], 1, Index - 1)\n\t\tend\n\n\t\tFiles[1] = Files[1] .. Extension\n\tend\nend\n\nlocal function UpdateInputText(Instance)\n\tif Instance ~= nil then\n\t\tif #Instance.Return > 0 then\n\t\t\tInstance.Text = #Instance.Return > 1 and \"<Multiple>\" or Instance.Return[1]\n\t\telse\n\t\t\tInstance.Text = \"\"\n\t\tend\n\tend\nend\n\nlocal function PruneResults(Items, DirectoryOnly)\n\tlocal Result = {}\n\n\tfor I, V in ipairs(Items) do\n\t\tif FileSystem.IsDirectory(V) then\n\t\t\tif DirectoryOnly then\n\t\t\t\tinsert(Result, V)\n\t\t\tend\n\t\telse\n\t\t\tif not DirectoryOnly then\n\t\t\t\tinsert(Result, V)\n\t\t\tend\n\t\tend\n\tend\n\n\treturn Result\nend\n\nlocal function OpenDirectory(Dir)\n\tif ActiveInstance ~= nil and ActiveInstance.Directory ~= nil then\n\t\tActiveInstance.Parsed = false\n\t\tif string.sub(Dir, #Dir, #Dir) == FileSystem.Separator() then\n\t\t\tDir = string.sub(Dir, 1, #Dir - 1)\n\t\tend\n\t\tActiveInstance.Directory = FileSystem.Sanitize(Dir)\n\tend\nend\n\nlocal function FileDialogItem(Id, Label, IsDirectory, Index)\n\tListBox.BeginItem(Id, {Selected = Utility.HasValue(ActiveInstance.Selected, Index)})\n\n\tif IsDirectory then\n\t\tlocal FontH = Style.Font:getHeight()\n\t\tImage.Begin('FileDialog_Folder', {Path = SLAB_FILE_PATH .. \"/Internal/Resources/Textures/Icons.png\", SubX = 0.0, SubY = 0.0, SubW = 50.0, SubH = 50.0, W = FontH, H = FontH})\n\t\tCursor.SameLine({CenterY = true})\n\tend\n\n\tText.Begin(Label)\n\n\tif ListBox.IsItemClicked(1) then\n\t\tlocal Set = true\n\t\tif ActiveInstance.AllowMultiSelect then\n\t\t\tif Keyboard.IsDown('lctrl') or Keyboard.IsDown('rctrl') then\n\t\t\t\tSet = false\n\t\t\t\tif Utility.HasValue(ActiveInstance.Selected, Index) then\n\t\t\t\t\tUtility.Remove(ActiveInstance.Selected, Index)\n\t\t\t\t\tUtility.Remove(ActiveInstance.Return, ActiveInstance.Directory .. \"/\" .. Label)\n\t\t\t\telse\n\t\t\t\t\tinsert(ActiveInstance.Selected, Index)\n\t\t\t\t\tinsert(ActiveInstance.Return, ActiveInstance.Directory .. \"/\" .. Label)\n\t\t\t\tend\n\t\t\telseif Keyboard.IsDown('lshift') or Keyboard.IsDown('rshift') then\n\t\t\t\tif #ActiveInstance.Selected > 0 then\n\t\t\t\t\tSet = false\n\t\t\t\t\tlocal Anchor = ActiveInstance.Selected[#ActiveInstance.Selected]\n\t\t\t\t\tlocal Min = min(Anchor, Index)\n\t\t\t\t\tlocal Max = max(Anchor, Index)\n\n\t\t\t\t\tActiveInstance.Selected = {}\n\t\t\t\t\tActiveInstance.Return = {}\n\t\t\t\t\tfor I = Min, Max, 1 do\n\t\t\t\t\t\tinsert(ActiveInstance.Selected, I)\n\t\t\t\t\t\tif I > #ActiveInstance.Directories then\n\t\t\t\t\t\t\tI = I - #ActiveInstance.Directories\n\t\t\t\t\t\t\tinsert(ActiveInstance.Return, ActiveInstance.Directory .. \"/\" .. ActiveInstance.Files[I])\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tinsert(ActiveInstance.Return, ActiveInstance.Directory .. \"/\" .. ActiveInstance.Directories[I])\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif Set then\n\t\t\tActiveInstance.Selected = {Index}\n\t\t\tActiveInstance.Return = {ActiveInstance.Directory .. \"/\" .. Label}\n\t\tend\n\n\t\tUpdateInputText(ActiveInstance)\n\tend\n\n\tlocal Result = false\n\n\tif ListBox.IsItemClicked(1, true) then\n\t\tif IsDirectory then\n\t\t\tOpenDirectory(ActiveInstance.Directory .. \"/\" .. Label)\n\t\telse\n\t\t\tResult = true\n\t\tend\n\tend\n\n\tListBox.EndItem()\n\n\treturn Result\nend\n\nlocal function AddDirectoryItem(Path)\n\tlocal Separator = FileSystem.Separator()\n\tlocal Item = {}\n\tItem.Path = Path\n\tItem.Name = FileSystem.GetBaseName(Path)\n\tItem.Name = Item.Name == \"\" and Separator or Item.Name\n\t-- Remove the starting slash for Unix style directories.\n\tif string.sub(Item.Name, 1, 1) == Separator and Item.Name ~= Separator then\n\t\tItem.Name = string.sub(Item.Name, 2)\n\tend\n\tItem.Children = nil\n\treturn Item\nend\n\nlocal function FileDialogExplorer(Instance, Root)\n\tif Instance == nil then\n\t\treturn\n\tend\n\n\tif Root ~= nil then\n\t\tlocal ShouldOpen = Window.IsAppearing() and string.find(Instance.Directory, Root.Path, 1, true) ~= nil\n\n\t\tlocal Options = {\n\t\t\tLabel = Root.Name,\n\t\t\tOpenWithHighlight = false,\n\t\t\tIsSelected = ActiveInstance.Directory == Root.Path,\n\t\t\tIsOpen = ShouldOpen\n\t\t}\n\t\tlocal IsOpen = Tree.Begin(Root.Path, Options)\n\n\t\tif Mouse.IsClicked(1) and Window.IsItemHot() then\n\t\t\tOpenDirectory(Root.Path)\n\t\tend\n\n\t\tif IsOpen then\n\t\t\tif Root.Children == nil then\n\t\t\t\tRoot.Children = {}\n\n\t\t\t\tlocal Separator = FileSystem.Separator()\n\t\t\t\tlocal Directories = FileSystem.GetDirectoryItems(Root.Path .. Separator, {Files = false})\n\t\t\t\tfor I, V in ipairs(Directories) do\n\t\t\t\t\tlocal Path = Root.Path\n\t\t\t\t\tif string.sub(Path, #Path) ~= Separator and Path ~= Separator then\n\t\t\t\t\t\tPath = Path .. Separator\n\t\t\t\t\tend\n\t\t\t\t\tif string.sub(V, 1, 1) == Separator then\n\t\t\t\t\t\tV = string.sub(V, 2)\n\t\t\t\t\tend\n\t\t\t\t\tlocal Item = AddDirectoryItem(Path .. FileSystem.GetBaseName(V))\n\t\t\t\t\tinsert(Root.Children, Item)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tfor I, V in ipairs(Root.Children) do\n\t\t\t\tFileDialogExplorer(Instance, V)\n\t\t\tend\n\n\t\t\tTree.End()\n\t\tend\n\tend\nend\n\nlocal function GetFilter(Instance, Index)\n\tlocal Filter = \"*.*\"\n\tlocal Desc = \"All Files\"\n\tif Instance ~= nil and #Instance.Filters > 0 then\n\t\tif Index == nil then\n\t\t\tIndex = Instance.SelectedFilter\n\t\tend\n\n\t\tlocal Item = Instance.Filters[Index]\n\t\tif Item ~= nil then\n\t\t\tif type(Item) == \"table\" then\n\t\t\t\tif #Item == 1 then\n\t\t\t\t\tFilter = Item[1]\n\t\t\t\t\tDesc = \"\"\n\t\t\t\telseif #Item == 2 then\n\t\t\t\t\tFilter = Item[1]\n\t\t\t\t\tDesc = Item[2]\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tFilter = tostring(Item)\n\t\t\t\tDesc = \"\"\n\t\t\tend\n\t\tend\n\tend\n\n\treturn Filter, Desc\nend\n\nlocal function GetExtension(Instance)\n\tlocal Filter, Desc = GetFilter(Instance)\n\tlocal Result = \"\"\n\n\tif Filter ~= \"*.*\" then\n\t\tlocal Index = string.find(Filter, \".\", 1, true)\n\n\t\tif Index ~= nil then\n\t\t\tResult = string.sub(Filter, Index)\n\t\tend\n\tend\n\n\treturn Result\nend\n\nlocal function IsInstanceOpen(Id)\n\tlocal Instance = Instances[Id]\n\tif Instance ~= nil then\n\t\treturn Instance.IsOpen\n\tend\n\treturn false\nend\n\nlocal function GetInstance(Id)\n\tif Instances[Id] == nil then\n\t\tlocal Instance = {}\n\t\tInstance.Id = Id\n\t\tInstance.IsOpen = false\n\t\tInstance.Opening = false\n\t\tInstance.W = 0.0\n\t\tInstance.H = 0.0\n\t\tInstances[Id] = Instance\n\tend\n\treturn Instances[Id]\nend\n\nfunction Dialog.Begin(Id, Options)\n\tlocal Instance = GetInstance(Id)\n\tif not Instance.IsOpen then\n\t\treturn false\n\tend\n\n\tOptions = Options == nil and {} or Options\n\tOptions.X = floor(Scale.GetScreenWidth() * 0.5 - Instance.W * 0.5)\n\tOptions.Y = floor(Scale.GetScreenHeight() * 0.5 - Instance.H * 0.5)\n\tOptions.Layer = 'Dialog'\n\tOptions.AllowFocus = false\n\tOptions.AllowMove = false\n\tOptions.AutoSizeWindow = Options.AutoSizeWindow == nil and true or Options.AutoSizeWindow\n\tOptions.NoSavedSettings = true\n\n\tWindow.Begin(Instance.Id, Options)\n\n\tif Instance.Opening then\n\t\tInput.SetFocused(nil)\n\t\tInstance.Opening = false\n\tend\n\n\tActiveInstance = Instance\n\tinsert(InstanceStack, 1, ActiveInstance)\n\n\treturn true\nend\n\nfunction Dialog.End()\n\tActiveInstance.W, ActiveInstance.H = Window.GetSize()\n\tWindow.End()\n\n\tActiveInstance = nil\n\tremove(InstanceStack, 1)\n\n\tif #InstanceStack > 0 then\n\t\tActiveInstance = InstanceStack[1]\n\tend\nend\n\nfunction Dialog.Open(Id)\n\tlocal Instance = GetInstance(Id)\n\tif not Instance.IsOpen then\n\t\tInstance.Opening = true\n\t\tInstance.IsOpen = true\n\t\tinsert(Stack, 1, Instance)\n\t\tWindow.SetStackLock(Instance.Id)\n\t\tWindow.PushToTop(Instance.Id)\n\tend\nend\n\nfunction Dialog.Close()\n\tif ActiveInstance ~= nil and ActiveInstance.IsOpen then\n\t\tActiveInstance.IsOpen = false\n\t\tremove(Stack, 1)\n\t\tWindow.SetStackLock(nil)\n\n\t\tif #Stack > 0 then\n\t\t\tInstance = Stack[1]\n\t\t\tWindow.SetStackLock(Instance.Id)\n\t\t\tWindow.PushToTop(Instance.Id)\n\t\tend\n\tend\nend\n\nfunction Dialog.IsOpen()\n\treturn #Stack > 0\nend\n\nfunction Dialog.MessageBox(Title, Message, Options)\n    local Result = \"\"\n    Dialog.Open('MessageBox')\n    if Dialog.Begin('MessageBox', {Title = Title, Border = 12}) then\n        Options = Options == nil and {} or Options\n        Options.Buttons = Options.Buttons == nil and {\"OK\"} or Options.Buttons\n\n        LayoutManager.Begin('MessageBox_Message_Layout', {AlignX = 'center', AlignY = 'center'})\n        LayoutManager.NewLine()\n        local TextW = min(Text.GetWidth(Message), Scale.GetScreenWidth() * 0.80)\n        Text.BeginFormatted(Message, {Align = 'center', W = TextW})\n        LayoutManager.End()\n\n        Cursor.NewLine()\n        Cursor.NewLine()\n\n        LayoutManager.Begin('MessageBox_Buttons_Layout', {AlignX = 'right', AlignY = 'bottom'})\n        for I, V in ipairs(Options.Buttons) do\n            if Button.Begin(V) then\n                Result = V\n            end\n            Cursor.SameLine()\n            LayoutManager.SameLine()\n        end\n        LayoutManager.End()\n\n        if Result ~= \"\" then\n            Dialog.Close()\n        end\n\n        Dialog.End()\n    end\n\n    return Result\nend\n\nfunction Dialog.FileDialog(Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.AllowMultiSelect = Options.AllowMultiSelect == nil and true or Options.AllowMultiSelect\n\tOptions.Directory = Options.Directory == nil and nil or Options.Directory\n\tOptions.Type = Options.Type == nil and 'openfile' or Options.Type\n\tOptions.Title = Options.Title == nil and nil or Options.Title\n\tOptions.Filters = Options.Filters == nil and {{\"*.*\", \"All Files\"}} or Options.Filters\n\tOptions.IncludeParent = Options.IncludeParent == nil and true or Options.IncludeParent\n\n\tif Options.Title == nil then\n\t\tOptions.Title = \"Open File\"\n\n\t\tif Options.Type == 'savefile' then\n\t\t\tOptions.AllowMultiSelect = false\n\t\t\tOptions.Title = \"Save File\"\n\t\telseif Options.Type == 'opendirectory' then\n\t\t\tOptions.Title = \"Open Directory\"\n\t\tend\n\tend\n\n\tlocal Result = {Button = \"\", Files = {}}\n\tlocal WasOpen = IsInstanceOpen('FileDialog')\n\n\tDialog.Open(\"FileDialog\")\n\tlocal W = Scale.GetScreenWidth() * 0.65\n\tlocal H = Scale.GetScreenHeight() * 0.65\n\tif Dialog.Begin('FileDialog', {\n\t\tTitle = Options.Title,\n\t\tAutoSizeWindow = false,\n\t\tW = W,\n\t\tH = H,\n\t\tAutoSizeContent = false,\n\t\tAllowResize = false\n\t}) then\n\t\tActiveInstance.AllowMultiSelect = Options.AllowMultiSelect\n\n\t\tif not WasOpen then\n\t\t\tActiveInstance.Text = \"\"\n\t\t\tif ActiveInstance.Directory == nil then\n\t\t\t\tActiveInstance.Directory = love.filesystem.getSourceBaseDirectory()\n\t\t\tend\n\n\t\t\tif Options.Directory ~= nil and FileSystem.IsDirectory(Options.Directory) then\n\t\t\t\tActiveInstance.Directory = Options.Directory\n\t\t\tend\n\n\t\t\tActiveInstance.Filters = Options.Filters\n\t\t\tActiveInstance.SelectedFilter = 1\n\t\tend\n\n\t\tlocal Clear = false\n\t\tif not ActiveInstance.Parsed then\n\t\t\tlocal Filter = GetFilter(ActiveInstance)\n\t\t\tActiveInstance.Root = AddDirectoryItem(FileSystem.GetRootDirectory(ActiveInstance.Directory))\n\t\t\tActiveInstance.Selected = {}\n\t\t\tActiveInstance.Directories = FileSystem.GetDirectoryItems(ActiveInstance.Directory .. \"/\", {Files = false})\n\t\t\tActiveInstance.Files = FileSystem.GetDirectoryItems(ActiveInstance.Directory .. \"/\", {Directories = false, Filter = Filter})\n\t\t\tActiveInstance.Return = {ActiveInstance.Directory .. \"/\"}\n\t\t\tActiveInstance.Text = \"\"\n\t\t\tActiveInstance.Parsed = true\n\n\t\t\tUpdateInputText(ActiveInstance)\n\n\t\t\tfor I, V in ipairs(ActiveInstance.Directories) do\n\t\t\t\tActiveInstance.Directories[I] = FileSystem.GetBaseName(V)\n\t\t\tend\n\n\t\t\tfor I, V in ipairs(ActiveInstance.Files) do\n\t\t\t\tActiveInstance.Files[I] = FileSystem.GetBaseName(V)\n\t\t\tend\n\n\t\t\tClear = true\n\t\tend\n\n\t\tlocal WinW, WinH = Window.GetSize()\n\t\tlocal ButtonW, ButtonH = Button.GetSize(\"OK\")\n\t\tlocal ExplorerW = 150.0\n\t\tlocal ListH = WinH - Text.GetHeight() - ButtonH * 3.0 - Cursor.PadY() * 2.0\n\t\tlocal PrevAnchorX = Cursor.GetAnchorX()\n\n\t\t-- Parent directory button for quick access\n\t\tlocal FontH = Style.Font:getHeight()\n\t\tlocal UpImage = {Path = SLAB_FILE_PATH .. \"/Internal/Resources/Textures/Icons.png\", SubX = 50.0, SubY = 0.0, SubW = 50.0, SubH = 50.0}\n\t\tif Button.Begin(\"\", {Image = UpImage, Color = {0, 0, 0, 0}, PadX = 2, PadY = 2, W = FontH, H = FontH}) then\n\t\t\tlocal Destination = FileSystem.Sanitize(ActiveInstance.Directory .. FileSystem.Separator() .. \"..\")\n\n\t\t\t-- Only attempt to move to parent directory if not the root drive.\n\t\t\tif not FileSystem.IsDrive(Destination) then\n\t\t\t\tOpenDirectory(Destination)\n\t\t\tend\n\t\tend\n\n\t\t-- TODO: Place in region so that it can be scrolled.\n\t\tCursor.SameLine()\n\t\tlocal CursorX, CursorY = Cursor.GetPosition()\n\t\tlocal RemainingW, RemianingH = Window.GetRemainingSize()\n\t\tlocal MouseX, MouseY = Window.GetMousePosition()\n\n\t\tRegion.Begin('FileDialog_BreadCrumbs', {\n\t\t\tX = CursorX,\n\t\t\tY = CursorY,\n\t\t\tW = RemainingW,\n\t\t\tH = FontH,\n\t\t\tAutoSizeContent = true,\n\t\t\tNoBackground = true,\n\t\t\tIntersect = true,\n\t\t\tMouseX = MouseX,\n\t\t\tMouseY = MouseY,\n\t\t\tIsObstructed = Window.IsObstructedAtMouse(),\n\t\t\tRounding = Style.Rounding,\n\t\t\tIgnoreScroll = true\n\t\t})\n\n\t\t-- Add some padding from left border. The cursor internally adds it's own padding.\n\t\tCursor.AdvanceX(0.0)\n\n\t\t-- Gather each directory name and render as bread crumbs on top of each view.\n\t\tlocal Tokens = {}\n\t\tfor Token in gmatch(ActiveInstance.Directory, \"([^\" .. FileSystem.Separator() .. \"]+)\") do insert(Tokens, Token) end\n\t\tfor I, Token in ipairs(Tokens) do\n\t\t\tWindow.PushID(Token .. '_Crumb')\n\n\t\t\tlocal Clicked = Text.Begin(Token, {IsSelectableTextOnly = true})\n\n\t\t\t-- Render an arrow between elements to provide spacing.\n\t\t\tif I < #Tokens then\n\t\t\t\tCursor.SameLine()\n\t\t\t\tImage.Begin(Token .. '_Crumb', {Path = UpImage.Path, SubX = 100.0, SubY = 0.0, SubW = 50.0, SubH = 50.0, W = FontH, H = FontH})\n\t\t\t\tCursor.SameLine()\n\t\t\tend\n\n\t\t\tif Clicked then\n\t\t\t\tlocal Destination = nil\n\t\t\t\tfor J = 1, I, 1 do\n\t\t\t\t\tDestination = Destination and (Destination .. FileSystem.Separator() .. Tokens[J]) or Tokens[J]\n\t\t\t\tend\n\n\t\t\t\tif Destination ~= nil then\n\t\t\t\t\tOpenDirectory(Destination)\n\t\t\t\tend\n\t\t\tend\n\t\t\tWindow.PopID()\n\t\tend\n\n\t\t-- Move the region's scrollable area to always have the current directory in view.\n\t\tlocal ContentW, ContentH = Region.GetContentSize()\n\t\tRegion.ResetTransform()\n\t\tRegion.Translate(nil, math.min(RemainingW - ContentW - 4.0, 0.0), 0.0)\n\t\tRegion.End()\n\t\tRegion.ApplyScissor()\n\n\t\tCursorX, CursorY = Cursor.GetPosition()\n\t\tMouseX, MouseY = Window.GetMousePosition()\n\t\tRegion.Begin('FileDialog_DirectoryExplorer', {\n\t\t\tX = CursorX,\n\t\t\tY = CursorY,\n\t\t\tW = ExplorerW,\n\t\t\tH = ListH,\n\t\t\tAutoSizeContent = true,\n\t\t\tNoBackground = true,\n\t\t\tIntersect = true,\n\t\t\tMouseX = MouseX,\n\t\t\tMouseY = MouseY,\n\t\t\tIsObstructed = Window.IsObstructedAtMouse(),\n\t\t\tRounding = Style.WindowRounding\n\t\t})\n\n\t\tCursor.AdvanceX(0.0)\n\t\tCursor.SetAnchorX(Cursor.GetX())\n\n\t\tFileDialogExplorer(ActiveInstance, ActiveInstance.Root)\n\n\t\tRegion.End()\n\t\tRegion.ApplyScissor()\n\t\tCursor.AdvanceX(ExplorerW + 4.0)\n\t\tCursor.SetY(CursorY)\n\n\t\tLayoutManager.Begin('FileDialog_ListBox_Expand', {AnchorX = true, ExpandW = true})\n\t\tListBox.Begin('FileDialog_ListBox', {H = ListH, Clear = Clear})\n\n\t\tlocal Index = 1\n\t\tlocal ItemSelected = false\n\t\tif Options.IncludeParent then\n\t\t\tif FileDialogItem('Item_Parent', \"..\", true, Index) then\n\t\t\t\tItemSelected = true\n\t\t\tend\n\n\t\t\tIndex = Index + 1\n\t\tend\n\n\t\tfor I, V in ipairs(ActiveInstance.Directories) do\n\t\t\tFileDialogItem('Item_' .. Index, V, true, Index)\n\t\t\tIndex = Index + 1\n\t\tend\n\t\tif Options.Type ~= 'opendirectory' then\n\t\t\tfor I, V in ipairs(ActiveInstance.Files) do\n\t\t\t\tif FileDialogItem('Item_' .. Index, V, false, Index) then\n\t\t\t\t\tItemSelected = true\n\t\t\t\tend\n\t\t\t\tIndex = Index + 1\n\t\t\tend\n\t\tend\n\t\tListBox.End()\n\t\tLayoutManager.End()\n\n\t\tlocal ListBoxX, ListBoxY, ListBoxW, ListBoxH = Cursor.GetItemBounds()\n\t\tlocal InputW = ListBoxX + ListBoxW - PrevAnchorX - FilterW - Cursor.PadX()\n\n\t\tCursor.SetAnchorX(PrevAnchorX)\n\t\tCursor.SetX(PrevAnchorX)\n\n\t\tlocal ReadOnly = Options.Type ~= 'savefile'\n\t\tif Input.Begin('FileDialog_Input', {W = InputW, ReadOnly = ReadOnly, Text = ActiveInstance.Text, Align = 'left'}) then\n\t\t\tActiveInstance.Text = Input.GetText()\n\t\t\tActiveInstance.Return[1] = ActiveInstance.Text\n\t\tend\n\n\t\tCursor.SameLine()\n\n\t\tlocal Filter, Desc = GetFilter(ActiveInstance)\n\t\tif ComboBox.Begin('FileDialog_Filter', {Selected = Filter .. \" \" .. Desc}) then\n\t\t\tfor I, V in ipairs(ActiveInstance.Filters) do\n\t\t\t\tFilter, Desc = GetFilter(ActiveInstance, I)\n\t\t\t\tif Text.Begin(Filter .. \" \" .. Desc, {IsSelectable = true}) then\n\t\t\t\t\tActiveInstance.SelectedFilter = I\n\t\t\t\t\tActiveInstance.Parsed = false\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tComboBox.End()\n\t\tend\n\n\t\tlocal FilterCBX, FilterCBY, FilterCBW, FilterCBH = Cursor.GetItemBounds()\n\t\tFilterW = FilterCBW\n\n\t\tLayoutManager.Begin('FileDialog_Buttons_Layout', {AlignX = 'right', AlignY = 'bottom'})\n\t\tif Button.Begin(\"OK\") or ItemSelected then\n\t\t\tlocal OpeningDirectory = false\n\t\t\tif #ActiveInstance.Return == 1 and Options.Type ~= 'opendirectory' then\n\t\t\t\tlocal Path = ActiveInstance.Return[1]\n\t\t\t\tif FileSystem.IsDirectory(Path) then\n\t\t\t\t\tOpeningDirectory = true\n\t\t\t\t\tOpenDirectory(Path)\n\t\t\t\telseif Options.Type == 'savefile' then\n\t\t\t\t\tif FileSystem.Exists(Path) then\n\t\t\t\t\t\tFileDialog_AskOverwrite = true\n\t\t\t\t\t\tOpeningDirectory = true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif not OpeningDirectory then\n\t\t\t\tResult.Button = \"OK\"\n\t\t\t\tResult.Files = PruneResults(ActiveInstance.Return, Options.Type == 'opendirectory')\n\n\t\t\t\tif Options.Type == 'savefile' then\n\t\t\t\t\tValidateSaveFile(Result.Files, GetExtension(ActiveInstance))\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tCursor.SameLine()\n\t\tLayoutManager.SameLine()\n\n\t\tif Button.Begin(\"Cancel\") then\n\t\t\tResult.Button = \"Cancel\"\n\t\tend\n\t\tLayoutManager.End()\n\n\t\tif FileDialog_AskOverwrite then\n\t\t\tlocal FileName = #ActiveInstance.Return > 0 and ActiveInstance.Return[1] or \"\"\n\t\t\tlocal AskOverwrite = Dialog.MessageBox(\"Overwriting\", \"Are you sure you would like to overwrite file \" .. FileName, {Buttons = {\"Cancel\", \"No\", \"Yes\"}})\n\n\t\t\tif AskOverwrite ~= \"\" then\n\t\t\t\tif AskOverwrite == \"No\" then\n\t\t\t\t\tResult.Button = \"Cancel\"\n\t\t\t\t\tResult.Files = {}\n\t\t\t\telseif AskOverwrite == \"Yes\" then\n\t\t\t\t\tResult.Button = \"OK\"\n\t\t\t\t\tResult.Files = PruneResults(ActiveInstance.Return, Options.Type == 'opendirectory')\n\t\t\t\tend\n\n\t\t\t\tFileDialog_AskOverwrite = false\n\t\t\tend\n\t\tend\n\n\t\tif Result.Button ~= \"\" then\n\t\t\tActiveInstance.Parsed = false\n\t\t\tDialog.Close()\n\t\tend\n\n\t\tDialog.End()\n\tend\n\treturn Result\nend\n\nreturn Dialog\n"
  },
  {
    "path": "Internal/UI/Dock.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal MenuState = require(SLAB_PATH .. '.Internal.UI.MenuState')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Utility = require(SLAB_PATH .. '.Internal.Core.Utility')\nlocal Scale = require(SLAB_PATH .. \".Internal.Core.Scale\")\n\n\nlocal Dock = {}\n\nlocal Instances = {}\nlocal Pending = nil\nlocal PendingWindow = nil\n\nlocal function IsValid(Id)\n\tif Id == nil then\n\t\treturn false\n\tend\n\n\tif type(Id) ~= 'string' then\n\t\treturn false\n\tend\n\n\treturn Id == 'Left' or Id == 'Bottom' or Id == 'Right'\nend\n\nlocal function GetInstance(Id)\n\tif Instances[Id] == nil then\n\t\tlocal Instance = {}\n\t\tInstance.Id = Id\n\t\tInstance.Window = nil\n\t\tInstance.Reset = false\n\t\tInstance.TearX = 0\n\t\tInstance.TearY = 0\n\t\tInstance.IsTearing = false\n\t\tInstance.Torn = false\n\t\tInstance.CachedOptions = nil\n\t\tInstance.Enabled = true\n\t\tInstance.NoSavedSettings = false\n\t\tInstances[Id] = Instance\n\tend\n\treturn Instances[Id]\nend\n\nlocal function GetOverlayBounds(Type)\n\tlocal X, Y, W, H = 0, 0, 0, 0\n\tlocal ViewW, ViewH = Scale.GetScreenDimensions()\n\tlocal Offset = 75\n\n\tif Type == 'Left' then\n\t\tW = 100\n\t\tH = 150\n\t\tX = Offset\n\t\tY = ViewH * 0.5 - H * 0.5\n\telseif Type == 'Right' then\n\t\tW = 100\n\t\tH = 150\n\t\tX = ViewW - Offset - W\n\t\tY = ViewH * 0.5 - H * 0.5\n\telseif Type == 'Bottom' then\n\t\tW = ViewW * 0.55\n\t\tH = 100\n\t\tX = ViewW * 0.5 - W * 0.5\n\t\tY = ViewH - Offset - H\n\tend\n\n\treturn X, Y, W, H\nend\n\nlocal function DrawOverlay(Type)\n\tlocal Instance = GetInstance(Type)\n\tif Instance ~= nil and Instance.Window ~= nil then\n\t\treturn\n\tend\n\n\tif not Instance.Enabled then\n\t\treturn\n\tend\n\n\tlocal X, Y, W, H = GetOverlayBounds(Type)\n\tlocal Color = {0.29, 0.59, 0.83, 0.65}\n\tlocal TitleH = 14\n\tlocal Spacing = 6\n\n\tlocal MouseX, MouseY = Mouse.Position()\n\tif X <= MouseX and MouseX <= X + W and Y <= MouseY and MouseY <= Y + H then\n\t\tColor = {0.50, 0.75, 0.96, 0.65}\n\t\tPending = Type\n\tend\n\n\tDrawCommands.Rectangle('fill', X, Y, W, TitleH, Color)\n\tDrawCommands.Rectangle('line', X, Y, W, TitleH, {0, 0, 0, 1})\n\n\tY = Y + TitleH + Spacing\n\tH = H - TitleH - Spacing\n\tDrawCommands.Rectangle('fill', X, Y, W, H, Color)\n\tDrawCommands.Rectangle('line', X, Y, W, H, {0, 0, 0, 1})\nend\n\nfunction Dock.DrawOverlay()\n\tPending = nil\n\n\tDrawCommands.SetLayer('Dock')\n\tDrawCommands.Begin()\n\n\tDrawOverlay('Left')\n\tDrawOverlay('Right')\n\tDrawOverlay('Bottom')\n\n\tDrawCommands.End()\n\n\tif Mouse.IsReleased(1) then\n\t\tfor Id, Instance in pairs(Instances) do\n\t\t\tInstance.IsTearing = false\n\t\tend\n\tend\nend\n\nfunction Dock.Override()\n\tif Pending ~= nil and PendingWindow ~= nil then\n\t\tlocal Instance = GetInstance(Pending)\n\t\tInstance.Window = PendingWindow.Id\n\t\tInstance.Reset = true\n\t\tPendingWindow = nil\n\t\tPending = nil\n\tend\nend\n\nfunction Dock.Commit()\n\tif Pending ~= nil and PendingWindow ~= nil and Mouse.IsReleased(1) then\n\t\tlocal Instance = GetInstance(Pending)\n\t\tInstance.Window = PendingWindow.Id\n\t\tInstance.Reset = true\n\t\tPendingWindow = nil\n\t\tPending = nil\n\tend\nend\n\nfunction Dock.GetDock(WinId)\n\tfor K, V in pairs(Instances) do\n\t\tif V.Window == WinId then\n\t\t\treturn K\n\t\tend\n\tend\n\n\treturn nil\nend\n\nfunction Dock.GetBounds(Type, Options)\n\tlocal X, Y, W, H = 0, 0, 0, 0\n\tlocal ViewW, ViewH = Scale.GetScreenDimensions()\n\tlocal MainMenuBarH = MenuState.MainMenuBarH\n\tlocal TitleH = Style.Font:getHeight()\n\n\tif Type == 'Left' then\n\t\tY = MainMenuBarH\n\t\tW = Options.W or 150\n\t\tH = ViewH - Y - TitleH\n\telseif Type == 'Right' then\n\t\tX = ViewW - 150\n\t\tY = MainMenuBarH\n\t\tW = Options.W or 150\n\t\tH = ViewH - Y - TitleH\n\telseif Type == 'Bottom' then\n\t\tY = ViewH - 150\n\t\tW = ViewW\n\t\tH = Options.H or 150\n\tend\n\n\treturn X, Y, W, H\nend\n\nfunction Dock.AlterOptions(WinId, Options)\n\tOptions = Options == nil and {} or Options\n\n\tfor Id, Instance in pairs(Instances) do\n\t\tif Instance.Window == WinId then\n\n\t\t\tif Instance.Torn or not Instance.Enabled then\n\t\t\t\tInstance.Window = nil\n\t\t\t\tUtility.CopyValues(Options, Instance.CachedOptions)\n\t\t\t\tInstance.CachedOptions = nil\n\t\t\t\tInstance.Torn = false\n\t\t\t\tOptions.ResetSize = true\n\t\t\telse\n\t\t\t\tif Instance.Reset then\n\t\t\t\t\tInstance.CachedOptions = {\n\t\t\t\t\t\tX = Options.X,\n\t\t\t\t\t\tY = Options.Y,\n\t\t\t\t\t\tW = Options.W,\n\t\t\t\t\t\tH = Options.H,\n\t\t\t\t\t\tAllowMove = Options.AllowMove,\n\t\t\t\t\t\tLayer = Options.Layer,\n\t\t\t\t\t\tSizerFilter = Utility.Copy(Options.SizerFilter),\n\t\t\t\t\t\tAutoSizeWindow = Options.AutoSizeWindow,\n\t\t\t\t\t\tAutoSizeWindowW = Options.AutoSizeWindowW,\n\t\t\t\t\t\tAutoSizeWindowH = Options.AutoSizeWindowH,\n\t\t\t\t\t\tAllowResize = Options.AllowResize\n\t\t\t\t\t}\n\t\t\t\tend\n\n\t\t\t\tOptions.AllowMove = false\n\t\t\t\tOptions.Layer = 'Dock'\n\t\t\t\tif Id == 'Left' then\n\t\t\t\t\tOptions.SizerFilter = {'E'}\n\t\t\t\telseif Id == 'Right' then\n\t\t\t\t\tOptions.SizerFilter = {'W'}\n\t\t\t\telseif Id == 'Bottom' then\n\t\t\t\t\tOptions.SizerFilter = {'N'}\n\t\t\t\tend\n\n\t\t\t\tlocal X, Y, W, H = Dock.GetBounds(Id, Options)\n\t\t\t\tOptions.X = X\n\t\t\t\tOptions.Y = Y\n\t\t\t\tOptions.W = W\n\t\t\t\tOptions.H = H\n\t\t\t\tOptions.AutoSizeWindow = false\n\t\t\t\tOptions.AutoSizeWindowW = false\n\t\t\t\tOptions.AutoSizeWindowH = false\n\t\t\t\tOptions.AllowResize = true\n\t\t\t\tOptions.ResetPosition = Instance.Reset\n\t\t\t\tOptions.ResetSize = Instance.Reset\n\t\t\t\tInstance.Reset = false\n\t\t\tend\n\n\t\t\tbreak\n\t\tend\n\tend\nend\n\nfunction Dock.SetPendingWindow(Instance, Type)\n\tPendingWindow = Instance\n\tPending = Type or Pending\nend\n\nfunction Dock.GetPendingWindow()\n\treturn PendingWindow\nend\n\nfunction Dock.IsTethered(WinId)\n\tfor Id, Instance in pairs(Instances) do\n\t\tif Instance.Window == WinId then\n\t\t\treturn not Instance.Torn\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction Dock.BeginTear(WinId, X, Y)\n\tfor Id, Instance in pairs(Instances) do\n\t\tif Instance.Window == WinId then\n\t\t\tInstance.TearX = X\n\t\t\tInstance.TearY = Y\n\t\t\tInstance.IsTearing = true\n\t\tend\n\tend\nend\n\nfunction Dock.UpdateTear(WinId, X, Y)\n\tfor Id, Instance in pairs(Instances) do\n\t\tif Instance.Window == WinId and Instance.IsTearing then\n\t\t\tlocal Threshold = 25.0\n\t\t\tlocal DistanceX = Instance.TearX - X\n\t\t\tlocal DistanceY = Instance.TearY - Y\n\t\t\tlocal DistanceSq = DistanceX * DistanceX + DistanceY * DistanceY\n\n\t\t\tif DistanceSq >= Threshold * Threshold then\n\t\t\t\tInstance.IsTearing = false\n\t\t\t\tInstance.Torn = true\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction Dock.GetCachedOptions(WinId)\n\tfor Id, Instance in pairs(Instances) do\n\t\tif Instance.Window == WinId then\n\t\t\treturn Instance.CachedOptions\n\t\tend\n\tend\n\n\treturn nil\nend\n\nfunction Dock.Toggle(List, Enabled)\n\tList = List == nil and {} or List\n\tEnabled = Enabled == nil and true or Enabled\n\n\tif type(List) == 'string' then\n\t\tList = {List}\n\tend\n\n\tfor I, V in ipairs(List) do\n\t\tif IsValid(V) then\n\t\t\tlocal Instance = GetInstance(V)\n\t\t\tInstance.Enabled = Enabled\n\t\tend\n\tend\nend\n\nfunction Dock.SetOptions(Type, Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.NoSavedSettings = Options.NoSavedSettings == nil and false or Options.NoSavedSettings\n\n\tif IsValid(Type) then\n\t\tlocal Instance = GetInstance(Type)\n\t\tInstance.NoSavedSettings = Options.NoSavedSettings\n\tend\nend\n\nfunction Dock.Save(Table)\n\tif Table ~= nil then\n\t\tlocal taken = {}\n\t\tlocal Settings = {}\n\t\tfor K, V in pairs(Instances) do\n\t\t\tif not V.NoSavedSettings and V.Window and not taken[V.Window] then\n\t\t\t\tif V.Window then\n\t\t\t\t\ttaken[V.Window] = true\n\t\t\t\tend\n\t\t\t\tSettings[K] = tostring(V.Window)\n\t\t\tend\n\t\tend\n\t\tTable['Dock'] = Settings\n\tend\nend\n\nfunction Dock.Load(Table)\n\tif Table ~= nil then\n\t\tlocal Settings = Table['Dock']\n\t\tif Settings ~= nil then\n\t\t\tfor K, V in pairs(Settings) do\n\t\t\t\tlocal Instance = GetInstance(K)\n\t\t\t\tInstance.Window = V\n\t\t\tend\n\t\tend\n\tend\nend\n\nreturn Dock\n\n"
  },
  {
    "path": "Internal/UI/Image.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Tooltip = require(SLAB_PATH .. '.Internal.UI.Tooltip')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\nlocal IdCache = require(SLAB_PATH .. '.Internal.Core.IdCache')\n\nlocal Image = {}\nlocal instances = {}\nlocal imageCache = {}\nlocal idCache = IdCache()\n\nlocal EMPTY = {}\nlocal WHITE = { 1, 1, 1, 1 }\nlocal BLACK = { 0, 0, 0, 1 }\n\nlocal function GetImage(path)\n\tif imageCache[path] == nil then\n\t\timageCache[path] = love.graphics.newImage(path)\n\tend\n\treturn imageCache[path]\nend\n\nlocal function GetInstance(id)\n\tlocal key = idCache:get(Window.GetId(), id)\n\tlocal instance = instances[key]\n\n\tif instance then return instance end\n\n\tinstance = {}\n\tinstance.Id = id\n\tinstance.Image = nil\n\tinstances[key] = instance\n\n\treturn instance\nend\n\nfunction Image.Begin(id, options)\n\tlocal statHandle = Stats.Begin('Image', 'Slab')\n\n\toptions = options or EMPTY\n\tlocal rotation = options.Rotation or 0\n\tlocal scale = options.Scale or 1\n\tlocal scaleX = options.ScaleX or scale\n\tlocal scaleY = options.ScaleY or scale\n\tlocal color = options.Color or WHITE\n\tlocal subW = options.SubW or 0.0\n\tlocal subH = options.SubH or 0.0\n\n\tlocal instance = GetInstance(id)\n\tlocal winItemId = Window.GetItemId(id)\n\n\n\tif instance.Image == nil then\n\t\tif options.Image == nil then\n\t\t\tassert(options.Path ~= nil, \"Path to an image is required if no image is set!\")\n\t\t\tinstance.Image = GetImage(options.Path)\n\t\telse\n\t\t\tinstance.Image = options.Image\n\t\tend\n\telseif options.Image then\n\t\tif instance.Image ~= options.Image then\n\t\t\tinstance.Image = options.Image\n\t\tend\n\tend\n\n\tinstance.Image:setWrap(options.WrapH or \"clamp\", options.WrapV or \"clamp\")\n\n\tlocal w = options.W or instance.Image:getWidth()\n\tlocal h = options.H or instance.Image:getHeight()\n\n\t-- The final width and height setting will be what the developer requested if it exists. The scale factor will be calculated here.\n\tscaleX = options.W and (options.W / instance.Image:getWidth()) or scaleX\n\tscaleY = options.H and (options.H / instance.Image:getHeight()) or scaleY\n\n\tlocal hasExplicitSize = options.W and options.H\n\tif not hasExplicitSize then\n\t\t-- if the size isn't explictly defined, then apply scaling to the size.\n\t\t-- (If size is explicit, don't apply scaling, because the w,h are already exactly correct.)\n\t\tw = w * scaleX\n\t\th = h * scaleY\n\tend\n\n\tlocal useSubImage = subW > 0.0 and subH > 0.0\n\tif useSubImage then\n\t\tscaleX = options.W and (options.W / subW) or scaleX\n\t\tscaleY = options.H and (options.H / subH) or scaleY\n\tend\n\n\tw, h = LayoutManager.ComputeSize(w, h)\n\tLayoutManager.AddControl(w, h, 'Image')\n\n\tlocal x, y = Cursor.GetPosition()\n\tdo\n\t\tlocal mouseX, mouseY = Window.GetMousePosition()\n\n\t\tif not Window.IsObstructedAtMouse() and x <= mouseX and mouseX <= x + w and y <= mouseY and mouseY <= y + h then\n\t\t\tTooltip.Begin(options.Tooltip or \"\")\n\t\t\tWindow.SetHotItem(winItemId)\n\t\tend\n\tend\n\n\tif useSubImage then\n\t\tDrawCommands.SubImage(\n\t\t\tx,\n\t\t\ty,\n\t\t\tinstance.Image,\n\t\t\toptions.SubX or 0,\n\t\t\toptions.SubY or 0,\n\t\t\tsubW,\n\t\t\tsubH,\n\t\t\trotation,\n\t\t\tscaleX,\n\t\t\tscaleY,\n\t\t\tcolor)\n\telse\n\t\tDrawCommands.Image(x, y, instance.Image, rotation, scaleX, scaleY, color)\n\tend\n\n\tif options.UseOutline then\n\t\tDrawCommands.Rectangle(\n\t\t\t'line',\n\t\t\tx,\n\t\t\ty,\n\t\t\tuseSubImage and subW or w,\n\t\t\tuseSubImage and subH or h,\n\t\t\toptions.OutlineColor or BLACK,\n\t\t\tnil,\n\t\t\tnil,\n\t\t\toptions.OutlineW or 1\n\t\t)\n\tend\n\n\tCursor.SetItemBounds(x, y, w, h)\n\tCursor.AdvanceY(h)\n\n\tWindow.AddItem(x, y, w, h, winItemId)\n\n\tStats.End(statHandle)\nend\n\nfunction Image.GetSize(Image)\n\tif Image ~= nil then\n\t\tlocal Data = Image\n\t\tif type(Image) == 'string' then\n\t\t\tData = GetImage(Image)\n\t\tend\n\n\t\tif Data ~= nil then\n\t\t\treturn Data:getWidth(), Data:getHeight()\n\t\tend\n\tend\n\n\treturn 0, 0\nend\n\nreturn Image\n"
  },
  {
    "path": "Internal/UI/Input.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal abs = math.abs\nlocal insert = table.insert\nlocal min = math.min\nlocal max = math.max\nlocal floor = math.floor\nlocal huge = math.huge\nlocal gsub = string.gsub\nlocal sub = string.sub\nlocal match = string.match\nlocal len = string.len\nlocal byte = string.byte\nlocal find = string.find\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal FileSystem = require(SLAB_PATH .. '.Internal.Core.FileSystem')\nlocal Keyboard = require(SLAB_PATH .. '.Internal.Input.Keyboard')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Region = require(SLAB_PATH .. '.Internal.UI.Region')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Text = require(SLAB_PATH .. '.Internal.UI.Text')\nlocal Tooltip = require(SLAB_PATH .. '.Internal.UI.Tooltip')\nlocal UTF8 = require('utf8')\nlocal Utility = require(SLAB_PATH .. '.Internal.Core.Utility')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\n\nlocal Input = {}\nlocal Instances = {}\nlocal Focused = nil\nlocal LastFocused = nil\nlocal TextCursorPos = 0\nlocal TextCursorPosLine = 0\nlocal TextCursorPosLineMax = 0\nlocal TextCursorPosLineNumber = 1\nlocal TextCursorAnchor = -1\nlocal TextCursorAlpha = 0.0\nlocal FadeIn = true\nlocal DragSelect = false\nlocal FocusToNext = false\nlocal LastText = \"\"\nlocal Pad = Region.GetScrollPad() + Region.GetScrollBarSize()\nlocal PendingFocus = nil\nlocal PendingCursorPos = -1\nlocal PendingCursorColumn = -1\nlocal PendingCursorLine = -1\nlocal IsSliding = false\nlocal DragDelta = 0\n\nlocal MIN_WIDTH = 150.0\nlocal DEF_PW_CHAR = \"*\"\nlocal LINE_NUMBER_W = 32\n\nlocal function SanitizeText(Data)\n\tlocal Result = false\n\n\tif Data ~= nil then\n\t\tlocal Count = 0\n\t\tData, Count = gsub(Data, \"\\r\", \"\")\n\t\tResult = Count > 0\n\tend\n\n\treturn Data, Result\nend\n\nlocal function UpdateLineNumbers(Instance, W, H, n_right, color)\n\tif not Instance.LineNumbers then return end\n\tn_right = n_right or 0\n\tlocal n_lines = max(H/Text.GetHeight(), n_right)\n\tlocal line_numbers = {}\n\tfor i = 0, n_lines do\n\t\ttable.insert(line_numbers, Instance.LineNumbersStart + i)\n\tend\n\n\tlocal lines = table.concat(line_numbers, \"\\r\\n\")\n\tif color then\n\t\tlocal colored_lines = {color, lines}\n\t\tInstance.LineNumbersObject:setf(colored_lines, W - 2, \"right\")\n\telse\n\t\tInstance.LineNumbersObject:setf(lines, W - 2, \"right\")\n\tend\nend\n\n\nlocal function GetDisplayCharacter(Data, Pos)\n\tlocal Result = ''\n\n\tif Data ~= nil and Pos > 0 and Pos < len(Data) then\n\t\tlocal Offset = UTF8.offset(Data, -1, Pos + 1)\n\t\tResult = sub(Data, Offset, Pos)\n\n\t\tif Result == nil then\n\t\t\tResult = 'nil'\n\t\tend\n\tend\n\n\tif Result == '\\n' then\n\t\tResult = \"\\\\n\"\n\tend\n\n\treturn Result\nend\n\nlocal function GetCharacter(Data, Index, Forward)\n\tlocal Result = \"\"\n\tif Forward then\n\t\tlocal Sub = sub(Data, Index + 1)\n\t\tResult = match(Sub, \"[%z\\1-\\127\\194-\\244%s\\n][\\128-\\191]*\")\n\telse\n\t\tlocal Sub = sub(Data, 1, Index)\n\t\tResult = match(Sub, \"[%z\\1-\\127\\194-\\244%s\\n][\\128-\\191]*$\")\n\tend\n\treturn Result\nend\n\nlocal function UpdateMultiLinePosition(Instance)\n\tif Instance == nil then return end\n\tif Instance.Lines ~= nil then\n\t\tlocal Count = 0\n\t\tlocal Start = 0\n\t\tlocal Found = false\n\t\tfor I, V in ipairs(Instance.Lines) do\n\t\t\tlocal Length = len(V)\n\t\t\tCount = Count + Length\n\t\t\tif TextCursorPos < Count then\n\t\t\t\tTextCursorPosLine = TextCursorPos - Start\n\t\t\t\tTextCursorPosLineNumber = I\n\t\t\t\tFound = true\n\t\t\t\tbreak\n\t\t\tend\n\t\t\tStart = Start + Length\n\t\tend\n\n\t\tif not Found then\n\t\t\tTextCursorPosLine = len(Instance.Lines[#Instance.Lines])\n\t\t\tTextCursorPosLineNumber = #Instance.Lines\n\t\tend\n\telse\n\t\tTextCursorPosLine = TextCursorPos\n\t\tTextCursorPosLineNumber = 1\n\tend\n\tTextCursorPosLineMax = TextCursorPosLine\nend\n\nlocal function ValidateTextCursorPos(Instance)\n\tif Instance ~= nil then\n\t\tlocal OldPos = TextCursorPos\n\t\tlocal Byte = byte(sub(Instance.Text, TextCursorPos, TextCursorPos))\n\t\t-- This is a continuation byte. Check next byte to see if it is an ASCII character or\n\t\t-- the beginning of a UTF8 character.\n\t\tif Byte ~= nil and Byte > 127 then\n\t\t\tlocal NextByte = byte(sub(Instance.Text, TextCursorPos + 1, TextCursorPos + 1))\n\t\t\tif NextByte ~= nil and NextByte > 127 and NextByte < 191 then\n\t\t\t\twhile Byte > 127 and Byte < 191 do\n\t\t\t\t\tTextCursorPos = TextCursorPos - 1\n\t\t\t\t\tByte = byte(sub(Instance.Text, TextCursorPos, TextCursorPos))\n\t\t\t\tend\n\n\t\t\t\tif TextCursorPos < OldPos or Byte >= 191 then\n\t\t\t\t\tTextCursorPos = TextCursorPos - 1\n\t\t\t\t\tUpdateMultiLinePosition(Instance)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function MoveToHome(Instance)\n\tif Instance ~= nil then\n\t\tif Instance.Lines ~= nil and TextCursorPosLineNumber > 1 then\n\t\t\tTextCursorPosLine = 0\n\t\t\tlocal Count = 0\n\t\t\tlocal Start = 0\n\t\t\tfor I, V in ipairs(Instance.Lines) do\n\t\t\t\tCount = Count + len(V)\n\t\t\t\tif I == TextCursorPosLineNumber then\n\t\t\t\t\tTextCursorPos = Start\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\tStart = Start + len(V)\n\t\t\tend\n\t\telse\n\t\t\tTextCursorPos = 0\n\t\tend\n\t\tUpdateMultiLinePosition(Instance)\n\tend\nend\n\nlocal function MoveToEnd(Instance)\n\tif Instance ~= nil then\n\t\tif Instance.Lines ~= nil then\n\t\t\tlocal Count = 0\n\t\t\tfor I, V in ipairs(Instance.Lines) do\n\t\t\t\tCount = Count + len(V)\n\t\t\t\tif I == TextCursorPosLineNumber then\n\t\t\t\t\tTextCursorPos = Count - 1\n\n\t\t\t\t\tif I == #Instance.Lines then\n\t\t\t\t\t\tTextCursorPos = Count\n\t\t\t\t\tend\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\tTextCursorPos = #Instance.Text\n\t\tend\n\t\tUpdateMultiLinePosition(Instance)\n\tend\nend\n\nlocal function ValidateNumber(Instance)\n\tlocal Result = false\n\n\tif Instance ~= nil and Instance.NumbersOnly and Instance.Text ~= \"\" then\n\t\tif sub(Instance.Text, #Instance.Text, #Instance.Text) == \".\" then\n\t\t\treturn\n\t\tend\n\n\t\tlocal Value = tonumber(Instance.Text)\n\t\tif Value == nil then\n\t\t\tValue = 0.0\n\t\tend\n\n\t\tlocal OldValue = Value\n\n\t\tif Instance.MinNumber ~= nil then\n\t\t\tValue = max(Value, Instance.MinNumber)\n\t\tend\n\t\tif Instance.MaxNumber ~= nil then\n\t\t\tValue = min(Value, Instance.MaxNumber)\n\t\tend\n\n\t\tResult = OldValue ~= Value\n\n\t\tInstance.Text = tostring(Value)\n\tend\n\n\treturn Result\nend\n\nlocal function GetAlignmentOffset(Instance)\n\tlocal Offset = 6.0\n\tif Instance ~= nil then\n\t\tif Instance.Align == 'center' then\n\t\t\tlocal TextW = Text.GetWidth(Instance.Text)\n\t\t\tOffset = (Instance.W * 0.5) - (TextW * 0.5)\n\t\tend\n\tend\n\treturn Offset\nend\n\nlocal function GetSelection(Instance)\n\tif Instance ~= nil and TextCursorAnchor >= 0 and TextCursorAnchor ~= TextCursorPos then\n\t\tlocal Min = min(TextCursorAnchor, TextCursorPos) + 1\n\t\tlocal Max = max(TextCursorAnchor, TextCursorPos)\n\n\t\treturn sub(Instance.Text, Min, Max)\n\tend\n\treturn \"\"\nend\n\nlocal function MoveCursorVertical(Instance, MoveDown)\n\tif (Instance == nil) or (Instance.Lines == nil) then return end\n\tlocal OldLineNumber = TextCursorPosLineNumber\n\tif MoveDown then\n\t\tTextCursorPosLineNumber = min(TextCursorPosLineNumber + 1, #Instance.Lines)\n\telse\n\t\tTextCursorPosLineNumber = max(1, TextCursorPosLineNumber - 1)\n\tend\n\tlocal Line = Instance.Lines[TextCursorPosLineNumber]\n\tif OldLineNumber == TextCursorPosLineNumber then\n\t\tTextCursorPosLine = MoveDown and len(Line) or 0\n\telse\n\t\tif TextCursorPosLineNumber == #Instance.Lines and TextCursorPosLine >= len(Line) then\n\t\t\tTextCursorPosLine = len(Line)\n\t\telse\n\t\t\tTextCursorPosLine = min(len(Line), TextCursorPosLineMax + 1)\n\t\t\tlocal Ch = GetCharacter(Line, TextCursorPosLine)\n\t\t\tif Ch ~= nil then\n\t\t\t\tTextCursorPosLine = TextCursorPosLine - len(Ch)\n\t\t\tend\n\t\tend\n\tend\n\tlocal Start = 0\n\tfor I, V in ipairs(Instance.Lines) do\n\t\tif I == TextCursorPosLineNumber then\n\t\t\tTextCursorPos = Start + TextCursorPosLine\n\t\t\tbreak\n\t\tend\n\t\tStart = Start + len(V)\n\tend\nend\n\nlocal function IsValidDigit(Instance, Ch)\n\tif Instance ~= nil then\n\t\tif Instance.NumbersOnly then\n\t\t\tif match(Ch, \"%d\") ~= nil then\n\t\t\t\treturn true\n\t\t\tend\n\n\t\t\tif Ch == \"-\" then\n\t\t\t\tif TextCursorAnchor == 0 or TextCursorPos == 0 or #Instance.Text == 0 then\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif Ch == \".\" then\n\t\t\t\tlocal Selected = GetSelection(Instance)\n\t\t\t\tif Selected ~= nil and find(Selected, \".\", 1, true) ~= nil then\n\t\t\t\t\treturn true\n\t\t\t\tend\n\n\t\t\t\tif find(Instance.Text, \".\", 1, true) == nil then\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nlocal function IsCommandKeyDown()\n\tlocal LKey, RKey = 'lctrl', 'rctrl'\n\tif Utility.IsOSX() then\n\t\tLKey, RKey = 'lgui', 'rgui'\n\tend\n\treturn Keyboard.IsDown(LKey) or Keyboard.IsDown(RKey)\nend\n\nlocal function IsHomePressed()\n\tlocal Result = false\n\tif Utility.IsOSX() then\n\t\tResult = IsCommandKeyDown() and Keyboard.IsPressed('left')\n\telse\n\t\tResult = Keyboard.IsPressed('home')\n\tend\n\treturn Result\nend\n\nlocal function IsEndPressed()\n\tlocal Result = false\n\tif Utility.IsOSX() then\n\t\tResult = IsCommandKeyDown() and Keyboard.IsPressed('right')\n\telse\n\t\tResult = Keyboard.IsPressed('end')\n\tend\n\treturn Result\nend\n\nlocal function IsNextSpaceDown()\n\tlocal Result = false\n\tif Utility.IsOSX() then\n\t\tResult = Keyboard.IsDown('lalt') or Keyboard.IsDown('ralt')\n\telse\n\t\tResult = Keyboard.IsDown('lctrl') or Keyboard.IsDown('rctrl')\n\tend\n\treturn Result\nend\n\nlocal function GetCursorXOffset(Instance)\n\tlocal Result = GetAlignmentOffset(Instance)\n\tif Instance ~= nil then\n\t\tif TextCursorPos > 0 then\n\t\t\tlocal Sub = sub(Instance.Text, 1, TextCursorPos)\n\t\t\tResult = Text.GetWidth(Sub) + GetAlignmentOffset(Instance)\n\t\tend\n\tend\n\treturn Result\nend\n\nlocal function GetCursorPos(Instance)\n\tlocal X, Y = GetAlignmentOffset(Instance), 0.0\n\n\tif Instance ~= nil then\n\t\tlocal Data = Instance.Text\n\t\tif Instance.Lines ~= nil then\n\t\t\tData = Instance.Lines[TextCursorPosLineNumber]\n\t\t\tY = Text.GetHeight() * (TextCursorPosLineNumber - 1)\n\t\tend\n\t\tlocal CursorPos = min(TextCursorPosLine, len(Data))\n\t\tif CursorPos > 0 then\n\t\t\tlocal Sub = sub(Data, 0, CursorPos)\n\t\t\tX = X + Text.GetWidth(Sub)\n\t\tend\n\tend\n\n\treturn X, Y\nend\n\nlocal function SelectWord(Instance)\n\tif Instance ~= nil then\n\t\tlocal Filter = \"%s\"\n\t\tif GetCharacter(Instance.Text, TextCursorPos) == \" \" then\n\t\t\tif GetCharacter(Instance.Text, TextCursorPos + 1) == \" \" then\n\t\t\t\tFilter = \"%S\"\n\t\t\telse\n\t\t\t\tTextCursorPos = TextCursorPos + 1\n\t\t\tend\n\t\tend\n\t\tTextCursorAnchor = 0\n\t\tlocal I = 0\n\t\twhile I ~= nil and I + 1 < TextCursorPos do\n\t\t\tI = find(Instance.Text, Filter, I + 1)\n\t\t\tif I ~= nil and I < TextCursorPos then\n\t\t\t\tTextCursorAnchor = I\n\t\t\telse\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\t\tI = find(Instance.Text, Filter, TextCursorPos + 1)\n\t\tif I ~= nil then\n\t\t\tTextCursorPos = I - 1\n\t\telse\n\t\t\tTextCursorPos = #Instance.Text\n\t\tend\n\t\tUpdateMultiLinePosition(Instance)\n\tend\nend\n\nlocal function GetNextCursorPos(Instance, Left)\n\tlocal Result = 0\n\tif Instance ~= nil then\n\t\tlocal NextSpace = IsNextSpaceDown()\n\n\t\tif NextSpace then\n\t\t\tif Left then\n\t\t\t\tResult = 0\n\t\t\t\tlocal I = 0\n\t\t\t\twhile I ~= nil and I + 1 < TextCursorPos do\n\t\t\t\t\tI = find(Instance.Text, \"%s\", I + 1)\n\t\t\t\t\tif I ~= nil and I < TextCursorPos then\n\t\t\t\t\t\tResult = I\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tlocal I = find(Instance.Text, \"%s\", TextCursorPos + 1)\n\t\t\t\tif I ~= nil then\n\t\t\t\t\tResult = I\n\t\t\t\telse\n\t\t\t\t\tResult = #Instance.Text\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\tif Left then\n\t\t\t\tlocal Ch = GetCharacter(Instance.Text, TextCursorPos)\n\t\t\t\tif Ch ~= nil then\n\t\t\t\t\tResult = TextCursorPos - len(Ch)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tlocal Ch = GetCharacter(Instance.Text, TextCursorPos, true)\n\t\t\t\tif Ch ~= nil then\n\t\t\t\t\tResult = TextCursorPos + len(Ch)\n\t\t\t\telse\n\t\t\t\t\tResult = TextCursorPos\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tResult = max(0, Result)\n\t\tResult = min(Result, len(Instance.Text))\n\tend\n\treturn Result\nend\n\nlocal function GetCursorPosLine(Instance, Line, X)\n\tlocal Result = 0\n\tif Instance ~= nil and Line ~= \"\" then\n\t\tif Text.GetWidth(Line) < X then\n\t\t\tResult = len(Line)\n\t\t\tif find(Line, \"\\n\") ~= nil then\n\t\t\t\tResult = len(Line) - 1\n\t\t\tend\n\t\telse\n\t\t\tX = X - GetAlignmentOffset(Instance)\n\t\t\tlocal PosX = X\n\t\t\tlocal Index = 0\n\t\t\tlocal Sub = \"\"\n\t\t\twhile Index <= len(Line) do\n\t\t\t\tlocal Ch = GetCharacter(Line, Index, true)\n\t\t\t\tif Ch == nil then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\tIndex = Index + len(Ch)\n\t\t\t\tSub = Sub .. Ch\n\t\t\t\tlocal PosX = Text.GetWidth(Sub)\n\t\t\t\tif PosX > X then\n\t\t\t\t\tlocal CharX = PosX - X\n\t\t\t\t\tlocal CharW = Text.GetWidth(Ch)\n\t\t\t\t\tif CharX < CharW * 0.65 then\n\t\t\t\t\t\tResult = Result + len(Ch)\n\t\t\t\t\tend\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\tResult = Index\n\t\t\tend\n\t\tend\n\tend\n\treturn Result\nend\n\nlocal function GetTextCursorPos(Instance, X, Y)\n\tlocal Result = 0\n\tif Instance ~= nil then\n\t\tlocal Line = Instance.Text\n\t\tlocal Start = 0\n\n\t\tif Instance.Lines ~= nil and #Instance.Lines > 0 then\n\t\t\tlocal H = Text.GetHeight()\n\t\t\tlocal LineNumber = 1\n\t\t\tlocal Found = false\n\t\t\tfor I, V in ipairs(Instance.Lines) do\n\t\t\t\tif Y <= H then\n\t\t\t\t\tLine = V\n\t\t\t\t\tFound = true\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\tH = H + Text.GetHeight()\n\t\t\t\tStart = Start + #V\n\t\t\tend\n\n\t\t\tif not Found then\n\t\t\t\tLine = Instance.Lines[#Instance.Lines]\n\t\t\tend\n\t\tend\n\n\t\tResult = min(Start + GetCursorPosLine(Instance, Line, X), #Instance.Text)\n\tend\n\treturn Result\nend\n\nlocal function MoveCursorPage(Instance, PageDown)\n\tif Instance ~= nil then\n\t\tlocal PageH = Instance.H - Text.GetHeight()\n\t\tlocal PageY = PageDown and PageH or 0.0\n\t\tlocal X, Y = GetCursorPos(Instance)\n\t\tlocal TX, TY = Region.InverseTransform(Instance.Id, 0.0, PageY)\n\t\tlocal NextY = 0.0\n\t\tif PageDown then\n\t\t\tNextY = TY + PageH\n\t\telse\n\t\t\tNextY = max(TY - PageH, 0.0)\n\t\tend\n\n\t\tTextCursorPos = GetTextCursorPos(Instance, 0.0, NextY)\n\t\tUpdateMultiLinePosition(Instance)\n\tend\nend\n\nlocal function UpdateTransform(Instance)\n\tif Instance == nil then return end\n\tlocal X, Y = GetCursorPos(Instance)\n\n\tlocal TX, TY = Region.InverseTransform(Instance.Id, 0.0, 0.0)\n\tlocal W = TX + Instance.W - Region.GetScrollPad() - Region.GetScrollBarSize()\n\tlocal H = TY + Instance.H\n\n\tif Instance.H > Text.GetHeight() then\n\t\tH = H - Region.GetScrollPad() - Region.GetScrollBarSize()\n\tend\n\n\tlocal NewX = 0.0\n\tif TextCursorPosLine == 0 then\n\t\tNewX = TX\n\telseif X > W then\n\t\tNewX = -(X - W)\n\telseif X < TX then\n\t\tNewX = TX - X\n\tend\n\n\tlocal NewY = 0.0\n\tif TextCursorPosLineNumber == 1 then\n\t\tNewY = TY\n\telseif Y > H then\n\t\tNewY = -(Y - H)\n\telseif Y < TY then\n\t\tNewY = TY - Y\n\tend\n\n\tRegion.Translate(Instance.Id, NewX, NewY)\n\tif Instance.LineNumbers then\n\t\tRegion.Translate(Instance.Id .. \"LineNumbers\", NewX, NewY)\n\tend\nend\n\nlocal function DeleteSelection(Instance)\n\tif Instance ~= nil and Instance.Text ~= \"\" and not Instance.ReadOnly then\n\t\tlocal Start = 0\n\t\tlocal Min = 0\n\t\tlocal Max = 0\n\n\t\tif TextCursorAnchor ~= -1 then\n\t\t\tMin = min(TextCursorAnchor, TextCursorPos)\n\t\t\tMax = max(TextCursorAnchor, TextCursorPos) + 1\n\t\telse\n\t\t\tif TextCursorPos == 0 then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tlocal NewTextCursorPos = TextCursorPos\n\t\t\tlocal Ch = GetCharacter(Instance.Text, TextCursorPos)\n\t\t\tif Ch ~= nil then\n\t\t\t\tMin = TextCursorPos - len(Ch)\n\t\t\t\tNewTextCursorPos = Min\n\t\t\tend\n\n\t\t\tCh = GetCharacter(Instance.Text, TextCursorPos, true)\n\t\t\tif Ch ~= nil then\n\t\t\t\tMax = TextCursorPos + 1\n\t\t\telse\n\t\t\t\tMax = len(Instance.Text) + 1\n\t\t\tend\n\n\t\t\tTextCursorPos = NewTextCursorPos\n\t\tend\n\n\t\tlocal Left = sub(Instance.Text, 1, Min)\n\t\tlocal Right = sub(Instance.Text, Max)\n\t\tInstance.Text = Left .. Right\n\n\t\tif Instance.IsPassword then\n\t\t\tlocal Left = sub(Instance.OrigText, 1, Min)\n\t\t\tlocal Right = sub(Instance.OrigText, Max)\n\t\t\tInstance.OrigText = Left .. Right\n\t\t\tInstance.PasswordText = string.rep(Instance.PasswordChar, #Instance.OrigText)\n\t\tend\n\n\t\tTextCursorPos = len(Left)\n\n\t\tif TextCursorAnchor ~= -1 then\n\t\t\tTextCursorPos = min(TextCursorAnchor, TextCursorPos)\n\t\tend\n\t\tTextCursorPos = max(0, TextCursorPos)\n\t\tTextCursorPos = min(TextCursorPos, len(Instance.Text))\n\n\t\tTextCursorAnchor = -1\n\t\tUpdateMultiLinePosition(Instance)\n\tend\n\treturn true\nend\n\nlocal function DrawSelection(Instance, X, Y, W, H, Color)\n\tif Instance ~= nil and TextCursorAnchor >= 0 and TextCursorAnchor ~= TextCursorPos then\n\t\tlocal Min = min(TextCursorAnchor, TextCursorPos)\n\t\tlocal Max = max(TextCursorAnchor, TextCursorPos)\n\t\tH = Text.GetHeight()\n\n\t\tif Instance.Lines ~= nil then\n\t\t\tlocal Count = 0\n\t\t\tlocal Start = 0\n\t\t\tlocal OffsetMin = 0\n\t\t\tlocal OffsetMax = 0\n\t\t\tlocal OffsetY = 0\n\t\t\tfor I, V in ipairs(Instance.Lines) do\n\t\t\t\tCount = Count + len(V)\n\t\t\t\tif Min < Count then\n\t\t\t\t\tif Min > Start then\n\t\t\t\t\t\tOffsetMin = max(Min - Start, 1)\n\t\t\t\t\telse\n\t\t\t\t\t\tOffsetMin = 0\n\t\t\t\t\tend\n\n\t\t\t\t\tif Max < Count then\n\t\t\t\t\t\tOffsetMax = max(Max - Start, 1)\n\t\t\t\t\telse\n\t\t\t\t\t\tOffsetMax = len(V)\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal SubMin = sub(V, 1, OffsetMin)\n\t\t\t\t\tlocal SubMax = sub(V, 1, OffsetMax)\n\t\t\t\t\tlocal MinX = Text.GetWidth(SubMin) - 1.0 + GetAlignmentOffset(Instance)\n\t\t\t\t\tlocal MaxX = Text.GetWidth(SubMax) + 1.0 + GetAlignmentOffset(Instance)\n\n\t\t\t\t\tDrawCommands.Rectangle('fill', X + MinX, Y + OffsetY, MaxX - MinX, H, Color)\n\t\t\t\tend\n\n\t\t\t\tif Max <= Count then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\tStart = Start + len(V)\n\t\t\t\tOffsetY = OffsetY + H\n\t\t\tend\n\t\telse\n\t\t\tlocal SubMin = sub(Instance.Text, 1, Min)\n\t\t\tlocal SubMax = sub(Instance.Text, 1, Max)\n\t\t\tlocal MinX = Text.GetWidth(SubMin) - 1.0 + GetAlignmentOffset(Instance)\n\t\t\tlocal MaxX = Text.GetWidth(SubMax) + 1.0 + GetAlignmentOffset(Instance)\n\n\t\t\tDrawCommands.Rectangle('fill', X + MinX, Y, MaxX - MinX, H, Color)\n\t\tend\n\tend\nend\n\nlocal function DrawCursor(Instance, X, Y, W, H)\n\tif Instance ~= nil then\n\t\tlocal CX, CY = GetCursorPos(Instance)\n\t\tlocal CX = X + CX\n\t\tlocal CY = Y + CY\n\t\tH = Text.GetHeight()\n\n\t\tDrawCommands.Line(CX, CY, CX, CY + H, 1.0, {0.0, 0.0, 0.0, TextCursorAlpha})\n\tend\nend\n\nlocal function IsHighlightTerminator(Ch)\n\tif Ch ~= nil then\n\t\treturn match(Ch, \"%w\") == nil\n\tend\n\n\treturn true\nend\n\nlocal function UpdateTextObject(Instance, Width, Align, Highlight, BaseColor)\n\tif (Instance == nil) or (Instance.TextObject == nil) then return end\n\n\tlocal ColoredText = {}\n\n\tif Highlight ~= nil then\n\t\t--local StartTime = love.timer.getTime()\n\n\t\tlocal _, TY = Region.InverseTransform(Instance.Id, 0, 0)\n\t\tlocal TextH = Text.GetHeight()\n\t\tlocal Top = TY - TextH * 2\n\t\tlocal Bottom = TY + Instance.H + TextH * 2\n\t\tlocal H = #Instance.Lines * TextH\n\t\tlocal TopLineNo = max(floor((Top / H) * #Instance.Lines), 1)\n\t\tlocal BottomLineNo = min(floor((Bottom / H) * #Instance.Lines), #Instance.Lines)\n\n\t\tlocal Index = 1\n\t\tlocal EndIndex = 1\n\t\tfor I = 1, BottomLineNo, 1 do\n\t\t\tlocal Count = len(Instance.Lines[I])\n\t\t\tif I < TopLineNo then\n\t\t\t\tIndex = Index + Count\n\t\t\tend\n\n\t\t\tEndIndex = EndIndex + Count\n\t\tend\n\n\t\tif Index > 1 then\n\t\t\tinsert(ColoredText, BaseColor)\n\t\t\tinsert(ColoredText, sub(Instance.Text, 1, Index - 1))\n\t\tend\n\n\t\twhile Index < EndIndex do\n\t\t\tlocal MatchIndex = nil\n\t\t\tlocal Key = nil\n\t\t\tfor K, V in pairs(Highlight) do\n\t\t\t\tlocal Found = nil\n\t\t\t\tlocal Anchor = Index\n\t\t\t\trepeat\n\t\t\t\t\tFound = find(Instance.Text, K, Anchor, true)\n\n\t\t\t\t\tif Found ~= nil then\n\t\t\t\t\t\tlocal FoundEnd = Found + len(K)\n\t\t\t\t\t\tlocal Prev = sub(Instance.Text, Found - 1, Found - 1)\n\t\t\t\t\t\tlocal Next = sub(Instance.Text, FoundEnd, FoundEnd)\n\n\t\t\t\t\t\tif Found == 1 then\n\t\t\t\t\t\t\tPrev = nil\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif FoundEnd > len(Instance.Text) then\n\t\t\t\t\t\t\tNext = nil\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif not (IsHighlightTerminator(Prev) and IsHighlightTerminator(Next)) then\n\t\t\t\t\t\t\tAnchor = Found + 1\n\t\t\t\t\t\t\tFound = nil\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tuntil Found ~= nil\n\n\t\t\t\tif Found ~= nil then\n\t\t\t\t\tif MatchIndex == nil then\n\t\t\t\t\t\tMatchIndex = Found\n\t\t\t\t\t\tKey = K\n\t\t\t\t\telseif Found < MatchIndex then\n\t\t\t\t\t\tMatchIndex = Found\n\t\t\t\t\t\tKey = K\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif Key ~= nil then\n\t\t\t\tinsert(ColoredText, BaseColor)\n\t\t\t\tinsert(ColoredText, sub(Instance.Text, Index, MatchIndex - 1))\n\n\t\t\t\tinsert(ColoredText, Highlight[Key])\n\t\t\t\tinsert(ColoredText, Key)\n\n\t\t\t\tIndex = MatchIndex + len(Key)\n\t\t\telse\n\t\t\t\tinsert(ColoredText, BaseColor)\n\t\t\t\tinsert(ColoredText, sub(Instance.Text, Index, EndIndex))\n\t\t\t\tIndex = EndIndex\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tif Index < len(Instance.Text) then\n\t\t\tinsert(ColoredText, BaseColor)\n\t\t\tinsert(ColoredText, sub(Instance.Text, Index))\n\t\tend\n\n\t\t--print(string.format(\"UpdateTextObject Time: %f\", (love.timer.getTime() - StartTime)))\n\tend\n\n\tif #ColoredText == 0 then\n\t\tColoredText = {BaseColor, Instance.Text}\n\tend\n\n\tInstance.TextObject:setf(ColoredText, Width, Align)\nend\n\nlocal function UpdateSlider(Instance, Precision)\n\tif Instance ~= nil then\n\t\tlocal Flag = true\n\t\tif Instance.NeedDrag then\n\t\t\tlocal DeltaX = Mouse.GetDelta()\n\t\t\tFlag = DeltaX ~= 0.0\n\t\tend\n\t\tif Flag then\n\t\t\tlocal MouseX, _ = Mouse.Position()\n\t\t\tlocal MinX = Cursor.GetPosition()\n\t\t\tlocal MaxX = MinX + Instance.W\n\t\t\tlocal Ratio = Utility.Clamp((MouseX - MinX) / (MaxX - MinX), 0.0, 1.0)\n\t\t\tlocal Min = Instance.MinNumber == nil and -huge or Instance.MinNumber\n\t\t\tlocal Max = Instance.MaxNumber == nil and huge or Instance.MaxNumber\n\t\t\tlocal Value = (Max - Min) * Ratio + Min\n\t\t\tif Precision > 0 then\n\t\t\t\tInstance.Text = string.format(\"%.\" .. Precision .. \"f\", Value)\n\t\t\telse\n\t\t\t\tInstance.Text = string.format(\"%d\", Value)\n\t\t\tend\n\t\t\tValidateNumber(Instance);\n\t\tend\n\tend\nend\n\nlocal function UpdateDrag(Instance, Step)\n\tif Instance ~= nil then\n\t\tlocal DeltaX = Mouse.GetDelta()\n\t\tif DeltaX ~= 0.0 then\n\t\t\t-- The drag threshold will be calculated dynamically. This is achieved by taking the active monitor\n\t\t\t-- width and dividing by the allowable range. The DPI scale is taken into account as well. The\n\t\t\t-- threshold is clamped at 10 to prevent large requirements for drag effect.\n\t\t\tlocal DPIScale = love.window.getDPIScale()\n\t\t\tlocal Width, Height, Flags = love.window.getMode()\n\t\t\tlocal DesktopWidth, DesktopHeight = love.window.getDesktopDimensions(Flags.display)\n\t\t\tlocal Min = Instance.MinNumber or -huge\n\t\t\tlocal Max = Instance.MaxNumber or huge\n\t\t\tlocal Diff = (Max - Min) / Step\n\t\t\tlocal DragThreshold = 1.0\n\n\t\t\tif Diff > 0 then\n\t\t\t\tDragThreshold = floor(DesktopWidth / Diff) / DPIScale\n\t\t\t\tDragThreshold = Utility.Clamp(DragThreshold, 1, 10)\n\t\t\tend\n\n\t\t\tDragDelta = DragDelta + DeltaX\n\t\t\tif abs(DragDelta) > DragThreshold then\n\t\t\t\tDragDelta = 0\n\t\t\t\tlocal Value = tonumber(Instance.Text)\n\t\t\t\tif Value ~= nil then\n\t\t\t\t\tValue = Value + Step * (DeltaX < 0 and -1 or 1)\n\t\t\t\t\tInstance.Text = tostring(Value)\n\t\t\t\t\tValidateNumber(Instance)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function DrawSlider(Instance, DrawSliderAsHandle)\n\tif Instance ~= nil and Instance.NumbersOnly then\n\t\tlocal Value = tonumber(Instance.Text)\n\t\tif Value ~= nil then\n\t\t\tlocal Min = Instance.MinNumber == nil and -huge or Instance.MinNumber\n\t\t\tlocal Max = Instance.MaxNumber == nil and huge or Instance.MaxNumber\n\t\t\tlocal Ratio = (Value - Min) / (Max - Min)\n\t\t\tlocal SliderSize = 6.0\n\t\t\tlocal MinX, MinY = Cursor.GetPosition()\n\t\t\tlocal MaxX, MaxY = MinX + Instance.W - SliderSize, MinY + Instance.H\n\t\t\tlocal X = (MaxX - MinX) * Ratio + MinX\n\t\t\tif DrawSliderAsHandle then\n\t\t\t\tDrawCommands.Rectangle('fill', X, MinY + 1.0, SliderSize, Instance.H - 2.0, Style.InputSliderColor)\n\t\t\telse\n\t\t\t\tlocal Padding = 2\n\t\t\t\tDrawCommands.Rectangle('fill', MinX+Padding, MinY+Padding, Padding + (Instance.W - Padding * 3) * Ratio, Instance.H - (Padding * 2), Style.InputSliderColor)\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function GetInstance(Id)\n\tfor _, V in ipairs(Instances) do\n\t\tif V.Id == Id then\n\t\t\treturn V\n\t\tend\n\tend\n\tlocal Instance = {}\n\tInstance.Id = Id\n\tInstance.Text = \"\"\n\tInstance.OrigText = \"\"\n\tInstance.PasswordText = \"\"\n\tInstance.TextChanged = false\n\tInstance.NumbersOnly = true\n\tInstance.ReadOnly = false\n\tInstance.Align = 'left'\n\tInstance.MinNumber = nil\n\tInstance.MaxNumber = nil\n\tInstance.Lines = nil\n\tInstance.TextObject = nil\n\tInstance.Highlight = nil\n\tInstance.ShouldUpdateTextObject = false\n\tInstance.LineNumbers = false\n\tInstance.LineNumbersStart = Instance.LineNumbers and 1 or nil\n\tInstance.LineNumbersObject = nil\n\tinsert(Instances, Instance)\n\treturn Instance\nend\n\nfunction Input.Begin(Id, Options)\n\tassert(Id ~= nil, \"Please pass a valid Id into Slab.Input.\")\n\n\tlocal StatHandle = Stats.Begin('Input', 'Slab')\n\n\tOptions = Options or {}\n\tOptions.Tooltip = Options.Tooltip or \"\"\n\tOptions.ReturnOnText = Options.ReturnOnText == nil and true or Options.ReturnOnText\n\tOptions.Text = Options.Text and tostring(Options.Text) or \"\"\n\tOptions.TextColor = Options.TextColor\n\tOptions.BgColor = Options.BgColor or Style.InputBgColor\n\tOptions.SelectColor = Options.SelectColor or Style.InputSelectColor\n\tOptions.SelectOnFocus = Options.SelectOnFocus == nil and true or Options.SelectOnFocus\n\tOptions.W = Options.W\n\tOptions.H = Options.H\n\tOptions.ReadOnly = Options.ReadOnly or false\n\tOptions.Align = Options.Align\n\tOptions.Rounding = Options.Rounding or Style.InputBgRounding\n\tOptions.MinNumber = Options.MinNumber\n\tOptions.MaxNumber = Options.MaxNumber\n\tOptions.MultiLine = Options.MultiLine or false\n\tOptions.MultiLineW = Options.MultiLineW or huge\n\tOptions.LineNumbers = Options.LineNumbers\n\tOptions.LineNumbersStart = Options.LineNumbersStart or 1\n\tOptions.Highlight = Options.Highlight\n\tOptions.Step = Options.Step or 1.0\n\tOptions.NoDrag = Options.NoDrag or false\n\tOptions.UseSlider = Options.UseSlider or false\n\tOptions.Precision = Options.Precision and math.floor(Utility.Clamp(Options.Precision, 0, 5)) or 3\n\tOptions.NeedDrag = Options.NeedDrag == nil and true or Options.NeedDrag\n\tOptions.IsPassword = not not Options.IsPassword --default is false\n\tOptions.PasswordChar = Options.IsPassword and Options.PasswordChar or DEF_PW_CHAR\n\n\tif type(Options.MinNumber) ~= \"number\" then\n\t\tOptions.MinNumber = nil\n\tend\n\n\tif type(Options.MaxNumber) ~= \"number\" then\n\t\tOptions.MaxNumber = nil\n\tend\n\n\tif Options.MultiLine then\n\t\tOptions.TextColor = Style.MultilineTextColor\n\tend\n\n\tlocal Instance = GetInstance(Window.GetId() .. \".\" .. Id)\n\tInstance.NumbersOnly = Options.NumbersOnly\n\tInstance.ReadOnly = Options.ReadOnly\n\tInstance.Align = Options.Align\n\tInstance.MinNumber = Options.MinNumber\n\tInstance.MaxNumber = Options.MaxNumber\n\tInstance.MultiLine = Options.MultiLine\n\tInstance.LineNumbers = Options.LineNumbers\n\tInstance.LineNumbersStart = Options.LineNumbersStart\n\tInstance.NeedDrag = Options.NeedDrag\n\tInstance.IsPassword = Options.IsPassword\n\tInstance.PasswordChar = Options.PasswordChar\n\n\tif Instance.MultiLineW ~= Options.MultiLineW then\n\t\tInstance.Lines = nil\n\tend\n\n\tInstance.MultiLineW = Options.MultiLineW\n\tlocal WinItemId = Window.GetItemId(Id)\n\n\tif Instance.Align == nil then\n\t\tInstance.Align = (Instance == Focused and not IsSliding) and 'left' or 'center'\n\n\t\tif Instance.ReadOnly then\n\t\t\tInstance.Align = 'center'\n\t\tend\n\n\t\tif Options.MultiLine then\n\t\t\tInstance.Align = 'left'\n\t\tend\n\tend\n\n\tif Focused ~= Instance then\n\t\tif Options.MultiLine and #Options.Text ~= #Instance.Text then\n\t\t\tInstance.Lines = nil\n\t\tend\n\n\t\tInstance.Text = Options.Text == nil and Instance.Text or Options.Text\n\n\t\tif Options.IsPassword then\n\t\t\tInstance.Text = Instance.PasswordText == nil and Instance.Text or Instance.PasswordText\n\t\tend\n\tend\n\n\tif Instance.MinNumber ~= nil and Instance.MaxNumber ~= nil then\n\t\tassert(Instance.MinNumber <= Instance.MaxNumber,\n\t\t\t\"Invalid MinNumber and MaxNumber passed to Input control '\" .. Instance.Id .. \"'. MinNumber: \" .. Instance.MinNumber .. \" MaxNumber: \" .. Instance.MaxNumber)\n\tend\n\n\tlocal H = Options.H == nil and Text.GetHeight() or Options.H\n\tlocal W = Options.W == nil and MIN_WIDTH or Options.W\n\tlocal ContentW, ContentH = 0.0, 0.0\n\tlocal Result = false\n\n\tW, H = LayoutManager.ComputeSize(W, H)\n\tLayoutManager.AddControl(W, H, 'Input')\n\n\tInstance.W = W\n\tInstance.H = H\n\n\tlocal X, Y = Cursor.GetPosition()\n\n\tif Options.MultiLine then\n\t\tOptions.SelectOnFocus = false\n\t\tlocal WasSanitized = false\n\t\tOptions.Text, WasSanitized = SanitizeText(Options.Text)\n\t\tif WasSanitized then\n\t\t\tResult = true\n\t\t\tLastText = Options.Text\n\t\tend\n\n\t\tContentW, ContentH = Text.GetSizeWrap(Instance.Text, Options.MultiLineW)\n\tend\n\n\tlocal ShouldUpdateTextObject = Instance.ShouldUpdateTextObject\n\tInstance.ShouldUpdateTextObject = false\n\n\tif Options.MultiLine and (Instance.Lines == nil) and (Instance.Text ~= \"\") then\n\t\tif Instance.TextObject == nil then\n\t\t\tInstance.TextObject = love.graphics.newText(Style.Font)\n\t\tend\n\t\tInstance.Lines = Text.GetLines(Instance.Text, Options.MultiLineW)\n\t\tContentH = #Instance.Lines * Text.GetHeight()\n\t\tShouldUpdateTextObject = true\n\tend\n\n\tif Instance.MultiLine and Instance.LineNumbers then\n\t\tif Instance.LineNumbersObject == nil then\n\t\t\tInstance.LineNumbersObject = love.graphics.newText(Style.Font)\n\t\t\tUpdateLineNumbers(Instance, LINE_NUMBER_W, H, nil, Options.TextColor)\n\t\tend\n\tend\n\n\tif Options.Highlight ~= nil then\n\t\tif Instance.Highlight == nil or Utility.TableCount(Options.Highlight) ~= Utility.TableCount(Instance.Highlight) then\n\t\t\tInstance.Highlight = Utility.Copy(Options.Highlight)\n\t\t\tShouldUpdateTextObject = true\n\t\telse\n\t\t\tfor K, V in pairs(Options.Highlight) do\n\t\t\t\tlocal HighlightColor = Instance.Highlight[K]\n\t\t\t\tif HighlightColor ~= nil then\n\t\t\t\t\tif V[1] ~= HighlightColor[1] or V[2] ~= HighlightColor[2] or V[3] ~= HighlightColor[3] or V[4] ~= HighlightColor[4] then\n\t\t\t\t\t\tShouldUpdateTextObject = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tInstance.Highlight = Utility.Copy(Options.Highlight)\n\t\t\t\t\tShouldUpdateTextObject = true\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\telse\n\t\tif Instance.Highlight ~= nil then\n\t\t\tInstance.Highlight = nil\n\t\t\tShouldUpdateTextObject = true\n\t\tend\n\tend\n\n\tif ShouldUpdateTextObject then\n\t\tUpdateLineNumbers(Instance, LINE_NUMBER_W, H, nil, Options.TextColor)\n\t\tUpdateTextObject(\n\t\t\tInstance,\n\t\t\tOptions.MultiLineW,\n\t\t\tInstance.Align,\n\t\t\tOptions.Highlight,\n\t\t\tOptions.TextColor\n\t\t)\n\tend\n\n\tlocal IsObstructed = Window.IsObstructedAtMouse()\n\tlocal MouseX, MouseY = Window.GetMousePosition()\n\tlocal Hovered = not IsObstructed and X <= MouseX and MouseX <= X + W and Y <= MouseY and MouseY <= Y + H\n\tlocal HoveredScrollBar = Region.IsHoverScrollBar(Instance.Id) or Region.IsScrolling()\n\n\tif Hovered and not HoveredScrollBar then\n\t\tMouse.SetCursor('ibeam')\n\t\tTooltip.Begin(Options.Tooltip)\n\t\tWindow.SetHotItem(WinItemId)\n\tend\n\n\tlocal CheckFocus = Mouse.IsClicked(1) and not HoveredScrollBar\n\tlocal NumbersOnlyEntry = Mouse.IsDoubleClicked(1) and Instance.NumbersOnly\n\n\tlocal FocusedThisFrame = false\n\tlocal ClearFocus = false\n\tif CheckFocus then\n\t\tif Hovered then\n\t\t\tFocusedThisFrame = Focused ~= Instance\n\t\t\tFocused = Instance\n\t\telseif Instance == Focused then\n\t\t\tClearFocus = true\n\t\t\tFocused = nil\n\t\tend\n\tend\n\n\tif FocusToNext and LastFocused == nil then\n\t\tFocusedThisFrame = true\n\t\tFocused = Instance\n\t\tCheckFocus = true\n\t\tFocusToNext = false\n\t\tTextCursorAnchor = -1\n\t\tTextCursorPos = 0\n\t\tTextCursorPosLine = 0\n\t\tTextCursorPosLineNumber = 1\n\tend\n\n\tif LastFocused == Instance then\n\t\tLastFocused = nil\n\tend\n\n\tlocal IsEditing = Instance == Focused and not IsSliding\n\n\tif Instance == Focused then\n\t\tlocal Back = false\n\t\t-- local IgnoreBack = false\n\t\tlocal ShouldDelete = false\n\t\tlocal ShouldUpdateTransform = false\n\t\t-- local PreviousTextCursorPos = TextCursorPos\n\n\t\tif IsCommandKeyDown() then\n\t\t\tif Keyboard.IsPressed('x') or Keyboard.IsPressed('c') then\n\t\t\t\tlocal Selected = GetSelection(Instance)\n\t\t\t\tif Selected ~= \"\" then\n\t\t\t\t\tlove.system.setClipboardText(Selected)\n\t\t\t\t\tShouldDelete = Keyboard.IsPressed('x')\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif Keyboard.IsPressed('v') then\n\t\t\t\tlocal Text = FileSystem.GetClipboard()\n\t\t\t\tInput.Text(Text)\n\t\t\t\tTextCursorPos = min(TextCursorPos + #Text - 1, #Instance.Text)\n\t\t\tend\n\t\tend\n\n\t\tif Keyboard.IsPressed('tab') then\n\t\t\tif Options.MultiLine then\n\t\t\t\tInput.Text('\\t')\n\t\t\telse\n\t\t\t\tLastFocused = Instance\n\t\t\t\tFocusToNext = true\n\t\t\tend\n\t\tend\n\n\t\tif Keyboard.IsPressed('backspace') then\n\t\t\tShouldDelete = true\n\t\t\t-- IgnoreBack = TextCursorAnchor ~= -1\n\t\tend\n\n\t\tif Keyboard.IsPressed('delete') then\n\t\t\tif TextCursorAnchor == -1 then\n\t\t\t\tlocal Ch = GetCharacter(Instance.Text, TextCursorPos, true)\n\t\t\t\tif Ch ~= nil then\n\t\t\t\t\tTextCursorPos = TextCursorPos + len(Ch)\n\t\t\t\t\tShouldDelete = true\n\t\t\t\tend\n\t\t\telse\n\t\t\t\t-- IgnoreBack = true\n\t\t\t\tShouldDelete = true\n\t\t\tend\n\t\tend\n\n\t\tif ShouldDelete then\n\t\t\tif DeleteSelection(Instance) then\n\t\t\t\tInstance.TextChanged = true\n\t\t\tend\n\t\tend\n\n\t\tlocal ClearAnchor = false\n\t\tlocal IsShiftDown = Keyboard.IsDown('lshift') or Keyboard.IsDown('rshift')\n\n\t\tif Keyboard.IsPressed('lshift') or Keyboard.IsPressed('rshift') then\n\t\t\tif TextCursorAnchor == -1 then\n\t\t\t\tTextCursorAnchor = TextCursorPos\n\t\t\tend\n\t\tend\n\n\t\tlocal HomePressed, EndPressed = false, false\n\n\t\tif IsHomePressed() then\n\t\t\tMoveToHome(Instance)\n\t\t\tShouldUpdateTransform = true\n\t\t\tHomePressed = true\n\t\tend\n\n\t\tif IsEndPressed() then\n\t\t\tMoveToEnd(Instance)\n\t\t\tShouldUpdateTransform = true\n\t\t\tEndPressed = true\n\t\tend\n\n\t\tif not HomePressed and (Keyboard.IsPressed('left') or Back) then\n\t\t\tTextCursorPos = GetNextCursorPos(Instance, true)\n\t\t\tShouldUpdateTransform = true\n\t\t\tUpdateMultiLinePosition(Instance)\n\t\tend\n\t\tif not EndPressed and Keyboard.IsPressed('right') then\n\t\t\tTextCursorPos = GetNextCursorPos(Instance, false)\n\t\t\tShouldUpdateTransform = true\n\t\t\tUpdateMultiLinePosition(Instance)\n\t\tend\n\n\t\tif Keyboard.IsPressed('up') then\n\t\t\tMoveCursorVertical(Instance, false)\n\t\t\tShouldUpdateTransform = true\n\t\tend\n\t\tif Keyboard.IsPressed('down') then\n\t\t\tMoveCursorVertical(Instance, true)\n\t\t\tShouldUpdateTransform = true\n\t\tend\n\n\t\tif Keyboard.IsPressed('pageup') then\n\t\t\tMoveCursorPage(Instance, false)\n\t\t\tShouldUpdateTransform = true\n\t\tend\n\t\tif Keyboard.IsPressed('pagedown') then\n\t\t\tMoveCursorPage(Instance, true)\n\t\t\tShouldUpdateTransform = true\n\t\tend\n\n\t\tif CheckFocus or DragSelect then\n\t\t\tif FocusedThisFrame then\n\t\t\t\tif Options.NumbersOnly and not NumbersOnlyEntry and not Options.NoDrag then\n\t\t\t\t\tIsSliding = true\n\t\t\t\t\tDragDelta = 0\n\t\t\t\telseif Options.SelectOnFocus and Instance.Text ~= \"\" then\n\t\t\t\t\tTextCursorAnchor = 0\n\t\t\t\t\tTextCursorPos = #Instance.Text\n\t\t\t\tend\n\n\t\t\t\t-- Display the soft keyboard on mobile devices when an input control receives focus.\n\t\t\t\tif Utility.IsMobile() and not Options.ReadOnly then\n\t\t\t\t\t-- Always display for non numeric controls. If this control is a numeric input, check to make\n\t\t\t\t\t-- sure the user requested to add text for this numeric control.\n\t\t\t\t\tif not Options.NumbersOnly or NumbersOnlyEntry or Options.NoDrag then\n\t\t\t\t\t\tlove.keyboard.setTextInput(true)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\t-- Enable key repeat when an input control is focused.\n\t\t\t\tlove.keyboard.setKeyRepeat(true)\n\t\t\telse\n\t\t\t\tlocal MouseInputX, MouseInputY = MouseX - X, MouseY - Y\n\t\t\t\tlocal CX, CY = Region.InverseTransform(Instance.Id, MouseInputX, MouseInputY)\n\t\t\t\tTextCursorPos = GetTextCursorPos(Instance, CX, CY)\n\t\t\t\tif Mouse.IsClicked(1) then\n\t\t\t\t\tTextCursorAnchor = TextCursorPos\n\t\t\t\t\tDragSelect = true\n\t\t\t\tend\n\t\t\t\tShouldUpdateTransform = true\n\t\t\t\tIsShiftDown = true\n\t\t\tend\n\t\t\tUpdateMultiLinePosition(Instance)\n\t\tend\n\n\t\tif IsSliding then\n\t\t\tlocal Current = tonumber(Instance.Text)\n\n\t\t\tif Options.UseSlider then\n\t\t\t\tUpdateSlider(Instance, Options.Precision)\n\t\t\telse\n\t\t\t\tUpdateDrag(Instance, Options.Step)\n\t\t\tend\n\n\t\t\tInstance.TextChanged = Current ~= tonumber(Instance.Text)\n\t\tend\n\n\t\tif Mouse.IsReleased(1) then\n\t\t\tDragSelect = false\n\t\t\tif TextCursorAnchor == TextCursorPos then\n\t\t\t\tTextCursorAnchor = -1\n\t\t\tend\n\n\t\t\tif IsSliding then\n\t\t\t\tIsSliding = false\n\t\t\t\tFocused = nil\n\t\t\t\tResult = true\n\t\t\t\tLastText = Instance.Text\n\t\t\tend\n\t\tend\n\n\t\tif Mouse.IsDoubleClicked(1) then\n\t\t\tlocal MouseInputX, MouseInputY = MouseX - X, MouseY - Y\n\t\t\tlocal CX, CY = Region.InverseTransform(Instance.Id, MouseInputX, MouseInputY)\n\t\t\tTextCursorPos = GetTextCursorPos(Instance, CX, CY)\n\t\t\tSelectWord(Instance)\n\t\t\tDragSelect = false\n\t\tend\n\n\t\tif Keyboard.IsPressed(\"return\") or Keyboard.IsPressed(\"kpenter\") then\n\t\t\tResult = true\n\t\t\tif Options.MultiLine then\n\t\t\t\tInput.Text('\\n')\n\t\t\telse\n\t\t\t\tClearFocus = true\n\t\t\tend\n\t\tend\n\n\t\tif Instance.TextChanged or Back then\n\t\t\tif Options.ReturnOnText then\n\t\t\t\tResult = true\n\t\t\tend\n\n\t\t\tif Options.MultiLine then\n\t\t\t\tInstance.Lines = Text.GetLines(Instance.Text, Options.MultiLineW)\n\t\t\t\tUpdateLineNumbers(\n\t\t\t\t\tInstance,\n\t\t\t\t\tLINE_NUMBER_W,\n\t\t\t\t\tH,\n\t\t\t\t\t#Instance.Lines,\n\t\t\t\t\tOptions.TextColor\n\t\t\t\t)\n\t\t\t\tUpdateTextObject(\n\t\t\t\t\tInstance,\n\t\t\t\t\tOptions.MultiLineW,\n\t\t\t\t\tInstance.Align,\n\t\t\t\t\tOptions.Highlight,\n\t\t\t\t\tOptions.TextColor\n\t\t\t\t)\n\t\t\tend\n\n\t\t\tUpdateMultiLinePosition(Instance)\n\n\t\t\tInstance.TextChanged = false\n\t\t\t-- PreviousTextCursorPos = -1\n\t\tend\n\n\t\tif ShouldUpdateTransform then\n\t\t\tClearAnchor = not IsShiftDown\n\t\t\tUpdateTransform(Instance)\n\t\tend\n\n\t\tif ClearAnchor then\n\t\t\tTextCursorAnchor = -1\n\t\tend\n\telse\n\t\tlocal WasValidated = ValidateNumber(Instance)\n\t\tif WasValidated then\n\t\t\tResult = true\n\t\t\tLastText = Instance.Text\n\t\tend\n\tend\n\n\tif Region.IsScrolling(Instance.Id) then\n\t\tlocal DeltaX, DeltaY = Mouse.GetDelta()\n\t\tlocal WheelX, WheelY = Region.GetWheelDelta()\n\n\t\tif DeltaY ~= 0.0 or WheelY ~= 0.0 then\n\t\t\tInstance.ShouldUpdateTextObject = true\n\t\tend\n\tend\n\n\tif (Instance == Focused and not Instance.ReadOnly) or Options.MultiLine then\n\t\tOptions.BgColor = Style.InputEditBgColor\n\tend\n\n\tlocal TX, TY = Window.TransformPoint(X, Y)\n\tif Instance.LineNumbers and (Instance.LineNumbersObject ~= nil) then\n\t\tRegion.Begin(Instance.Id .. \"LineNumbers\", {\n\t\t\tX = X,\n\t\t\tY = Y,\n\t\t\tW = LINE_NUMBER_W,\n\t\t\tH = H,\n\t\t\tContentW = ContentW + Pad,\n\t\t\tContentH = ContentH + Pad,\n\t\t\tBgColor = Options.BgColor,\n\t\t\tSX = TX,\n\t\t\tSY = TY,\n\t\t\tMouseX = MouseX,\n\t\t\tMouseY = MouseY,\n\t\t\tIntersect = true,\n\t\t\tIgnoreScroll = true,\n\t\t\tRounding = Options.Rounding,\n\t\t\tIsObstructed = IsObstructed,\n\t\t\tAutoSizeContent = false\n\t\t})\n\t\t\tLayoutManager.Begin(\"IgnoreLineNumbers\", {Ignore = true})\n\t\t\tText.BeginObject(Instance.LineNumbersObject)\n\t\t\tLayoutManager.End()\n\t\tRegion.End()\n\t\tRegion.ApplyScissor()\n\t\tX = X + LINE_NUMBER_W\n\tend\n\n\tTX, TY = Window.TransformPoint(X, Y)\n\tRegion.Begin(Instance.Id, {\n\t\tX = X,\n\t\tY = Y,\n\t\tW = W,\n\t\tH = H,\n\t\tContentW = ContentW + Pad,\n\t\tContentH = ContentH + Pad,\n\t\tBgColor = Options.BgColor,\n\t\tSX = TX,\n\t\tSY = TY,\n\t\tMouseX = MouseX,\n\t\tMouseY = MouseY,\n\t\tIntersect = true,\n\t\tIgnoreScroll = not Options.MultiLine,\n\t\tRounding = Options.Rounding,\n\t\tIsObstructed = IsObstructed,\n\t\tAutoSizeContent = false\n\t})\n\tif Instance == Focused then\n\t\tif not IsSliding then\n\t\t\tDrawSelection(Instance, X, Y, W, H, Options.SelectColor)\n\t\t\tDrawCursor(Instance, X, Y, W, H)\n\t\tend\n\tend\n\n\tif Options.UseSlider then\n\t\tif not IsEditing then\n\t\t\tDrawSlider(Instance, Options.DrawSliderAsHandle)\n\t\tend\n\tend\n\n\tif Instance.Text ~= \"\" then\n\t\tCursor.SetPosition(X + GetAlignmentOffset(Instance), Y)\n\n\t\tLayoutManager.Begin(\"Ignore\", {Ignore = true})\n\n\t\tif Instance.TextObject ~= nil then\n\t\t\tText.BeginObject(Instance.TextObject)\n\t\telse\n\t\t\tText.Begin(Instance.Text, {AddItem = false, Color = Options.TextColor})\n\t\tend\n\n\t\tLayoutManager.End()\n\tend\n\tRegion.End()\n\tRegion.ApplyScissor()\n\n\tCursor.SetItemBounds(X, Y, W, H)\n\tCursor.SetPosition(X, Y)\n\tCursor.AdvanceX(W)\n\tCursor.AdvanceY(H)\n\n\tWindow.AddItem(X, Y, W, H, WinItemId)\n\n\tif ClearFocus then\n\t\tValidateNumber(Instance)\n\t\tLastText = Instance.Text\n\t\tFocused = nil\n\n\t\tif not Options.MultiLine then\n\t\t\tRegion.ResetTransform(Instance.Id)\n\t\tend\n\n\t\t-- Close the soft keyboard on mobile platforms when an input control loses focus.\n\t\tif Utility.IsMobile() then\n\t\t\tlove.keyboard.setTextInput(false)\n\t\tend\n\n\t\t-- Restore the key repeat flag to the state before an input control gained focus.\n\t\tlove.keyboard.setKeyRepeat(false)\n\tend\n\n\tStats.End(StatHandle)\n\n\treturn Result\nend\n\nfunction Input.Text(Ch)\n\tif Focused ~= nil and not Focused.ReadOnly then\n\t\tif not IsValidDigit(Focused, Ch) then\n\t\t\treturn\n\t\tend\n\n\t\tif TextCursorAnchor ~= -1 then\n\t\t\tDeleteSelection(Focused)\n\t\tend\n\n\t\tif TextCursorPos == 0 then\n\t\t\tFocused.Text = Ch .. Focused.Text\n\t\t\tFocused.OrigText = Ch .. Focused.OrigText\n\t\telse\n\t\t\tlocal Left = sub(Focused.Text, 0, TextCursorPos)\n\t\t\tlocal Right = sub(Focused.Text, TextCursorPos + 1)\n\t\t\tFocused.Text = Left .. Ch .. Right\n\n\t\t\tif Focused.IsPassword then\n\t\t\t\tlocal Left = sub(Focused.OrigText, 0, TextCursorPos)\n\t\t\t\tlocal Right = sub(Focused.OrigText, TextCursorPos + 1)\n\t\t\t\tFocused.OrigText = Left .. Ch .. Right\n\t\t\tend\n\t\tend\n\n\t\tif Focused.IsPassword then\n\t\t\tFocused.PasswordText = string.rep(Focused.PasswordChar, #Focused.OrigText)\n\t\t\tFocused.Text = Focused.PasswordText\n\t\tend\n\n\t\tTextCursorPos = min(TextCursorPos + len(Ch), len(Focused.Text))\n\t\tTextCursorAnchor = -1\n\t\tUpdateTransform(Focused)\n\t\tFocused.TextChanged = true\n\tend\nend\n\nfunction Input.Update(dt)\n\tlocal Delta = dt * 2.0\n\tif FadeIn then\n\t\tTextCursorAlpha = min(TextCursorAlpha + Delta, 1.0)\n\t\tFadeIn = TextCursorAlpha < 1.0\n\telse\n\t\tTextCursorAlpha = max(TextCursorAlpha - Delta, 0.0)\n\t\tFadeIn = TextCursorAlpha == 0.0\n\tend\n\n\tif PendingFocus ~= nil then\n\t\tLastFocused = Focused\n\t\tFocused = PendingFocus\n\t\tPendingFocus = nil\n\tend\n\n\tif Focused ~= nil then\n\t\tif PendingCursorPos >= 0 then\n\t\t\tTextCursorPos = min(PendingCursorPos, #Focused.Text)\n\t\t\tValidateTextCursorPos(Focused)\n\t\t\tUpdateMultiLinePosition(Focused)\n\t\t\tPendingCursorPos = -1\n\t\tend\n\n\t\tlocal MultiLineChanged = false\n\n\t\tif PendingCursorColumn >= 0 then\n\t\t\tif Focused.Lines ~= nil then\n\t\t\t\tTextCursorPosLine = PendingCursorColumn\n\t\t\t\tMultiLineChanged = true\n\t\t\tend\n\n\t\t\tPendingCursorColumn = -1\n\t\tend\n\n\t\tif PendingCursorLine > 0 then\n\t\t\tif Focused.Lines ~= nil then\n\t\t\t\tTextCursorPosLineNumber = min(PendingCursorLine, #Focused.Lines)\n\t\t\t\tMultiLineChanged = true\n\t\t\tend\n\n\t\t\tPendingCursorLine = 0\n\t\tend\n\n\t\tif MultiLineChanged then\n\t\t\tlocal Line = Focused.Lines[TextCursorPosLineNumber]\n\t\t\tTextCursorPosLine = min(TextCursorPosLine, len(Line))\n\t\t\tlocal Start = 0\n\t\t\tfor I, V in ipairs(Focused.Lines) do\n\t\t\t\tif I == TextCursorPosLineNumber then\n\t\t\t\t\tTextCursorPos = Start + TextCursorPosLine\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\tStart = Start + len(V)\n\t\t\tend\n\t\t\tValidateTextCursorPos(Focused)\n\t\tend\n\telse\n\t\tPendingCursorPos = -1\n\t\tPendingCursorColumn = -1\n\t\tPendingCursorLine = 0\n\tend\nend\n\nfunction Input.GetText()\n\tif Focused ~= nil then\n\t\tif Focused.NumbersOnly and (Focused.Text == \"\" or Focused.Text == \".\") then\n\t\t\treturn \"0\"\n\t\tend\n\t\treturn Focused.IsPassword and Focused.OrigText or Focused.Text\n\tend\n\treturn LastText\nend\n\nfunction Input.GetCursorPos()\n\tif Focused ~= nil then\n\t\treturn TextCursorPos, TextCursorPosLine, TextCursorPosLineNumber\n\tend\n\n\treturn 0, 0, 0\nend\n\nfunction Input.IsAnyFocused()\n\treturn Focused ~= nil\nend\n\nfunction Input.IsFocused(Id)\n\tlocal Instance = GetInstance(Window.GetId() .. '.' .. Id)\n\treturn Instance == Focused\nend\n\nfunction Input.SetFocused(Id)\n\tif Id == nil then\n\t\tFocused = nil\n\t\tPendingFocus = nil\n\t\treturn\n\tend\n\n\tlocal Instance = GetInstance(Window.GetId() .. '.' .. Id)\n\tPendingFocus = Instance\nend\n\nfunction Input.SetCursorPos(Pos)\n\tPendingCursorPos = max(Pos, 0)\nend\n\nfunction Input.SetCursorPosLine(Column, Line)\n\tif Column ~= nil then\n\t\tPendingCursorColumn = max(Column, 0)\n\tend\n\n\tif Line ~= nil then\n\t\tPendingCursorLine = max(Line, 1)\n\tend\nend\n\nfunction Input.GetDebugInfo()\n\tlocal Info = {}\n\tlocal X, Y = GetCursorPos(Focused)\n\n\tif Focused ~= nil then\n\t\tRegion.InverseTransform(Focused.Id, X, Y)\n\tend\n\n\tInfo['Focused'] = Focused ~= nil and Focused.Id or 'nil'\n\tInfo['Width'] = Focused ~= nil and Focused.W or 0\n\tInfo['Height'] = Focused ~= nil and Focused.H or 0\n\tInfo['CursorX'] = X\n\tInfo['CursorY'] = Y\n\tInfo['CursorPos'] = TextCursorPos\n\tInfo['Character'] = Focused ~= nil and GetDisplayCharacter(Focused.Text, TextCursorPos) or ''\n\tInfo['LineCursorPos'] = TextCursorPosLine\n\tInfo['LineCursorPosMax'] = TextCursorPosLineMax\n\tInfo['LineNumber'] = TextCursorPosLineNumber\n\tInfo['LineLength'] = (Focused ~= nil and Focused.Lines ~= nil) and len(Focused.Lines[TextCursorPosLineNumber]) or 0\n\tInfo['Lines'] = Focused ~= nil and Focused.Lines or nil\n\n\treturn Info\nend\n\nreturn Input\n"
  },
  {
    "path": "Internal/UI/LayoutManager.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal insert = table.insert\nlocal remove = table.remove\nlocal max = math.max\nlocal min = math.min\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\n\nlocal LayoutManager = {}\n\nlocal Instances = {}\nlocal Stack = {}\nlocal Active = nil\n\nlocal function GetWindowBounds()\n\tlocal WinX, WinY, WinW, WinH = Window.GetBounds(true)\n\tlocal Border = Window.GetBorder()\n\n\tWinX = WinX + Border\n\tWinY = WinY + Border\n\tWinW = WinW - Border * 2\n\tWinH = WinH - Border * 2\n\n\treturn WinX, WinY, WinW, WinH\nend\n\nlocal function GetRowSize(Instance)\n\tif Instance ~= nil then\n\t\tlocal Column = Instance.Columns[Instance.ColumnNo]\n\n\t\tif Column.Rows ~= nil then\n\t\t\tlocal Row = Column.Rows[Column.RowNo]\n\n\t\t\tif Row ~= nil then\n\t\t\t\treturn Row.W, Row.H\n\t\t\tend\n\t\tend\n\tend\n\n\treturn 0, 0\nend\n\nlocal function GetRowCursorPos(Instance)\n\tif Instance ~= nil then\n\t\tlocal Column = Instance.Columns[Instance.ColumnNo]\n\n\t\tif Column.Rows ~= nil then\n\t\t\tlocal Row = Column.Rows[Column.RowNo]\n\n\t\t\tif Row ~= nil then\n\t\t\t\treturn Row.CursorX, Row.CursorY\n\t\t\tend\n\t\tend\n\tend\n\n\treturn nil, nil\nend\n\nlocal function GetLayoutH(Instance, IncludePad)\n\tIncludePad = IncludePad == nil and true or IncludePad\n\n\tif Instance ~= nil then\n\t\tlocal Column = Instance.Columns[Instance.ColumnNo]\n\n\t\tif Column.Rows ~= nil then\n\t\t\tlocal H = 0\n\n\t\t\tfor I, V in ipairs(Column.Rows) do\n\t\t\t\tH = H + V.H\n\n\t\t\t\tif IncludePad then\n\t\t\t\t\tH = H + Cursor.PadY()\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn H\n\t\tend\n\tend\n\n\treturn 0\nend\n\nlocal function GetPreviousRowBottom(Instance)\n\tif Instance ~= nil then\n\t\tlocal Column = Instance.Columns[Instance.ColumnNo]\n\n\t\tif Column.Rows ~= nil and Column.RowNo > 1 and Column.RowNo <= #Column.Rows then\n\t\t\tlocal Y = Column.Rows[Column.RowNo - 1].CursorY\n\t\t\tlocal H = Column.Rows[Column.RowNo - 1].H\n\t\t\treturn Y + H\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal function GetColumnPosition(Instance)\n\tif Instance ~= nil then\n\t\tlocal WinX, WinY, WinW, WinH = GetWindowBounds()\n\t\tlocal WinL, WinT = Window.GetPosition()\n\t\tlocal Count = #Instance.Columns\n\t\tlocal ColumnW = WinW / Count\n\t\tlocal TotalW = 0\n\n\t\tfor I = 1, Instance.ColumnNo - 1, 1 do\n\t\t\tlocal Column = Instance.Columns[I]\n\t\t\tTotalW = TotalW + Column.W + Cursor.PadX() * 2\n\t\tend\n\n\t\tlocal AnchorX, AnchorY = Instance.X, Instance.Y\n\n\t\tif not Instance.AnchorX then\n\t\t\tAnchorX = WinX - WinL - Window.GetBorder()\n\t\tend\n\n\t\tif not Instance.AnchorY then\n\t\t\tAnchorY = WinY - WinT - Window.GetBorder()\n\t\tend\n\n\t\treturn AnchorX + TotalW, AnchorY\n\tend\n\n\treturn 0, 0\nend\n\nlocal function GetColumnSize(Instance)\n\tif Instance ~= nil then\n\t\tlocal Column = Instance.Columns[Instance.ColumnNo]\n\t\tlocal WinX, WinY, WinW, WinH = GetWindowBounds()\n\t\tlocal Count = #Instance.Columns\n\t\tlocal ColumnW = WinW / Count\n\t\tlocal W, H = ColumnW, GetLayoutH(Instance)\n\n\t\tif not Window.IsAutoSize() then\n\t\t\tH = WinH\n\t\t\tColumn.W = W\n\t\tend\n\n\t\treturn W, H\n\tend\n\n\treturn 0, 0\nend\n\nlocal function AddControl(Instance, W, H, Type)\n\tif Instance ~= nil then\n\t\tlocal RowW, RowH = GetRowSize(Instance)\n\t\tlocal WinX, WinY, WinW, WinH = GetWindowBounds()\n\t\tlocal CursorX, CursorY = Cursor.GetPosition()\n\t\tlocal X, Y = GetRowCursorPos(Instance)\n\t\tlocal LayoutH = GetLayoutH(Instance)\n\t\tlocal PrevRowBottom = GetPreviousRowBottom(Instance)\n\t\tlocal AnchorX, AnchorY = GetColumnPosition(Instance)\n\t\tWinW, WinH = GetColumnSize(Instance)\n\t\tlocal Column = Instance.Columns[Instance.ColumnNo]\n\t\tlocal Border = Window.GetBorder()\n\n\t\tif RowW == 0 then\n\t\t\tRowW = W\n\t\tend\n\n\t\tif RowH == 0 then\n\t\t\tRowH = H\n\t\tend\n\n\t\tif X == nil then\n\t\t\tif Instance.AlignX == 'center' then\n\t\t\t\tX = max(WinW * 0.5 - RowW * 0.5 + AnchorX, AnchorX)\n\t\t\telseif Instance.AlignX == 'right' then\n\t\t\t\tlocal Right = WinW - RowW\n\t\t\t\tif not Window.IsAutoSize() then\n\t\t\t\t\tRight = Right + Window.GetBorder()\n\t\t\t\tend\n\n\t\t\t\tX = max(Right, AnchorX) - Border * 2\n\t\t\telse\n\t\t\t\tX = AnchorX\n\t\t\tend\n\t\tend\n\n\t\tif Y == nil then\n\t\t\tif PrevRowBottom ~= nil then\n\t\t\t\tY = PrevRowBottom + Cursor.PadY()\n\t\t\telse\n\t\t\t\tlocal RegionH = WinY + WinH - CursorY\n\t\t\t\tif Instance.AlignY == 'center' then\n\t\t\t\t\tY = max(RegionH * 0.5 - LayoutH * 0.5 + AnchorY, AnchorY)\n\t\t\t\telseif Instance.AlignY == 'bottom' then\n\t\t\t\t\tY = max(WinH - LayoutH, AnchorY)\n\t\t\t\telse\n\t\t\t\t\tY = AnchorY\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tCursor.SetX(WinX + X)\n\t\tCursor.SetY(WinY + Y)\n\n\t\tif H < RowH then\n\t\t\tif Instance.AlignRowY == 'center' then\n\t\t\t\tCursor.SetY(WinY + Y + RowH * 0.5 - H * 0.5)\n\t\t\telseif Instance.AlignRowY == 'bottom' then\n\t\t\t\tCursor.SetY(WinY + Y + RowH - H)\n\t\t\tend\n\t\tend\n\n\t\tlocal RowNo = Column.RowNo\n\n\t\tif Column.Rows ~= nil then\n\t\t\tlocal Row = Column.Rows[RowNo]\n\n\t\t\tif Row ~= nil then\n\t\t\t\tRow.CursorX = X + W + Cursor.PadX()\n\t\t\t\tRow.CursorY = Y\n\t\t\tend\n\t\tend\n\n\t\tif Column.PendingRows[RowNo] == nil then\n\t\t\tlocal Row = {\n\t\t\t\tCursorX = nil,\n\t\t\t\tCursorY = nil,\n\t\t\t\tW = 0,\n\t\t\t\tH = 0,\n\t\t\t\tRequestH = 0,\n\t\t\t\tMaxH = 0,\n\t\t\t\tControls = {}\n\t\t\t}\n\t\t\tinsert(Column.PendingRows, Row)\n\t\tend\n\n\t\tlocal Row = Column.PendingRows[RowNo]\n\n\t\tinsert(Row.Controls, {\n\t\t\tX = Cursor.GetX(),\n\t\t\tY = Cursor.GetY(),\n\t\t\tW = W,\n\t\t\tH = H,\n\t\t\tAlteredSize = Column.AlteredSize,\n\t\t\tType = Type\n\t\t})\n\t\tRow.W = Row.W + W\n\t\tRow.H = max(Row.H, H)\n\n\t\tColumn.RowNo = RowNo + 1\n\t\tColumn.AlteredSize = false\n\t\tColumn.W = max(Row.W, Column.W)\n\tend\nend\n\nlocal function GetInstance(Id)\n\tlocal Key = Window.GetId() .. '.' .. Id\n\n\tif Instances[Key] == nil then\n\t\tlocal Instance = {}\n\t\tInstance.Id = Id\n\t\tInstance.WindowId = Window.GetId()\n\t\tInstance.AlignX = 'left'\n\t\tInstance.AlignY = 'top'\n\t\tInstance.AlignRowY = 'top'\n\t\tInstance.Ignore = false\n\t\tInstance.ExpandW = false\n\t\tInstance.ExpandH = false\n\t\tInstance.X = 0\n\t\tInstance.Y = 0\n\t\tInstance.Columns = {}\n\t\tInstance.ColumnNo = 1\n\t\tInstances[Key] = Instance\n\tend\n\n\treturn Instances[Key]\nend\n\nfunction LayoutManager.AddControl(W, H, Type)\n\tif Active ~= nil and (not Active.Ignore) then\n\t\tAddControl(Active, W, H, Type)\n\tend\nend\n\nfunction LayoutManager.ComputeSize(W, H)\n\tif Active ~= nil then\n\t\tlocal X, Y = GetColumnPosition(Active)\n\t\tlocal WinW, WinH = GetColumnSize(Active)\n\t\tlocal RealW = WinW - X\n\t\tlocal RealH = WinH - Y\n\t\tlocal Column = Active.Columns[Active.ColumnNo]\n\n\t\tif not Active.AnchorX then\n\t\t\tRealW = WinW\n\t\tend\n\n\t\tif not Active.AnchorY then\n\t\t\tRealH = WinH\n\t\tend\n\n\t\t-- Retrieve the calculated row width. This information is stored in the 'PendingRows'\n\t\t-- field of the active column. This information is updated in the 'AddControl' function.\n\t\tlocal Row = nil\n\t\tlocal RemainingW = WinW\n\t\tif Column.PendingRows ~= nil then\n\t\t\tRow = Column.PendingRows[Column.RowNo]\n\n\t\t\tif Row ~= nil then\n\t\t\t\tRemainingW = WinW - Row.W\n\t\t\tend\n\t\tend\n\n\t\tW = min(W, RemainingW)\n\n\t\tif Window.IsAutoSize() then\n\t\t\tlocal LayoutH = GetLayoutH(Active, false)\n\n\t\t\tif LayoutH > 0 then\n\t\t\t\tRealH = LayoutH\n\t\t\tend\n\t\tend\n\n\t\tif Active.ExpandW then\n\t\t\tif Column.Rows ~= nil then\n\t\t\t\tlocal Count = 0\n\t\t\t\tlocal ReduceW = 0\n\t\t\t\tlocal Pad = 0\n\t\t\t\tlocal Row = Column.Rows[Column.RowNo]\n\t\t\t\tif Row ~= nil then\n\t\t\t\t\tfor I, V in ipairs(Row.Controls) do\n\t\t\t\t\t\tif V.AlteredSize then\n\t\t\t\t\t\t\tCount = Count + 1\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tReduceW = ReduceW + V.W\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif #Row.Controls > 1 then\n\t\t\t\t\t\tPad = Cursor.PadX() * (#Row.Controls - 1)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tCount = max(Count, 1)\n\n\t\t\t\tW = (RealW - ReduceW - Pad) / Count\n\t\t\tend\n\t\tend\n\n\t\tif Active.ExpandH then\n\t\t\tif Column.Rows ~= nil then\n\t\t\t\tlocal Count = 0\n\t\t\t\tlocal ReduceH = 0\n\t\t\t\tlocal Pad = 0\n\t\t\t\tlocal MaxRowH = 0\n\t\t\t\tfor I, Row in ipairs(Column.Rows) do\n\t\t\t\t\tlocal IsSizeAltered = false\n\n\t\t\t\t\tif I == Column.RowNo then\n\t\t\t\t\t\tMaxRowH = Row.MaxH\n\t\t\t\t\t\tRow.RequestH = max(Row.RequestH, H)\n\t\t\t\t\tend\n\n\t\t\t\t\tfor J, Control in ipairs(Row.Controls) do\n\t\t\t\t\t\tif Control.AlteredSize then\n\t\t\t\t\t\t\tif not IsSizeAltered then\n\t\t\t\t\t\t\t\tCount = Count + 1\n\t\t\t\t\t\t\t\tIsSizeAltered = true\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif not IsSizeAltered then\n\t\t\t\t\t\tReduceH = ReduceH + Row.H\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif #Column.Rows > 1 then\n\t\t\t\t\tPad = Cursor.PadY() * (#Column.Rows - 1)\n\t\t\t\tend\n\n\t\t\t\tCount = max(Count, 1)\n\n\t\t\t\tRealH = max(RealH - ReduceH - Pad, 0)\n\t\t\t\tH = max(RealH / Count, H)\n\t\t\t\tH = max(H, MaxRowH)\n\t\t\tend\n\t\tend\n\n\t\tColumn.AlteredSize = Active.ExpandW or Active.ExpandH\n\tend\n\n\treturn W, H\nend\n\nfunction LayoutManager.Begin(Id, Options)\n\tassert(Id ~= nil or type(Id) ~= string, \"A valid string Id must be given to BeginLayout!\")\n\n\tOptions = Options == nil and {} or Options\n\tOptions.AlignX = Options.AlignX == nil and 'left' or Options.AlignX\n\tOptions.AlignY = Options.AlignY == nil and 'top' or Options.AlignY\n\tOptions.AlignRowY = Options.AlignRowY == nil and 'top' or Options.AlignRowY\n\tOptions.Ignore = Options.Ignore == nil and false or Options.Ignore\n\tOptions.ExpandW = Options.ExpandW == nil and false or Options.ExpandW\n\tOptions.ExpandH = Options.ExpandH == nil and false or Options.ExpandH\n\tOptions.AnchorX = Options.AnchorX == nil and false or Options.AnchorX\n\tOptions.AnchorY = Options.AnchorY == nil and true or Options.AnchorY\n\tOptions.Columns = Options.Columns == nil and 1 or Options.Columns\n\n\tOptions.Columns = max(Options.Columns, 1)\n\n\tlocal Instance = GetInstance(Id)\n\tInstance.AlignX = Options.AlignX\n\tInstance.AlignY = Options.AlignY\n\tInstance.AlignRowY = Options.AlignRowY\n\tInstance.Ignore = Options.Ignore\n\tInstance.ExpandW = Options.ExpandW\n\tInstance.ExpandH = Options.ExpandH\n\tInstance.X, Instance.Y = Cursor.GetRelativePosition()\n\tInstance.AnchorX = Options.AnchorX\n\tInstance.AnchorY = Options.AnchorY\n\n\tif Options.Columns ~= #Instance.Columns then\n\t\tInstance.Columns = {}\n\t\tfor I = 1, Options.Columns, 1 do\n\t\t\tlocal Column = {\n\t\t\t\tRows = nil,\n\t\t\t\tPendingRows = {},\n\t\t\t\tRowNo = 1,\n\t\t\t\tW = 0\n\t\t\t}\n\n\t\t\tinsert(Instance.Columns, Column)\n\t\tend\n\tend\n\n\tfor I, Column in ipairs(Instance.Columns) do\n\t\tColumn.PendingRows = {}\n\t\tColumn.RowNo = 1\n\tend\n\n\tinsert(Stack, 1, Instance)\n\tActive = Instance\nend\n\nfunction LayoutManager.End()\n\tassert(Active ~= nil, \"LayoutManager.End was called without a call to LayoutManager.Begin!\")\n\n\tfor I, Column in ipairs(Active.Columns) do\n\t\tlocal Rows = Column.Rows\n\t\tColumn.Rows = Column.PendingRows\n\t\tColumn.PendingRows = nil\n\n\t\tif Rows ~= nil and Column.Rows ~= nil and #Rows == #Column.Rows then\n\t\t\tfor I, V in ipairs(Rows) do\n\t\t\t\tColumn.Rows[I].MaxH = Rows[I].RequestH\n\t\t\tend\n\t\tend\n\tend\n\n\tremove(Stack, 1)\n\tActive = nil\n\n\tif #Stack > 0 then\n\t\tActive = Stack[1]\n\tend\nend\n\nfunction LayoutManager.SameLine(CursorOptions)\n\tCursor.SameLine(CursorOptions)\n\tif Active ~= nil then\n\t\tlocal Column = Active.Columns[Active.ColumnNo]\n\t\tColumn.RowNo = max(Column.RowNo - 1, 1)\n\n\t\tif Column.Rows ~= nil and CursorOptions ~= nil then\n\t\t\tlocal Row = Column.Rows[Column.RowNo]\n\t\t\tlocal Pad = CursorOptions.Pad\n\n\t\t\tif Row ~= nil and Pad ~= nil then\n\t\t\t\tRow.CursorX = Row.CursorX + Pad\n\t\t\t\tCursor.SetX(Row.CursorX)\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction LayoutManager.NewLine()\n\tif Active ~= nil then\n\t\tAddControl(Active, 0, Cursor.GetNewLineSize(), 'NewLine')\n\tend\n\tCursor.NewLine()\nend\n\nfunction LayoutManager.SetColumn(Index)\n\tif Active ~= nil then\n\t\tIndex = max(Index, 1)\n\t\tIndex = min(Index, #Active.Columns)\n\t\tActive.ColumnNo = Index\n\tend\nend\n\nfunction LayoutManager.GetActiveSize()\n\tif Active ~= nil then\n\t\treturn GetColumnSize(Active)\n\tend\n\n\tlocal WinX, WinY, WinW, WinH = GetWindowBounds()\n\treturn WinW, WinH\nend\n\nfunction LayoutManager.GetCurrentColumnIndex()\n\tif Active ~= nil then\n\t\treturn Active.ColumnNo\n\tend\n\n\treturn 0\nend\n\nfunction LayoutManager.Validate()\n\tlocal Message = nil\n\n\tfor I, V in ipairs(Stack) do\n\t\tif Message == nil then\n\t\t\tMessage = \"The following layouts have not had EndLayout called:\\n\"\n\t\tend\n\n\t\tMessage = Message .. \"'\" .. V.Id .. \"' in window '\" .. V.WindowId .. \"'\\n\"\n\tend\n\n\tassert(Message == nil, Message)\nend\n\n--[[\n\tThis function will return a map of table names with their debug information.\n--]]\nfunction LayoutManager.GetDebugInfo()\n\tlocal Result = {}\n\n\tfor K, V in pairs(Instances) do\n\t\tlocal Info = {}\n\t\tinsert(Info, \"X: \" .. V.X)\n\t\tinsert(Info, \"Y: \" .. V.Y)\n\t\tinsert(Info, \"AlignX: \" .. V.AlignX)\n\t\tinsert(Info, \"AlignY: \" .. V.AlignY)\n\t\tinsert(Info, \"AlignRowY: \" .. V.AlignRowY)\n\t\tinsert(Info, \"Ignore: \" .. tostring(V.Ignore))\n\t\tinsert(Info, \"ExpandW: \" .. tostring(V.ExpandW))\n\t\tinsert(Info, \"ExpandH: \" .. tostring(V.ExpandH))\n\n\t\tinsert(Info, \"Columns: \" .. #V.Columns)\n\t\tfor ColumnNo, Column in ipairs(V.Columns) do\n\t\t\tinsert(Info, \"   \" .. ColumnNo .. \": W: \" .. Column.W .. \" Rows: \" .. (Column.Rows and #Column.Rows or 0))\n\n\t\t\tif Column.Rows ~= nil then\n\t\t\t\tfor RowNo, Row in ipairs(Column.Rows) do\n\t\t\t\t\tinsert(Info, \"      \" .. RowNo .. \": W: \" .. Row.W .. \" H: \" .. Row.H .. \" Controls: \" .. (Row.Controls and #Row.Controls or 0))\n\n\t\t\t\t\tif Row.Controls ~= nil then\n\t\t\t\t\t\tfor ControlNo, Control in pairs(Row.Controls) do\n\t\t\t\t\t\t\tinsert(Info, \"         \" .. ControlNo .. \": \" .. \" W: \" .. Control.W .. \" H: \" .. Control.H .. \" Type: \" .. tostring(Control.Type))\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tResult[K] = Info\n\tend\n\n\treturn Result\nend\n\nreturn LayoutManager\n"
  },
  {
    "path": "Internal/UI/ListBox.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Region = require(SLAB_PATH .. '.Internal.UI.Region')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Text = require(SLAB_PATH .. '.Internal.UI.Text')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\n\nlocal ListBox = {}\nlocal Instances = {}\nlocal ActiveInstance = nil\nlocal EMPTY = {}\nlocal IGNORE = { Ignore = true }\n\nlocal function GetItemInstance(instance, id)\n\tif not instance then\n\t\treturn\n\tend\n\n\tif instance.Items[id] == nil then\n\t\tinstance.Items[id] = {\n\t\t\tId = id,\n\t\t\tX = 0,\n\t\t\tY = 0,\n\t\t\tW = 0,\n\t\t\tH = 0,\n\t\t}\n\tend\n\treturn instance.Items[id]\nend\n\nlocal function GetInstance(id)\n\tif Instances[id] == nil then\n\t\tInstances[id] = {\n\t\t\tId = id,\n\t\t\tX = 0,\n\t\t\tY = 0,\n\t\t\tW = 0,\n\t\t\tH = 0,\n\t\t\tItems = {},\n\t\t\tActiveItem = nil,\n\t\t\tHotItem = nil,\n\t\t\tSelected = false,\n\t\t\tStatHandle = nil,\n\t\t\tRegion = {\n\t\t\t\tAutoSizeContent = true,\n\t\t\t\tIntersect = true,\n\t\t\t},\n\t\t}\n\tend\n\treturn Instances[id]\nend\n\nfunction ListBox.Begin(id, options)\n\tlocal statHandle = Stats.Begin('ListBox', 'Slab')\n\n\toptions = options or EMPTY\n\tlocal w = options.W or 150\n\tlocal h = options.H or 150\n\tlocal rounding = options.Rounding or Style.WindowRounding\n\tlocal bgColor = options.BgColor or Style.ListBoxBgColor\n\n\tlocal instance = GetInstance(Window.GetItemId(id))\n\n\tif options.Clear then\n\t\tfor i = 1, #instance.Items do\n\t\t\tinstance.Items[i] = nil\n\t\tend\n\tend\n\n\tw, h = LayoutManager.ComputeSize(w, h)\n\tLayoutManager.AddControl(w, h, 'ListBox')\n\n\tdo\n\t\tlocal remainingW, remainingH = Window.GetRemainingSize()\n\t\tif options.StretchW then\n\t\t\tw = remainingW\n\t\tend\n\n\t\tif options.StretchH then\n\t\t\th = remainingH\n\t\tend\n\tend\n\n\tlocal x, y = Cursor.GetPosition()\n\tinstance.X = x\n\tinstance.Y = y\n\tinstance.W = w\n\tinstance.H = h\n\tinstance.StatHandle = statHandle\n\tActiveInstance = instance\n\n\tCursor.SetItemBounds(x, y, w, h)\n\tCursor.AdvanceY(0)\n\n\tWindow.AddItem(x, y, w, h, instance.Id)\n\n\tlocal isObstructed = Window.IsObstructedAtMouse()\n\n\tlocal mouseX, mouseY = Window.GetMousePosition()\n\tdo\n\t\tlocal tx, ty = Window.TransformPoint(x, y)\n\t\tlocal region = instance.Region\n\t\tregion.X = x\n\t\tregion.Y = y\n\t\tregion.W = w\n\t\tregion.H = h\n\t\tregion.SX = tx\n\t\tregion.SY = ty\n\t\tregion.BgColor = bgColor\n\t\tregion.MouseX = mouseX\n\t\tregion.MouseY = mouseY\n\t\tregion.ResetContent = Window.HasResized()\n\t\tregion.IsObstructed = isObstructed\n\t\tregion.Rounding = rounding\n\t\tRegion.Begin(instance.Id, region)\n\tend\n\n\tlocal in_region = Region.Contains(mouseX, mouseY)\n\tinstance.HotItem = nil\n\tmouseX, mouseY = Region.InverseTransform(instance.Id, mouseX, mouseY)\n\tfor k, v in pairs(instance.Items) do\n\t\tif not isObstructed\n\t\t\tand not Region.IsHoverScrollBar(instance.Id)\n\t\t\tand v.X <= mouseX and mouseX <= v.X + instance.W and v.Y <= mouseY and mouseY <= v.Y + v.H\n\t\t\tand in_region\n\t\t\tthen\n\t\t\tinstance.HotItem = v\n\t\tend\n\n\t\tif instance.HotItem == v or v.Selected then\n\t\t\tDrawCommands.Rectangle('fill', v.X, v.Y, instance.W, v.H, Style.TextHoverBgColor)\n\t\tend\n\tend\n\n\tLayoutManager.Begin('Ignore', IGNORE)\nend\n\nfunction ListBox.BeginItem(id, options)\n\toptions = options or EMPTY\n\n\tassert(ActiveInstance ~= nil, \"Trying to call BeginListBoxItem outside of BeginListBox.\")\n\tif ActiveInstance.ActiveItem ~= nil then\n\t\terror((\"BeginListBoxItem was called for item '%s' without a call to EndListBoxItem.\"):format(\n\t\t\tActiveInstance.ActiveItem.Id or \"nil\"\n\t\t))\n\tend\n\tlocal item = GetItemInstance(ActiveInstance, id)\n\titem.X = ActiveInstance.X\n\titem.Y = Cursor.GetY()\n\tCursor.SetX(item.X)\n\tCursor.AdvanceX(0)\n\tActiveInstance.ActiveItem = item\n\tActiveInstance.ActiveItem.Selected = options.Selected\nend\n\nfunction ListBox.IsItemClicked(button, isDoubleClick)\n\tassert(ActiveInstance ~= nil, \"Trying to call IsItemClicked outside of BeginListBox.\")\n\tassert(ActiveInstance.ActiveItem ~= nil, \"IsItemClicked was called outside of BeginListBoxItem.\")\n\tif ActiveInstance.HotItem ~= ActiveInstance.ActiveItem then\n\t\treturn false\n\tend\n\n\tbutton = button or 1\n\tif isDoubleClick then\n\t\treturn Mouse.IsDoubleClicked(button)\n\telse\n\t\treturn Mouse.IsClicked(button)\n\tend\nend\n\nfunction ListBox.EndItem()\n\tassert(ActiveInstance ~= nil, \"Trying to call BeginListBoxItem outside of BeginListBox.\")\n\tassert(ActiveInstance.ActiveItem ~= nil, \"Trying to call EndListBoxItem without calling BeginListBoxItem.\")\n\tlocal itemX, itemY, itemW, itemH = Cursor.GetItemBounds()\n\tActiveInstance.ActiveItem.W = itemW\n\tActiveInstance.ActiveItem.H = Cursor.GetLineHeight()\n\tCursor.SetY(ActiveInstance.ActiveItem.Y + ActiveInstance.ActiveItem.H)\n\tCursor.AdvanceY(0)\n\tActiveInstance.ActiveItem = nil\nend\n\nfunction ListBox.End()\n\tassert(ActiveInstance ~= nil, \"EndListBox was called without calling BeginListBox.\")\n\tRegion.End()\n\tRegion.ApplyScissor()\n\n\tCursor.SetItemBounds(ActiveInstance.X, ActiveInstance.Y, ActiveInstance.W, ActiveInstance.H)\n\tCursor.SetPosition(ActiveInstance.X, ActiveInstance.Y)\n\tCursor.AdvanceY(ActiveInstance.H)\n\n\tLayoutManager.End()\n\n\tStats.End(ActiveInstance.StatHandle)\n\n\tActiveInstance = nil\nend\n\nreturn ListBox\n"
  },
  {
    "path": "Internal/UI/Menu.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal insert = table.insert\nlocal remove = table.remove\nlocal max = math.max\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal MenuState = require(SLAB_PATH .. '.Internal.UI.MenuState')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Text = require(SLAB_PATH .. '.Internal.UI.Text')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\nlocal Scale = require(SLAB_PATH .. \".Internal.Core.Scale\")\n\n\nlocal Menu = {}\nlocal Instances = {}\n\nlocal Pad = 8.0\nlocal LeftPad = 25.0\nlocal RightPad = 70.0\nlocal CheckSize = 5.0\nlocal OpenedContextMenu = nil\n\nlocal function IsItemHovered()\n\tlocal ItemX, ItemY, ItemW, ItemH = Cursor.GetItemBounds()\n\tlocal MouseX, MouseY = Window.GetMousePosition()\n\treturn not Window.IsObstructedAtMouse()\n\t\tand ItemX < MouseX and MouseX < ItemX + Window.GetWidth()\n\t\tand ItemY < MouseY and MouseY < ItemY + ItemH\nend\n\nlocal function AlterOptions(Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.Enabled = Options.Enabled == nil and true or Options.Enabled\n\tOptions.IsSelectable = Options.Enabled\n\tOptions.SelectOnHover = Options.Enabled\n\tOptions.PadH = Style.MenuItemPadH\n\n\tif Options.Enabled then\n\t\tOptions.Color = Style.TextColor\n\telse\n\t\tOptions.Color = Style.TextDisabledColor\n\tend\n\n\treturn Options\nend\n\nlocal function ConstrainPosition(X, Y, W, H)\n\tlocal ResultX, ResultY = X, Y\n\n\tlocal Right = X + W\n\tlocal Bottom = Y + H\n\tlocal OffsetX = Right >= Scale.GetScreenWidth()\n\tlocal OffsetY = Bottom >= Scale.GetScreenHeight()\n\n\tif OffsetX then\n\t\tResultX = X - (Right - Scale.GetScreenWidth())\n\tend\n\n\tif OffsetY then\n\t\tResultY = Y - H\n\tend\n\n\tlocal WinX, WinY, WinW, WinH = Window.GetBounds()\n\tif OffsetX then\n\t\tResultX = WinX - W\n\tend\n\n\tResultX = max(ResultX, 0.0)\n\tResultY = max(ResultY, 0.0)\n\n\treturn ResultX, ResultY\nend\n\nlocal function BeginWindow(Id, X, Y, RoundAllCorners)\n\tlocal Instance = Instances[Id]\n\tif Instance ~= nil then\n\t\tX, Y = ConstrainPosition(X, Y, Instance.W, Instance.H)\n\tend\n\n\tCursor.PushContext()\n\tWindow.Begin(Id,\n\t{\n\t\tX = X,\n\t\tY = Y,\n\t\tW = 0.0,\n\t\tH = 0.0,\n\t\tAllowResize = false,\n\t\tAllowFocus = false,\n\t\tBorder = 0.0,\n\t\tAutoSizeWindow = true,\n\t\tLayer = 'ContextMenu',\n\t\tBgColor = Style.MenuColor,\n\t\tRounding = RoundAllCorners and {2, 2, 2, 2} or {0, 0, 2, 2},\n\t\tNoSavedSettings = true\n\t})\nend\n\nfunction Menu.BeginMenu(Label, Options)\n\tlocal Result = false\n\tlocal X, Y = Cursor.GetPosition()\n\tlocal IsMenuBar = Window.IsMenuBar()\n\tlocal Id = Window.GetId() .. \".\" .. Label\n\tlocal Win = Window.Top()\n\n\tOptions = AlterOptions(Options)\n\tOptions.IsSelected = Options.Enabled and Win.Selected == Id\n\n\tif IsMenuBar then\n\t\tOptions.IsSelectableTextOnly = Options.Enabled\n\t\tOptions.Pad = Pad * 2\n\t\tOptions.PadH = Style.MenuPadH\n\telse\n\t\tCursor.SetX(X + LeftPad)\n\tend\n\n\tlocal MenuX = 0.0\n\tlocal MenuY = 0.0\n\n\t-- 'Result' may be false if 'Enabled' is false. The hovered state is still required\n\t-- so that will be handled differently.\n\tResult = Text.Begin(Label, Options)\n\tlocal ItemX, ItemY, ItemW, ItemH = Cursor.GetItemBounds()\n\tif IsMenuBar then\n\t\tCursor.SameLine()\n\n\t\t-- Menubar items don't extend to the width of the window since these items are layed out horizontally. Only\n\t\t-- need to perform hover check on item bounds.\n\t\tlocal Hovered = Cursor.IsInItemBounds(Window.GetMousePosition())\n\t\tif Hovered then\n\t\t\tif Mouse.IsClicked(1) then\n\t\t\t\tif Result then\n\t\t\t\t\tMenuState.WasOpened = MenuState.IsOpened\n\t\t\t\t\tMenuState.IsOpened = not MenuState.IsOpened\n\n\t\t\t\t\tif MenuState.IsOpened then\n\t\t\t\t\t\tMenuState.RequestClose = false\n\t\t\t\t\tend\n\t\t\t\telseif MenuState.WasOpened then\n\t\t\t\t\tMenuState.RequestClose = false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif MenuState.IsOpened and OpenedContextMenu == nil then\n\t\t\tif Result then\n\t\t\t\tWin.Selected = Id\n\t\t\tend\n\t\telse\n\t\t\tWin.Selected = nil\n\t\tend\n\n\t\tMenuX = X\n\t\tMenuY = Y + Window.GetHeight()\n\telse\n\t\tlocal WinX, WinY, WinW, WinH = Window.GetBounds()\n\t\tlocal H = Style.Font:getHeight()\n\t\tlocal TriX = WinX + WinW - H * 0.75\n\t\tlocal TriY = Y + H * 0.5\n\t\tlocal Radius = H * 0.35\n\t\tDrawCommands.Triangle('fill', TriX, TriY, Radius, 90, Style.TextColor)\n\n\t\tMenuX = X + WinW\n\t\tMenuY = Y\n\n\t\tif Result then\n\t\t\tWin.Selected = Id\n\t\tend\n\n\t\tWindow.AddItem(ItemX, ItemY, ItemW + RightPad, ItemH)\n\n\t\t-- Prevent closing the menu window if this item is clicked.\n\t\tif IsItemHovered() and Mouse.IsClicked(1) then\n\t\t\tMenuState.RequestClose = false\n\t\tend\n\tend\n\n\tResult = Win.Selected == Id\n\n\tif Result then\n\t\tBeginWindow(Id, MenuX, MenuY, not IsMenuBar)\n\tend\n\n\treturn Result\nend\n\nfunction Menu.MenuItem(Label, Options)\n\tOptions = AlterOptions(Options)\n\n\tlocal HintWidth = Options.Hint == nil and 0 or Text.GetWidth(Options.Hint)\n\n\tCursor.SetX(Cursor.GetX() + LeftPad)\n\tlocal Result = Text.Begin(Label, Options)\n\tlocal ItemX, ItemY, ItemW, ItemH = Cursor.GetItemBounds()\n\tWindow.AddItem(ItemX, ItemY, ItemW + RightPad + HintWidth, ItemH)\n\n\tif Options.Hint ~= nil then\n\t\tCursor.SameLine()\n\t\tText.BeginFormatted(Options.Hint, {\n\t\t\tAlign = \"right\",\n\t\t\tW = Window.GetRemainingSize() - LeftPad, -- Pad the right side equal to the left side\n\t\t\tH = ItemH,\n\t\t\tColor = Style.TextDisabledColor,\n\t\t\tRightPad = LeftPad, -- hack, see Text.BeginFormatted()\n\t\t})\n\tend\n\n\tif Result then\n\t\tlocal Win = Window.Top()\n\t\tWin.Selected = nil\n\n\t\tResult = Mouse.IsClicked(1)\n\t\tif Result and MenuState.WasOpened then\n\t\t\tMenuState.RequestClose = true\n\t\tend\n\telse\n\t\tif IsItemHovered() and Mouse.IsClicked(1) then\n\t\t\tMenuState.RequestClose = false\n\t\tend\n\tend\n\n\treturn Result\nend\n\nfunction Menu.MenuItemChecked(Label, IsChecked, Options)\n\tOptions = AlterOptions(Options)\n\tlocal X, Y = Cursor.GetPosition()\n\tlocal Result = Menu.MenuItem(Label, Options)\n\n\tif IsChecked then\n\t\tlocal H = Style.Font:getHeight() + Options.PadH\n\t\tDrawCommands.Check(X + LeftPad * 0.5, Y + H * 0.5, CheckSize, Options.Color)\n\tend\n\n\treturn Result\nend\n\nfunction Menu.Separator()\n\tlocal Ctx = Context.Top()\n\tif Ctx.Type == 'Menu' then\n\t\tlocal Item = GetItem(\"Sep_\" .. Ctx.Data.SeparatorId)\n\t\tItem.IsSeparator = true\n\t\tCtx.Data.SeparatorId = Ctx.Data.SeparatorId + 1\n\tend\nend\n\nfunction Menu.EndMenu()\n\tlocal Id = Window.GetId()\n\tif Instances[Id] == nil then\n\t\tInstances[Id] = {}\n\tend\n\tInstances[Id].W = Window.GetWidth()\n\tInstances[Id].H = Window.GetHeight()\n\n\tWindow.End()\n\tCursor.PopContext()\nend\n\nfunction Menu.Pad()\n\treturn Pad\nend\n\nfunction Menu.BeginContextMenu(Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.IsItem = Options.IsItem == nil and false or Options.IsItem\n\tOptions.IsWindow = Options.IsWindow == nil and false or Options.IsWindow\n\tOptions.Button = Options.Button == nil and 2 or Options.Button\n\n\tlocal BaseId = nil\n\tlocal Id = nil\n\tif Options.IsWindow then\n\t\tBaseId = Window.GetId()\n\telseif Options.IsItem then\n\t\tBaseId = Window.GetContextHotItem()\n\t\tif BaseId == nil then\n\t\t\tBaseId = Window.GetHotItem()\n\t\tend\n\tend\n\n\tif Options.IsItem and Window.GetLastItem() ~= BaseId then\n\t\treturn false\n\tend\n\n\tif BaseId ~= nil then\n\t\tId = BaseId .. '.ContextMenu'\n\tend\n\n\tif Id == nil then\n\t\treturn false\n\tend\n\n\tif MenuState.IsOpened and OpenedContextMenu ~= nil then\n\t\tif OpenedContextMenu.Id == Id then\n\t\t\tBeginWindow(OpenedContextMenu.Id, OpenedContextMenu.X, OpenedContextMenu.Y, true)\n\t\t\treturn true\n\t\tend\n\t\treturn false\n\tend\n\n\tlocal IsOpening = false\n\tif not Window.IsObstructedAtMouse() and Window.IsMouseHovered() and Mouse.IsClicked(Options.Button) then\n\t\tlocal IsValidWindow = Options.IsWindow and Window.GetHotItem() == nil\n\t\tlocal IsValidItem = Options.IsItem\n\n\t\tif IsValidWindow or IsValidItem then\n\t\t\tMenuState.IsOpened = true\n\t\t\tIsOpening = true\n\t\tend\n\tend\n\n\tif IsOpening then\n\t\tlocal X, Y = Mouse.Position()\n\t\tX, Y = ConstrainPosition(X, Y, 0.0, 0.0)\n\t\tOpenedContextMenu = {Id = Id, X = X, Y = Y, Win = Window.Top()}\n\t\tWindow.SetContextHotItem(Options.IsItem and BaseId or nil)\n\tend\n\n\treturn false\nend\n\nfunction Menu.EndContextMenu()\n\tMenu.EndMenu()\nend\n\nfunction Menu.Close()\n\tMenuState.WasOpened = MenuState.IsOpened\n\tMenuState.IsOpened = false\n\tMenuState.RequestClose = false\n\n\tif OpenedContextMenu ~= nil then\n\t\tOpenedContextMenu.Win.ContextHotItem = nil\n\t\tOpenedContextMenu = nil\n\tend\nend\n\nreturn Menu\n"
  },
  {
    "path": "Internal/UI/MenuBar.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Cursor = require(SLAB_PATH .. \".Internal.Core.Cursor\")\nlocal DrawCommands = require(SLAB_PATH .. \".Internal.Core.DrawCommands\")\nlocal Menu = require(SLAB_PATH .. \".Internal.UI.Menu\")\nlocal MenuState = require(SLAB_PATH .. \".Internal.UI.MenuState\")\nlocal Style = require(SLAB_PATH .. \".Style\")\nlocal Window = require(SLAB_PATH .. \".Internal.UI.Window\")\n\nlocal MenuBar = {}\nlocal Instances = {}\n\nlocal function GetInstance()\n\tlocal Win = Window.Top()\n\tif Instances[Win] == nil then\n\t\tlocal Instance = {}\n\t\tInstance.Selected = nil\n\t\tInstance.Id = Win.Id .. '_MenuBar'\n\t\tInstances[Win] = Instance\n\tend\n\treturn Instances[Win]\nend\n\nfunction MenuBar.Begin(IsMainMenuBar)\n\tlocal X, Y = Cursor.GetPosition()\n\tlocal WinX, WinY, WinW, WinH = Window.GetBounds()\n\tlocal Instance = GetInstance()\n\n\tif not MenuState.IsOpened then\n\t\tInstance.Selected = nil\n\tend\n\n\tif IsMainMenuBar then\n\t\tMenuState.MainMenuBarH = Style.Font:getHeight() + Style.MenuPadH\n\tend\n\n\tWindow.Begin(Instance.Id,\n\t{\n\t\tX = X,\n\t\tY = Y,\n\t\tW = WinW,\n\t\tH = Style.Font:getHeight() + Style.MenuPadH,\n\t\tAllowResize = false,\n\t\tAllowFocus = false,\n\t\tBorder = 0.0,\n\t\tBgColor = Style.MenuColor,\n\t\tNoOutline = true,\n\t\tIsMenuBar = true,\n\t\tAutoSizeWindow = false,\n\t\tAutoSizeContent = false,\n\t\tLayer = IsMainMenuBar and 'MainMenuBar' or nil,\n\t\tRounding = 0.0,\n\t\tNoSavedSettings = true\n\t})\n\n\tCursor.AdvanceX(4.0)\n\n\treturn true\nend\n\nfunction MenuBar.End()\n\tWindow.End()\nend\n\nfunction MenuBar.Clear()\n\tfor I, V in ipairs(Instances) do\n\t\tV.Selected = nil\n\tend\nend\n\nreturn MenuBar\n"
  },
  {
    "path": "Internal/UI/MenuState.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal MenuState =\n{\n\tIsOpened = false,\n\tWasOpened = false,\n\tRequestClose = false,\n\tMainMenuBarH = 0\n}\n\nreturn MenuState\n"
  },
  {
    "path": "Internal/UI/Region.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal max = math.max\nlocal min = math.min\nlocal floor = math.floor\n\nlocal DrawCommands = require(SLAB_PATH .. \".Internal.Core.DrawCommands\")\nlocal MenuState = require(SLAB_PATH .. \".Internal.UI.MenuState\")\nlocal Mouse = require(SLAB_PATH .. \".Internal.Input.Mouse\")\nlocal Style = require(SLAB_PATH .. \".Style\")\nlocal Utility = require(SLAB_PATH .. \".Internal.Core.Utility\")\n\nlocal Region = {}\nlocal Instances = {}\nlocal Stack = {}\nlocal ActiveInstance = nil\nlocal ScrollPad = 3\nlocal ScrollBarSize = 10\nlocal WheelX = 0\nlocal WheelY = 0\nlocal WheelSpeed = 3\nlocal HotInstance = nil\nlocal WheelInstance = nil\nlocal ScrollInstance = nil\n\nlocal EMPTY = {}\nlocal tempColor = {}\n\nlocal function GetXScrollSize(instance)\n\tif instance ~= nil then\n\t\treturn max(instance.W - (instance.ContentW - instance.W), 10)\n\tend\n\treturn 0\nend\n\nlocal function GetYScrollSize(instance)\n\tif instance ~= nil then\n\t\treturn max(instance.H - (instance.ContentH - instance.H), 10)\n\tend\n\treturn 0\nend\n\nlocal function IsScrollHovered(instance, x, y)\n\tlocal hasScrollX, hasScrollY = false, false\n\tif not instance then return end\n\n\tif instance.HasScrollX then\n\t\tlocal posY = instance.Y + instance.H - ScrollPad - ScrollBarSize\n\t\tlocal sizeX = GetXScrollSize(instance)\n\t\tlocal posX = instance.ScrollPosX\n\t\thasScrollX = instance.X + posX <= x and x < instance.X + posX + sizeX and posY <= y and y < posY + ScrollBarSize\n\tend\n\n\tif instance.HasScrollY then\n\t\tlocal posX = instance.X + instance.W - ScrollPad - ScrollBarSize\n\t\tlocal sizeY = GetYScrollSize(instance)\n\t\tlocal posY = instance.ScrollPosY\n\t\thasScrollY = posX <= x and x < posX + ScrollBarSize and instance.Y + posY <= y and y < instance.Y + posY + sizeY\n\tend\n\n\treturn hasScrollX, hasScrollY\nend\n\nlocal function Contains(instance, x, y)\n\tif instance ~= nil then\n\t\treturn instance.X <= x and x <= instance.X + instance.W and instance.Y <= y and y <= instance.Y + instance.H\n\tend\n\treturn false\nend\n\nlocal function UpdateScrollBars(instance, isObstructed, forceShowX, forceShowY)\n\tif instance.IgnoreScroll then\n\t\treturn\n\tend\n\n\tinstance.HasScrollX = forceShowX or (instance.ContentW > instance.W)\n\tinstance.HasScrollY = forceShowY or (instance.ContentH > instance.H)\n\n\tlocal x, y = instance.MouseX, instance.MouseY\n\tinstance.HoverScrollX, instance.HoverScrollY = IsScrollHovered(instance, x, y)\n\tlocal xSize = instance.W - GetXScrollSize(instance)\n\tlocal ySize = instance.H - GetYScrollSize(instance)\n\n\tif isObstructed then\n\t\tinstance.HoverScrollX = false\n\t\tinstance.HoverScrollY = false\n\tend\n\n\tlocal isMouseReleased = Mouse.IsReleased(1)\n\tlocal isMouseClicked = Mouse.IsClicked(1)\n\n\tlocal deltaX, deltaY = Mouse.GetDelta()\n\n\tif WheelInstance == instance then\n\t\tinstance.HoverScrollX = WheelX ~= 0\n\t\tinstance.HoverScrollY = WheelY ~= 0\n\n\t\tif not instance.HoverScrollX and instance.HoverScrollY and not instance.HasScrollY then\n\t\t\tinstance.HoverScrollX = instance.HoverScrollY\n\t\t\tWheelX = -WheelY\n\t\tend\n\tend\n\n\tif not isObstructed and Contains(instance, x, y) or (instance.HoverScrollX or instance.HoverScrollY) then\n\t\tif WheelInstance == instance then\n\t\t\tif WheelX ~= 0 then\n\t\t\t\tinstance.ScrollPosX = max(instance.ScrollPosX + WheelX, 0)\n\t\t\t\tinstance.IsScrollingX = true\n\t\t\t\tisMouseReleased = true\n\t\t\t\tWheelX = 0\n\t\t\tend\n\n\t\t\tif WheelY ~= 0 then\n\t\t\t\tinstance.ScrollPosY = max(instance.ScrollPosY - WheelY, 0)\n\t\t\t\tinstance.IsScrollingY = true\n\t\t\t\tisMouseReleased = true\n\t\t\t\tWheelY = 0\n\t\t\tend\n\n\t\t\tWheelInstance = nil\n\t\t\tScrollInstance = instance\n\t\tend\n\n\t\tif ScrollInstance == nil and isMouseClicked and (instance.HoverScrollX or instance.HoverScrollY) then\n\t\t\tScrollInstance = instance\n\t\t\tScrollInstance.IsScrollingX = instance.HoverScrollX\n\t\t\tScrollInstance.IsScrollingY = instance.HoverScrollY\n\t\tend\n\tend\n\n\tif ScrollInstance == instance and isMouseReleased then\n\t\tinstance.IsScrollingX = false\n\t\tinstance.IsScrollingY = false\n\t\tScrollInstance = nil\n\tend\n\n\tif instance.HasScrollX then\n\t\tif instance.HasScrollY then\n\t\t\txSize = xSize - ScrollBarSize - ScrollPad\n\t\tend\n\t\txSize = max(xSize, 0)\n\t\tif ScrollInstance == instance then\n\t\t\tMenuState.RequestClose = false\n\n\t\t\tif instance.IsScrollingX then\n\t\t\t\tinstance.ScrollPosX = max(instance.ScrollPosX + deltaX, 0)\n\t\t\tend\n\t\tend\n\t\tinstance.ScrollPosX = min(instance.ScrollPosX, xSize)\n\tend\n\n\tif instance.HasScrollY then\n\t\tif instance.HasScrollX then\n\t\t\tySize = ySize - ScrollBarSize - ScrollPad\n\t\tend\n\t\tySize = max(ySize, 0)\n\t\tif ScrollInstance == instance then\n\t\t\tMenuState.RequestClose = false\n\n\t\t\tif instance.IsScrollingY then\n\t\t\t\tinstance.ScrollPosY = max(instance.ScrollPosY + deltaY, 0)\n\t\t\tend\n\t\tend\n\t\tinstance.ScrollPosY = min(instance.ScrollPosY, ySize)\n\tend\n\n\tlocal xRatio, yRatio = 0, 0\n\tif xSize ~= 0 then\n\t\txRatio = max(instance.ScrollPosX / xSize, 0)\n\tend\n\tif ySize ~= 0 then\n\t\tyRatio = max(instance.ScrollPosY / ySize, 0)\n\tend\n\n\tlocal tx = max(instance.ContentW - instance.W, 0) * -xRatio\n\tlocal ty = max(instance.ContentH - instance.H, 0) * -yRatio\n\tinstance.Transform:setTransformation(floor(tx), floor(ty))\nend\n\nlocal function DrawScrollBars(instance)\n\tif not instance.HasScrollX and not instance.HasScrollY then\n\t\treturn\n\tend\n\n\tif HotInstance ~= instance and ScrollInstance ~= instance and not Utility.IsMobile() then\n\t\tlocal dt = love.timer.getDelta()\n\t\tinstance.ScrollAlphaX = max(instance.ScrollAlphaX - dt, 0)\n\t\tinstance.ScrollAlphaY = max(instance.ScrollAlphaY - dt, 0)\n\telse\n\t\tinstance.ScrollAlphaX = 1\n\t\tinstance.ScrollAlphaY = 1\n\tend\n\n\tif instance.HasScrollX then\n\t\tlocal xSize = GetXScrollSize(instance)\n\t\tlocal color = Utility.MakeColor(Style.ScrollBarColor, tempColor)\n\t\tif instance.HoverScrollX or instance.IsScrollingX then\n\t\t\tcolor = Utility.MakeColor(Style.ScrollBarHoveredColor, tempColor)\n\t\tend\n\t\tcolor[4] = instance.ScrollAlphaX\n\t\tlocal xPos = instance.ScrollPosX\n\t\tDrawCommands.Rectangle('fill', instance.X + xPos, instance.Y + instance.H - ScrollPad - ScrollBarSize, xSize, ScrollBarSize, color, Style.ScrollBarRounding)\n\tend\n\n\tif instance.HasScrollY then\n\t\tlocal ySize = GetYScrollSize(instance)\n\t\tlocal color = Utility.MakeColor(Style.ScrollBarColor, tempColor)\n\t\tif instance.HoverScrollY or instance.IsScrollingY then\n\t\t\tcolor = Utility.MakeColor(Style.ScrollBarHoveredColor, tempColor)\n\t\tend\n\t\tcolor[4] = instance.ScrollAlphaY\n\t\tlocal yPos = instance.ScrollPosY\n\t\tDrawCommands.Rectangle('fill', instance.X + instance.W - ScrollPad - ScrollBarSize, instance.Y + yPos, ScrollBarSize, ySize, color, Style.ScrollBarRounding)\n\tend\nend\n\nlocal function GetInstance(id)\n\tif id == nil then\n\t\treturn ActiveInstance\n\tend\n\n\tif Instances[id] == nil then\n\t\tlocal instance = {\n\t\t\tId = id,\n\t\t\tX = 0,\n\t\t\tY = 0,\n\t\t\tW = 0,\n\t\t\tH = 0,\n\t\t\tSX = 0,\n\t\t\tSY = 0,\n\t\t\tContentW = 0,\n\t\t\tContentH = 0,\n\t\t\tHasScrollX = false,\n\t\t\tHasScrollY = false,\n\t\t\tHoverScrollX = false,\n\t\t\tHoverScrollY = false,\n\t\t\tIsScrollingX = false,\n\t\t\tIsScrollingY = false,\n\t\t\tScrollPosX = 0,\n\t\t\tScrollPosY = 0,\n\t\t\tScrollAlphaX = 0,\n\t\t\tScrollAlphaY = 0,\n\t\t\tIntersect = false,\n\t\t\tAutoSizeContent = false,\n\t\t\tTransform = love.math.newTransform(),\n\t\t}\n\t\tInstances[id] = instance\n\tend\n\treturn Instances[id]\nend\n\nfunction Region.Begin(id, options)\n\toptions = options or EMPTY\n\n\tlocal instance = GetInstance(id)\n\tinstance.X = options.X or 0\n\tinstance.Y = options.Y or 0\n\tinstance.W = options.W or 0\n\tinstance.H = options.H or 0\n\tinstance.SX = options.SX or instance.X\n\tinstance.SY = options.SY or instance.Y\n\tinstance.Intersect = options.Intersect\n\tinstance.IgnoreScroll = options.IgnoreScroll\n\tinstance.MouseX = options.MouseX or 0\n\tinstance.MouseY = options.MouseY or 0\n\tinstance.AutoSizeContent = options.AutoSizeContent\n\n\tif options.ResetContent then\n\t\tinstance.ContentW = 0\n\t\tinstance.ContentH = 0\n\tend\n\n\tif not options.AutoSizeContent then\n\t\tinstance.ContentW = options.ContentW or 0\n\t\tinstance.ContentH = options.ContentH or 0\n\tend\n\n\tActiveInstance = instance\n\ttable.insert(Stack, 1, ActiveInstance)\n\n\tUpdateScrollBars(instance, options.IsObstructed, options.ForceShowX, options.ForceShowY)\n\n\tif options.AutoSizeContent then\n\t\tinstance.ContentH = 0\n\t\tinstance.ContentW = 0\n\tend\n\n\tif HotInstance == instance and (not Contains(instance, instance.MouseX, instance.MouseY) or options.IsObstructed) then\n\t\tHotInstance = nil\n\tend\n\n\tif not options.IsObstructed then\n\t\tif (\n\t\t\toptions.ForceShowX or\n\t\t\toptions.ForceShowY or\n\t\t\tContains(instance, instance.MouseX, instance.MouseY) or\n\t\t\t(instance.HoverScrollX or instance.HoverScrollY)\n\t\t) then\n\t\t\tif ScrollInstance == nil then\n\t\t\t\tHotInstance = instance\n\t\t\telse\n\t\t\t\tHotInstance = ScrollInstance\n\t\t\tend\n\t\tend\n\tend\n\n\tif not options.NoBackground then\n\t\tDrawCommands.Rectangle('fill', instance.X, instance.Y, instance.W, instance.H, options.BgColor or Style.WindowBackgroundColor, options.Rounding or 0)\n\tend\n\tif not options.NoOutline then\n\t\tDrawCommands.Rectangle('line', instance.X, instance.Y, instance.W, instance.H, nil, options.Rounding or 0)\n\tend\n\tDrawCommands.TransformPush()\n\tDrawCommands.ApplyTransform(instance.Transform)\n\tRegion.ApplyScissor()\nend\n\nfunction Region.End()\n\tDrawCommands.TransformPop()\n\tDrawScrollBars(ActiveInstance)\n\n\tif HotInstance == ActiveInstance\n\t\tand WheelInstance == nil\n\t\tand (WheelX ~= 0 or WheelY ~= 0)\n\t\tand not ActiveInstance.IgnoreScroll then\n\t\tWheelInstance = ActiveInstance\n\tend\n\n\tif ActiveInstance.Intersect then\n\t\tDrawCommands.IntersectScissor()\n\telse\n\t\tDrawCommands.Scissor()\n\tend\n\n\tActiveInstance = nil\n\ttable.remove(Stack, 1)\n\n\tif #Stack > 0 then\n\t\tActiveInstance = Stack[1]\n\tend\nend\n\nfunction Region.IsHoverScrollBar(id)\n\tlocal instance = GetInstance(id)\n\tif instance ~= nil then\n\t\treturn instance.HoverScrollX or instance.HoverScrollY\n\tend\n\treturn false\nend\n\nfunction Region.Translate(id, x, y)\n\tlocal instance = GetInstance(id)\n\tif instance ~= nil then\n\t\tinstance.Transform:translate(x, y)\n\t\tlocal tx, ty = instance.Transform:inverseTransformPoint(0, 0)\n\n\t\tif not instance.IgnoreScroll then\n\t\t\tif x ~= 0 and instance.HasScrollX then\n\t\t\t\tlocal xSize = instance.W - GetXScrollSize(instance)\n\t\t\t\tlocal ContentW = instance.ContentW - instance.W\n\n\t\t\t\tif instance.HasScrollY then\n\t\t\t\t\txSize = xSize - ScrollPad - ScrollBarSize\n\t\t\t\tend\n\n\t\t\t\txSize = max(xSize, 0)\n\n\t\t\t\tinstance.ScrollPosX = (tx / ContentW) * xSize\n\t\t\t\tinstance.ScrollPosX = max(instance.ScrollPosX, 0)\n\t\t\t\tinstance.ScrollPosX = min(instance.ScrollPosX, xSize)\n\t\t\tend\n\n\t\t\tif y ~= 0 and instance.HasScrollY then\n\t\t\t\tlocal ySize = instance.H - GetYScrollSize(instance)\n\n\t\t\t\tif instance.HasScrollX then\n\t\t\t\t\tySize = ySize - ScrollPad - ScrollBarSize\n\t\t\t\tend\n\n\t\t\t\tySize = max(ySize, 0)\n\n\t\t\t\tlocal ContentH = instance.ContentH - instance.H\n\n\t\t\t\tinstance.ScrollPosY = (ty / ContentH) * ySize\n\t\t\t\tinstance.ScrollPosY = max(instance.ScrollPosY, 0)\n\t\t\t\tinstance.ScrollPosY = min(instance.ScrollPosY, ySize)\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction Region.Transform(id, x, y)\n\tlocal instance = GetInstance(id)\n\tif instance ~= nil then\n\t\treturn instance.Transform:transformPoint(x, y)\n\tend\n\treturn x, y\nend\n\nfunction Region.InverseTransform(id, x, y)\n\tlocal instance = GetInstance(id)\n\tif instance ~= nil then\n\t\treturn instance.Transform:inverseTransformPoint(x, y)\n\tend\n\treturn x, y\nend\n\nfunction Region.ResetTransform(id)\n\tlocal instance = GetInstance(id)\n\tif instance ~= nil then\n\t\tinstance.Transform:reset()\n\t\tinstance.ScrollPosX = 0\n\t\tinstance.ScrollPosY = 0\n\tend\nend\n\nfunction Region.IsActive(id)\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.Id == id\n\tend\n\treturn false\nend\n\nfunction Region.AddItem(x, y, W, H)\n\tif ActiveInstance ~= nil and ActiveInstance.AutoSizeContent then\n\t\tlocal newW = x + W - ActiveInstance.X\n\t\tlocal newH = y + H - ActiveInstance.Y\n\t\tActiveInstance.ContentW = max(ActiveInstance.ContentW, newW)\n\t\tActiveInstance.ContentH = max(ActiveInstance.ContentH, newH)\n\tend\nend\n\nfunction Region.ApplyScissor()\n\tif ActiveInstance ~= nil then\n\t\tif ActiveInstance.Intersect then\n\t\t\tDrawCommands.IntersectScissor(ActiveInstance.SX, ActiveInstance.SY, ActiveInstance.W, ActiveInstance.H)\n\t\telse\n\t\t\tDrawCommands.Scissor(ActiveInstance.SX, ActiveInstance.SY, ActiveInstance.W, ActiveInstance.H)\n\t\tend\n\tend\nend\n\nfunction Region.GetBounds()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.X, ActiveInstance.Y, ActiveInstance.W, ActiveInstance.H\n\tend\n\treturn 0, 0, 0, 0\nend\n\nfunction Region.GetContentSize()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.ContentW, ActiveInstance.ContentH\n\tend\n\treturn 0, 0\nend\n\nfunction Region.GetContentBounds()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.X, ActiveInstance.Y, ActiveInstance.ContentW, ActiveInstance.ContentH\n\tend\n\treturn 0, 0, 0, 0\nend\n\nfunction Region.Contains(x, y)\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.X <= x and x <= ActiveInstance.X + ActiveInstance.W and ActiveInstance.Y <= y and y <= ActiveInstance.Y + ActiveInstance.H\n\tend\n\treturn false\nend\n\nfunction Region.ResetContentSize(id)\n\tlocal instance = GetInstance(id)\n\tif instance ~= nil then\n\t\tinstance.ContentW = 0\n\t\tinstance.ContentH = 0\n\tend\nend\n\nfunction Region.GetScrollPad()\n\treturn ScrollPad\nend\n\nfunction Region.GetScrollBarSize()\n\treturn ScrollBarSize\nend\n\nfunction Region.WheelMoved(x, y)\n\tWheelX = x * WheelSpeed\n\tWheelY = y * WheelSpeed\nend\n\nfunction Region.GetWheelDelta()\n\treturn WheelX, WheelY\nend\n\nfunction Region.IsScrolling(id)\n\tif id then\n\t\tlocal instance = GetInstance(id)\n\t\treturn ScrollInstance == instance or WheelInstance == instance\n\tend\n\tif ScrollInstance then\n\t\treturn ScrollInstance.IsScrollingX or ScrollInstance.IsScrollingY\n\tend\n\tif WheelInstance then\n\t\treturn WheelInstance.IsScrollingX or WheelInstance.IsScrollingY\n\tend\n\treturn false\nend\n\nfunction Region.GetHotInstanceId()\n\treturn HotInstance and HotInstance.Id or ''\nend\n\nfunction Region.ClearHotInstance(id)\n\tif not id or (HotInstance and HotInstance.Id == id) then\n\t\tHotInstance = nil\n\tend\nend\n\nfunction Region.GetInstanceIds()\n\tlocal result = {}\n\n\tfor k, v in pairs(Instances) do\n\t\ttable.insert(result, k)\n\tend\n\n\treturn result\nend\n\nfunction Region.GetDebugInfo(id)\n\tlocal result = {}\n\tlocal instance = Instances[id]\n\n\ttable.insert(result, \"ScrollInstance: \" .. (ScrollInstance ~= nil and ScrollInstance.Id or \"nil\"))\n\ttable.insert(result, \"WheelInstance: \" .. (WheelInstance ~= nil and WheelInstance.Id or \"nil\"))\n\ttable.insert(result, \"WheelX: \" .. WheelX)\n\ttable.insert(result, \"WheelY: \" .. WheelY)\n\ttable.insert(result, \"Wheel Speed: \" .. WheelSpeed)\n\n\tif instance ~= nil then\n\t\ttable.insert(result, \"Id: \" .. instance.Id)\n\t\ttable.insert(result, \"W: \" .. instance.W)\n\t\ttable.insert(result, \"H: \" .. instance.H)\n\t\ttable.insert(result, \"ContentW: \" .. instance.ContentW)\n\t\ttable.insert(result, \"ContentH: \" .. instance.ContentH)\n\t\ttable.insert(result, \"ScrollPosX: \" .. instance.ScrollPosX)\n\t\ttable.insert(result, \"ScrollPosY: \" .. instance.ScrollPosY)\n\n\t\tlocal tx, ty = instance.Transform:transformPoint(0, 0)\n\t\ttable.insert(result, \"TX: \" .. tx)\n\t\ttable.insert(result, \"TY: \" .. ty)\n\t\ttable.insert(result, \"Max TX: \" .. instance.ContentW - instance.W)\n\t\ttable.insert(result, \"Max TY: \" .. instance.ContentH - instance.H)\n\tend\n\n\treturn result\nend\n\nfunction Region.SetWheelSpeed(Speed)\n\tWheelSpeed = Speed or 3\nend\n\nfunction Region.GetWheelSpeed()\n\treturn WheelSpeed\nend\n\nreturn Region\n"
  },
  {
    "path": "Internal/UI/Separator.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal max = math.max\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\n\nlocal Separator = {}\n\nfunction Separator.Begin(Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.IncludeBorders = Options.IncludeBorders == nil and false or Options.IncludeBorders\n\tOptions.H = Options.H == nil and 4.0 or Options.H\n\tOptions.Thickness = Options.Thickness == nil and 1.0 or Options.Thickness\n\n\tlocal W, H = LayoutManager.GetActiveSize()\n\tW, H = LayoutManager.ComputeSize(W, max(Options.H, Options.Thickness))\n\tLayoutManager.AddControl(W, H, 'Separator')\n\tlocal X, Y = Cursor.GetPosition()\n\n\tif Options.IncludeBorders then\n\t\tW = W + Window.GetBorder() * 2\n\t\tX = X - Window.GetBorder()\n\tend\n\n\tDrawCommands.Line(X, Y + H * 0.5, X + W, Y + H * 0.5, Options.Thickness, Style.SeparatorColor)\n\n\tCursor.SetItemBounds(X, Y, W, H)\n\tCursor.AdvanceY(H)\nend\n\nreturn Separator\n"
  },
  {
    "path": "Internal/UI/Shape.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal insert = table.insert\nlocal abs = math.abs\nlocal max = math.max\nlocal min = math.min\nlocal huge = math.huge\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\n\nlocal Shape = {}\nlocal Curve = nil\nlocal CurveX, CurveY = 0, 0\n\nfunction Shape.Rectangle(Options)\n\tlocal StatHandle = Stats.Begin('Rectangle', 'Slab')\n\n\tOptions = Options == nil and {} or Options\n\tOptions.Mode = Options.Mode == nil and 'fill' or Options.Mode\n\tOptions.W = Options.W == nil and 32 or Options.W\n\tOptions.H = Options.H == nil and 32 or Options.H\n\tOptions.Color = Options.Color == nil and nil or Options.Color\n\tOptions.Rounding = Options.Rounding == nil and 2.0 or Options.Rounding\n\tOptions.Outline = Options.Outline == nil and false or Options.Outline\n\tOptions.OutlineColor = Options.OutlineColor == nil and {0.0, 0.0, 0.0, 1.0} or Options.OutlineColor\n\tOptions.Segments = Options.Segments == nil and 10 or Options.Segments\n\n\tlocal W = Options.W\n\tlocal H = Options.H\n\tLayoutManager.AddControl(W, H, 'Rectangle')\n\n\tlocal X, Y = Cursor.GetPosition()\n\n\tif Options.Outline then\n\t\tDrawCommands.Rectangle('line', X, Y, W, H, Options.OutlineColor, Options.Rounding, Options.Segments)\n\tend\n\n\tDrawCommands.Rectangle(Options.Mode, X, Y, W, H, Options.Color, Options.Rounding, Options.Segments)\n\n\tWindow.AddItem(X, Y, W, H)\n\tCursor.SetItemBounds(X, Y, W, H)\n\tCursor.AdvanceY(H)\n\n\tStats.End(StatHandle)\nend\n\nfunction Shape.Circle(Options)\n\tlocal StatHandle = Stats.Begin('Circle', 'Slab')\n\n\tOptions = Options == nil and {} or Options\n\tOptions.Mode = Options.Mode == nil and 'fill' or Options.Mode\n\tOptions.Radius = Options.Radius == nil and 12.0 or Options.Radius\n\tOptions.Color = Options.Color == nil and nil or Options.Color\n\tOptions.Segments = Options.Segments == nil and nil or Options.Segments\n\n\tlocal Diameter = Options.Radius * 2.0\n\n\tLayoutManager.AddControl(Diameter, Diameter, 'Circle')\n\n\tlocal X, Y = Cursor.GetPosition()\n\tlocal CenterX = X + Options.Radius\n\tlocal CenterY = Y + Options.Radius\n\n\tDrawCommands.Circle(Options.Mode, CenterX, CenterY, Options.Radius, Options.Color, Options.Segments)\n\tWindow.AddItem(X, Y, Diameter, Diameter)\n\tCursor.SetItemBounds(X, Y, Diameter, Diameter)\n\tCursor.AdvanceY(Diameter)\n\n\tStats.End(StatHandle)\nend\n\nfunction Shape.Triangle(Options)\n\tlocal StatHandle = Stats.Begin('Triangle', 'Slab')\n\n\tOptions = Options == nil and {} or Options\n\tOptions.Mode = Options.Mode == nil and 'fill' or Options.Mode\n\tOptions.Radius = Options.Radius == nil and 12 or Options.Radius\n\tOptions.Rotation = Options.Rotation == nil and 0 or Options.Rotation\n\tOptions.Color = Options.Color == nil and nil or Options.Color\n\n\tlocal Diameter = Options.Radius * 2.0\n\n\tLayoutManager.AddControl(Diameter, Diameter, 'Triangle')\n\n\tlocal X, Y = Cursor.GetPosition()\n\tlocal CenterX = X + Options.Radius\n\tlocal CenterY = Y + Options.Radius\n\n\tDrawCommands.Triangle(Options.Mode, CenterX, CenterY, Options.Radius, Options.Rotation, Options.Color)\n\tWindow.AddItem(X, Y, Diameter, Diameter)\n\tCursor.SetItemBounds(X, Y, Diameter, Diameter)\n\tCursor.AdvanceY(Diameter)\n\n\tStats.End(StatHandle)\nend\n\nfunction Shape.Line(X2, Y2, Options)\n\tlocal StatHandle = Stats.Begin('Line', 'Slab')\n\n\tOptions = Options == nil and {} or Options\n\tOptions.Width = Options.Width == nil and 1.0 or Options.Width\n\tOptions.Color = Options.Color == nil and nil or Options.Color\n\n\tlocal X, Y = Cursor.GetPosition()\n\tlocal W, H = abs(X2 - X), abs(Y2 - Y)\n\tH = max(H, Options.Width)\n\n\tDrawCommands.Line(X, Y, X2, Y2, Options.Width, Options.Color)\n\tWindow.AddItem(X, Y, W, H)\n\tCursor.SetItemBounds(X, Y, W, H)\n\tCursor.AdvanceY(H)\n\n\tStats.End(StatHandle)\nend\n\nfunction Shape.Curve(Points, Options)\n\tlocal StatHandle = Stats.Begin('Curve', 'Slab')\n\n\tOptions = Options == nil and {} or Options\n\tOptions.Color = Options.Color == nil and nil or Options.Color\n\tOptions.Depth = Options.Depth == nil and nil or Options.Depth\n\n\tCurve = love.math.newBezierCurve(Points)\n\n\tlocal MinX, MinY = huge, huge\n\tlocal MaxX, MaxY = 0, 0\n\tfor I = 1, Curve:getControlPointCount(), 1 do\n\t\tlocal PX, PY = Curve:getControlPoint(I)\n\t\tMinX = min(MinX, PX)\n\t\tMinY = min(MinY, PY)\n\n\t\tMaxX = max(MaxX, PX)\n\t\tMaxY = max(MaxY, PY)\n\tend\n\n\tlocal W = abs(MaxX - MinX)\n\tlocal H = abs(MaxY - MinY)\n\n\tLayoutManager.AddControl(W, H, 'Curve')\n\n\tCurveX, CurveY = Cursor.GetPosition()\n\tCurve:translate(CurveX, CurveY)\n\n\tDrawCommands.Curve(Curve:render(Options.Depth), Options.Color)\n\tWindow.AddItem(MinX, MinY, W, H)\n\tCursor.SetItemBounds(MinX, MinY, W, H)\n\tCursor.AdvanceY(H)\n\n\tStats.End(StatHandle)\nend\n\nfunction Shape.GetCurveControlPointCount()\n\tif Curve ~= nil then\n\t\treturn Curve:getControlPointCount()\n\tend\n\n\treturn 0\nend\n\nfunction Shape.GetCurveControlPoint(Index, Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.LocalSpace = Options.LocalSpace == nil and true or Options.LocalSpace\n\n\tlocal X, Y = 0, 0\n\tif Curve ~= nil then\n\t\tif Options.LocalSpace then\n\t\t\tCurve:translate(-CurveX, -CurveY)\n\t\tend\n\n\t\tX, Y = Curve:getControlPoint(Index)\n\n\t\tif Options.LocalSpace then\n\t\t\tCurve:translate(CurveX, CurveY)\n\t\tend\n\tend\n\n\treturn X, Y\nend\n\nfunction Shape.EvaluateCurve(Time, Options)\n\tOptions = Options == nil and {} or Options\n\tOptions.LocalSpace = Options.LocalSpace == nil and true or Options.LocalSpace\n\n\tlocal X, Y = 0, 0\n\tif Curve ~= nil then\n\t\tif Options.LocalSpace then\n\t\t\tCurve:translate(-CurveX, -CurveY)\n\t\tend\n\n\t\tX, Y = Curve:evaluate(Time)\n\n\t\tif Options.LocalSpace then\n\t\t\tCurve:translate(CurveX, CurveY)\n\t\tend\n\tend\n\n\treturn X, Y\nend\n\nfunction Shape.Polygon(Points, Options)\n\tlocal StatHandle = Stats.Begin('Polygon', 'Slab')\n\n\tOptions = Options == nil and {} or Options\n\tOptions.Color = Options.Color == nil and nil or Options.Color\n\tOptions.Mode = Options.Mode == nil and 'fill' or Options.Mode\n\n\tlocal MinX, MinY = huge, huge\n\tlocal MaxX, MaxY = 0, 0\n\tlocal Verts = {}\n\n\tfor I = 1, #Points, 2 do\n\t\tMinX = min(MinX, Points[I])\n\t\tMinY = min(MinY, Points[I+1])\n\n\t\tMaxX = max(MaxX, Points[I])\n\t\tMaxY = max(MaxY, Points[I+1])\n\tend\n\n\tlocal W = abs(MaxX - MinX)\n\tlocal H = abs(MaxY - MinY)\n\n\tLayoutManager.AddControl(W, H, 'Polygon')\n\n\tMinX, MinY = huge, huge\n\tMaxX, MaxY = 0, 0\n\tlocal X, Y = Cursor.GetPosition()\n\tfor I = 1, #Points, 2 do\n\t\tinsert(Verts, Points[I] + X)\n\t\tinsert(Verts, Points[I+1] + Y)\n\n\t\tMinX = min(MinX, Verts[I])\n\t\tMinY = min(MinY, Verts[I+1])\n\n\t\tMaxX = max(MaxX, Verts[I])\n\t\tMaxY = max(MaxY, Verts[I+1])\n\tend\n\n\tDrawCommands.Polygon(Options.Mode, Verts, Options.Color)\n\tWindow.AddItem(MinX, MinY, W, H)\n\tCursor.SetItemBounds(MinX, MinY, W, H)\n\tCursor.AdvanceY(H)\n\n\tStats.End(StatHandle)\nend\n\nreturn Shape\n"
  },
  {
    "path": "Internal/UI/Text.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal floor = math.floor\nlocal insert = table.insert\nlocal max = math.max\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Region = require(SLAB_PATH .. '.Internal.UI.Region')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\nlocal Scale = require(SLAB_PATH .. \".Internal.Core.Scale\")\n\n\nlocal Text = {}\nlocal EMPTY = {}\n\nfunction Text.Begin(label, options)\n\tlocal statHandle = Stats.Begin('Text', 'Slab')\n\n\toptions = options or EMPTY\n\tlocal color = options.Color or Style.TextColor\n\tlocal pad = options.Pad or 0 -- TODO: rename on next major version?\n\tlocal padH = options.PadH or 0\n\tlocal isSelectableTextOnly = options.IsSelectableTextOnly\n\tlocal isSelectable = options.IsSelectable or isSelectableTextOnly\n\n\tif options.URL ~= nil then\n\t\tisSelectableTextOnly = true\n\t\tcolor = Style.TextURLColor\n\tend\n\n\tlocal w = Text.GetWidth(label)\n\tlocal h = Style.Font:getHeight()\n\n\tLayoutManager.AddControl(w + pad, h + padH, 'Text')\n\n\tlocal result = false\n\tlocal winId = Window.GetItemId(label)\n\tlocal x, y = Cursor.GetPosition()\n\tlocal mouseX, mouseY = Window.GetMousePosition()\n\n\tlocal isObstructed = Window.IsObstructedAtMouse()\n\n\tif not isObstructed and x <= mouseX and mouseX <= x + w and y <= mouseY and mouseY <= y + h then\n\t\tWindow.SetHotItem(winId)\n\tend\n\n\tlocal winX, winY, winW, winH = Region.GetContentBounds()\n\tlocal checkX = isSelectableTextOnly and x or winX\n\t-- The region's width may have been reset prior to the first control being added. Account for this discrepency.\n\tlocal checkW = isSelectableTextOnly and w or max(winW, w)\n\tlocal hovered = not isObstructed and checkX <= mouseX and mouseX <= checkX + checkW + pad and y <= mouseY and mouseY <= y + h + padH\n\n\tif isSelectable or options.IsSelected then\n\t\tif hovered or options.IsSelected then\n\t\t\tDrawCommands.Rectangle('fill', checkX, y, checkW + pad, h + padH, options.HoverColor or Style.TextHoverBgColor)\n\t\tend\n\n\t\tresult = hovered and (options.SelectOnHover or Mouse.IsClicked(1))\n\tend\n\n\tif hovered and options.URL ~= nil then\n\t\tMouse.SetCursor('hand')\n\n\t\tif Mouse.IsClicked(1) then\n\t\t\tlove.system.openURL(options.URL)\n\t\tend\n\tend\n\n\tDrawCommands.Print(label, floor(x + pad * 0.5), floor(y + padH * 0.5), color, Style.Font)\n\n\tif options.URL ~= nil then\n\t\tDrawCommands.Line(x + pad, y + h, x + w, y + h, 1.0, color)\n\tend\n\n\tCursor.SetItemBounds(x, y, w + pad, h + padH)\n\tCursor.AdvanceY(h + padH)\n\n\tif options.AddItem ~= false then\n\t\tWindow.AddItem(x, y, w + pad, h + padH, winId)\n\tend\n\n\tStats.End(statHandle)\n\n\treturn result\nend\n\nfunction Text.BeginFormatted(label, options)\n\tlocal statHandle = Stats.Begin('Textf', 'Slab')\n\n\tlocal winW, winH = Window.GetBorderlessSize()\n\n\toptions = options or EMPTY\n\tlocal w = options.W or winW\n\tlocal h = options.H or 0\n\n\t-- TODO: Hack to ensure right-aligned menu hints don't change menu item click area\n\tlocal rightPad = options.RightPad or 0\n\n\tif Window.IsAutoSize() and options.W == nil then\n\t\tw = Scale.GetScreenWidth()\n\tend\n\n\tlocal width, wrapped = Style.Font:getWrap(label, w)\n\tlocal textHeight = #wrapped * Style.Font:getHeight()\n\tlocal height = max(h, textHeight)\n\tlocal padH = height - textHeight\n\n\tif options.W ~= nil then\n\t\twidth = options.W\n\tend\n\n\tLayoutManager.AddControl(width, height, 'TextFormatted')\n\n\tlocal x, y = Cursor.GetPosition()\n\n\tDrawCommands.Printf(label, floor(x), floor(y + padH * 0.5), width, options.Align or 'left', options.Color or Style.TextColor, Style.Font)\n\n\tCursor.SetItemBounds(floor(x), floor(y), width, height)\n\tCursor.AdvanceY(height)\n\n\tWindow.ResetContentSize()\n\tWindow.AddItem(floor(x), floor(y), width + rightPad, height)\n\n\tStats.End(statHandle)\nend\n\nfunction Text.BeginObject(object, options)\n\tlocal statHandle = Stats.Begin('TextObject', 'Slab')\n\n\tlocal winW, winH = Window.GetBorderlessSize()\n\n\toptions = options or EMPTY\n\toptions.Color = options.Color == nil and Style.TextColor or options.Color\n\n\tlocal w, h = object:getDimensions()\n\n\tLayoutManager.AddControl(w, h, 'TextObject')\n\n\tlocal x, y = Cursor.GetPosition()\n\n\tDrawCommands.Text(object, floor(x), floor(y), options.Color)\n\n\tCursor.SetItemBounds(floor(x), floor(y), w, h)\n\tCursor.AdvanceY(y)\n\n\tWindow.ResetContentSize()\n\tWindow.AddItem(floor(x), floor(y), w, h)\n\n\tStats.End(statHandle)\nend\n\nfunction Text.GetWidth(label)\n\treturn Style.Font:getWidth(label)\nend\n\nfunction Text.GetHeight()\n\treturn Style.Font:getHeight()\nend\n\nfunction Text.GetSize(label)\n\treturn Style.Font:getWidth(label), Style.Font:getHeight()\nend\n\nfunction Text.GetSizeWrap(label, width)\n\tlocal w, lines = Style.Font:getWrap(label, width)\n\treturn w, #lines * Text.GetHeight()\nend\n\nfunction Text.GetLines(label, width)\n\tlocal _, lines = Style.Font:getWrap(label, width)\n\n\tlocal start = 0\n\tfor i, v in ipairs(lines) do\n\t\tlocal line\n\t\tlocal len_v = #v\n\t\tif len_v == 0 then\n\t\t\tline = \"\\n\"\n\t\telse\n\t\t\tlocal offset = start + len_v + 1\n\t\t\tlocal ch = string.sub(label, offset, offset)\n\n\t\t\tif ch == \"\\n\" then\n\t\t\t\tline = lines[i] .. \"\\n\"\n\t\t\tend\n\t\tend\n\n\t\tif line then\n\t\t\tlines[i] = line\n\t\tend\n\n\t\tstart = start + #lines[i]\n\tend\n\n\tif string.sub(label, #label, #label) == '\\n' then\n\t\tinsert(lines, \"\")\n\tend\n\n\tif #lines == 0 then\n\t\tinsert(lines, \"\")\n\tend\n\n\treturn lines\nend\n\nfunction Text.CreateObject()\n\treturn love.graphics.newText(Style.Font)\nend\n\nreturn Text\n"
  },
  {
    "path": "Internal/UI/Tooltip.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal insert = table.insert\nlocal format = string.format\nlocal min = math.min\n\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Text = require(SLAB_PATH .. '.Internal.UI.Text')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\nlocal Utility = require(SLAB_PATH .. '.Internal.Core.Utility')\n\nlocal Tooltip = {}\nlocal LastDisplayTime = 0.0\nlocal AccumDisplayTime = 0.0\nlocal TooltipTime = 0.75\nlocal TooltipExpireTime = 0.025\nlocal Alpha = 0.0\nlocal OffsetY = 0.0\nlocal ResetSize = false\n\nfunction Tooltip.Begin(Tip)\n\tif Tip == nil or Tip == \"\" then\n\t\treturn\n\tend\n\n\tlocal Elapsed = love.timer.getTime() - LastDisplayTime\n\tif Elapsed > TooltipExpireTime then\n\t\tAccumDisplayTime = 0.0\n\t\tAlpha = 0.0\n\t\tResetSize = true\n\tend\n\n\tlocal DeltaTime = love.timer.getDelta()\n\tAccumDisplayTime = AccumDisplayTime + DeltaTime\n\tLastDisplayTime = love.timer.getTime()\n\n\tif AccumDisplayTime > TooltipTime then\n\t\tlocal X, Y = Mouse.Position()\n\t\tAlpha = min(Alpha + DeltaTime * 4.0, 1.0)\n\t\tlocal BgColor = Utility.MakeColor(Style.WindowBackgroundColor)\n\t\tlocal TextColor = Utility.MakeColor(Style.TextColor)\n\t\tBgColor[4] = Alpha\n\t\tTextColor[4] = Alpha\n\n\t\tlocal CursorX, CursorY = Cursor.GetPosition()\n\n\t\tLayoutManager.Begin('Ignore', {Ignore = true})\n\t\tWindow.Begin('tooltip',\n\t\t{\n\t\t\tX = X,\n\t\t\tY = Y - OffsetY,\n\t\t\tW = 0,\n\t\t\tH = 0,\n\t\t\tAutoSizeWindow = true,\n\t\t\tAutoSizeContent = false,\n\t\t\tAllowResize = false,\n\t\t\tAllowFocus = false,\n\t\t\tLayer = 'ContextMenu',\n\t\t\tResetWindowSize = ResetSize,\n\t\t\tCanObstruct = false,\n\t\t\tNoSavedSettings = true\n\t\t})\n\t\tText.BeginFormatted(Tip, {Color = TextColor})\n\t\tOffsetY = Window.GetHeight()\n\t\tWindow.End()\n\t\tLayoutManager.End()\n\t\tCursor.SetPosition(CursorX, CursorY)\n\t\tResetSize = false\n\tend\nend\n\nfunction Tooltip.GetDebugInfo()\n\tlocal Info = {}\n\n\tlocal Elapsed = love.timer.getTime() - LastDisplayTime\n\tinsert(Info, format(\"Time: %.2f\", AccumDisplayTime))\n\tinsert(Info, format(\"Is Visible: %s\", tostring(AccumDisplayTime > TooltipTime and Elapsed <= TooltipExpireTime)))\n\tinsert(Info, format(\"Time to Display: %.2f\", TooltipTime))\n\tinsert(Info, format(\"Expire Time: %f\", TooltipExpireTime))\n\n\treturn Info\nend\n\nreturn Tooltip\n"
  },
  {
    "path": "Internal/UI/Tree.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal max = math.max\nlocal insert = table.insert\nlocal remove = table.remove\n\nlocal Button = require(SLAB_PATH .. '.Internal.UI.Button')\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal Image = require(SLAB_PATH .. '.Internal.UI.Image')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Messages = require(SLAB_PATH .. '.Internal.Core.Messages')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Region = require(SLAB_PATH .. '.Internal.UI.Region')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Text = require(SLAB_PATH .. '.Internal.UI.Text')\nlocal Tooltip = require(SLAB_PATH .. '.Internal.UI.Tooltip')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\nlocal IdCache = require(SLAB_PATH .. '.Internal.Core.IdCache')\n\nlocal Tree = {}\nlocal Instances = setmetatable({}, {__mode = \"k\"})\nlocal Hierarchy = {}\n\nlocal Radius = 4.0\n\nlocal idCache = IdCache()\nlocal EMPTY = {}\nlocal IGNORE = { Ignore = true }\nlocal CENTERY = { CenterY = true }\nlocal TRANSPARENT = { 0, 0, 0, 0 }\n\nlocal function GetInstance(Id)\n\tlocal IdString = tostring(Id)\n\n\tif #Hierarchy > 0 then\n\t\tIdString = idCache:get(Hierarchy[1].Id, IdString)\n\tend\n\n\tif Instances[Id] == nil then\n\t\tlocal Instance = {}\n\t\tInstance.X = 0.0\n\t\tInstance.Y = 0.0\n\t\tInstance.W = 0.0\n\t\tInstance.H = 0.0\n\t\tInstance.IsOpen = false\n\t\tInstance.WasOpen = false\n\t\tInstance.Id = IdString\n\t\tInstance.StatHandle = nil\n\t\tInstance.TreeR = 0\n\t\tInstance.TreeB = 0\n\t\tInstance.NoSavedSettings = false\n\t\tInstances[Id] = Instance\n\tend\n\treturn Instances[Id]\nend\n\nfunction Tree.Begin(Id, Options)\n\tlocal StatHandle = Stats.Begin('Tree', 'Slab')\n\n\tlocal IsTableId = type(Id) == 'table'\n\tlocal IdLabel = tostring(Id)\n\n\tOptions = Options or EMPTY\n\tlocal label = Options.Label or IdLabel\n\tlocal icon = Options.Icon\n\tlocal openWithHighlight = Options.OpenWithHighlight == nil or Options.OpenWithHighlight\n\n\tif Options.IconPath ~= nil then\n\t\tMessages.Broadcast('Tree.Options.IconPath', \"'IconPath' option has been deprecated since v0.8.0. Please use the 'Icon' option.\")\n\tend\n\n\tlocal Instance = nil\n\tlocal WinItemId = Window.GetItemId(IdLabel)\n\n\tif IsTableId then\n\t\tInstance = GetInstance(Id)\n\telse\n\t\tInstance = GetInstance(WinItemId)\n\tend\n\n\tInstance.WasOpen = Instance.IsOpen\n\tInstance.StatHandle = StatHandle\n\tInstance.NoSavedSettings = Options.NoSavedSettings or IsTableId\n\n\tlocal MouseX, MouseY = Mouse.Position()\n\tlocal TMouseX, TMouseY = Region.InverseTransform(nil, MouseX, MouseY)\n\tlocal WinX, WinY = Window.GetPosition()\n\tlocal WinW, WinH = Window.GetBorderlessSize()\n\tlocal IsObstructed = Window.IsObstructedAtMouse() or Region.IsHoverScrollBar()\n\tlocal W = Text.GetWidth(label)\n\tlocal H = max(Style.Font:getHeight(), Instance.H)\n\n\tif not Options.IsLeaf then\n\t\tW = W + H + Radius\n\tend\n\n\t-- Account for icon if one is requested.\n\tW = icon and W + H or W\n\n\tWinX = WinX + Window.GetBorder()\n\tWinY = WinY + Window.GetBorder()\n\n\tif #Hierarchy == 0 then\n\t\tlocal ControlW, ControlH = W, H\n\t\tif Instance.TreeR > 0 and Instance.TreeB > 0 then\n\t\t\tControlW = Instance.TreeR - Instance.X\n\t\t\tControlH = Instance.TreeB - Instance.Y\n\t\tend\n\n\t\tLayoutManager.AddControl(ControlW, ControlH, 'Tree')\n\n\t\tInstance.TreeR = 0\n\t\tInstance.TreeB = 0\n\tend\n\n\tlocal Root = Instance\n\tif #Hierarchy > 0 then\n\t\tRoot = Hierarchy[#Hierarchy]\n\tend\n\n\tlocal X, Y = Cursor.GetPosition()\n\tif Root ~= Instance then\n\t\tX = Root ~= Instance and (Root.X + H * #Hierarchy)\n\t\tCursor.SetX(X)\n\tend\n\tlocal TriX, TriY = X + Radius, Y + H * 0.5\n\n\tlocal IsHot = not IsObstructed and WinX <= TMouseX and TMouseX <= WinX + WinW and Y <= TMouseY and TMouseY <= Y + H and Region.Contains(MouseX, MouseY)\n\n\tif IsHot or Options.IsSelected then\n\t\tDrawCommands.Rectangle('fill', WinX, Y, WinW, H, Style.TextHoverBgColor)\n\tend\n\n\tif IsHot then\n\t\tif Mouse.IsClicked(1) and not Options.IsLeaf and openWithHighlight then\n\t\t\tInstance.IsOpen = not Instance.IsOpen\n\t\tend\n\tend\n\n\tlocal IsExpanderClicked = false\n\tlocal ExpandIconOptions = Instance.ExpandIconOptions\n\n\tif not Options.IsLeaf then\n\t\t-- Render the triangle depending on if the tree item is open/closed.\n\t\tlocal SubX = Instance.IsOpen and 0 or 200\n\t\tlocal SubY = Instance.IsOpen and 100 or 50\n\n\t\tif not ExpandIconOptions then\n\t\t\tExpandIconOptions = {\n\t\t\t\tImage = { Path = SLAB_FILE_PATH .. '/Internal/Resources/Textures/Icons.png', SubW = 50, SubH = 50 },\n\t\t\t\tPadX = 0,\n\t\t\t\tPadY = 0,\n\t\t\t\tColor = TRANSPARENT,\n\t\t\t\tHoverColor = TRANSPARENT,\n\t\t\t\tPressColor = TRANSPARENT,\n\t\t\t\tIconId = Instance.Id .. '_Open',\n\t\t\t\tExpandId = Instance.Id .. '_Expand',\n\t\t\t}\n\t\t\tInstance.ExpandIconOptions = ExpandIconOptions\n\t\tend\n\n\t\tExpandIconOptions.Image.SubX = SubX\n\t\tExpandIconOptions.Image.SubY = SubY\n\t\tExpandIconOptions.W = H\n\t\tExpandIconOptions.H = H\n\n\t\tif Button.Begin(ExpandIconOptions.ExpandId, ExpandIconOptions) and not openWithHighlight then\n\t\t\tInstance.IsOpen = not Instance.IsOpen\n\t\t\tWindow.SetHotItem(nil)\n\t\t\tIsExpanderClicked = true\n\t\tend\n\n\t\tCursor.SameLine()\n\telse\n\t\t-- Advance the cursor for leaf nodes so text aligns with other items that are not leaf nodes.\n\t\tCursor.AdvanceX(H + Cursor.PadX())\n\tend\n\n\tif not Instance.IsOpen and Instance.WasOpen then\n\t\tWindow.ResetContentSize()\n\t\tRegion.ResetContentSize()\n\tend\n\n\tInstance.X = X\n\tInstance.Y = Y\n\tInstance.W = W\n\tInstance.H = H\n\n\tLayoutManager.Begin('Ignore', IGNORE)\n\n\tif icon ~= nil then\n\t\t-- Force the icon to be of the same size as the tree item.\n\t\ticon.W = H\n\t\ticon.H = H\n\t\tImage.Begin(ExpandIconOptions.IconId, icon)\n\n\t\tlocal ItemX, ItemY, ItemW, ItemH = Cursor.GetItemBounds()\n\t\tInstance.H = max(Instance.H, ItemH)\n\t\tCursor.SameLine(CENTERY)\n\tend\n\n\tText.Begin(label)\n\n\tLayoutManager.End()\n\n\tlocal ItemX, ItemY, ItemW, ItemH = Cursor.GetItemBounds()\n\tRoot.TreeR = max(Root.TreeR, ItemX + ItemW)\n\tRoot.TreeB = max(Root.TreeB, Y + H)\n\n\tCursor.SetY(Instance.Y)\n\tCursor.AdvanceY(H)\n\n\tif Instance.IsOpen then\n\t\tinsert(Hierarchy, 1, Instance)\n\tend\n\n\tif IsHot then\n\t\tTooltip.Begin(Options.Tooltip or \"\")\n\n\t\tif not IsExpanderClicked then\n\t\t\tWindow.SetHotItem(WinItemId)\n\t\tend\n\tend\n\n\t-- The size of the item has already been determined by Text.Begin. However, this item's ID needs to be\n\t-- set as the last item for hot item checks. So the item will be added with zero width and height.\n\tWindow.AddItem(X, Y, 0, 0, WinItemId)\n\n\tif not Instance.IsOpen then\n\t\tStats.End(Instance.StatHandle)\n\tend\n\n\treturn Instance.IsOpen\nend\n\nfunction Tree.End()\n\tlocal StatHandle = Hierarchy[1].StatHandle\n\tremove(Hierarchy, 1)\n\tStats.End(StatHandle)\nend\n\nfunction Tree.Save(Table)\n\tif Table ~= nil then\n\t\tlocal Settings = {}\n\t\tfor K, V in ipairs(Instances) do\n\t\t\tif not V.NoSavedSettings then\n\t\t\t\tSettings[V.Id] = {\n\t\t\t\t\tIsOpen = V.IsOpen\n\t\t\t\t}\n\t\t\tend\n\t\tend\n\t\tTable['Tree'] = Settings\n\tend\nend\n\nfunction Tree.Load(Table)\n\tif Table ~= nil then\n\t\tlocal Settings = Table['Tree']\n\t\tif Settings ~= nil then\n\t\t\tfor K, V in pairs(Settings) do\n\t\t\t\tlocal Instance = GetInstance(K)\n\t\t\t\tInstance.IsOpen = V.IsOpen\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction Tree.GetDebugInfo()\n\tlocal Result = {}\n\n\tfor K, V in pairs(Instances) do\n\t\ttable.insert(Result, tostring(K))\n\tend\n\n\treturn Result\nend\n\nreturn Tree\n"
  },
  {
    "path": "Internal/UI/Window.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal insert = table.insert\nlocal remove = table.remove\nlocal max = math.max\nlocal floor = math.floor\n\nlocal Cursor = require(SLAB_PATH .. \".Internal.Core.Cursor\")\nlocal Dock = require(SLAB_PATH .. '.Internal.UI.Dock')\nlocal DrawCommands = require(SLAB_PATH .. \".Internal.Core.DrawCommands\")\nlocal MenuState = require(SLAB_PATH .. \".Internal.UI.MenuState\")\nlocal Mouse = require(SLAB_PATH .. \".Internal.Input.Mouse\")\nlocal Region = require(SLAB_PATH .. \".Internal.UI.Region\")\nlocal Stats = require(SLAB_PATH .. \".Internal.Core.Stats\")\nlocal Style = require(SLAB_PATH .. \".Style\")\nlocal Utility = require(SLAB_PATH .. \".Internal.Core.Utility\")\nlocal IdCache = require(SLAB_PATH .. \".Internal.Core.IdCache\")\nlocal Scale = require(SLAB_PATH .. \".Internal.Core.Scale\")\n\n\nlocal Window = {}\n\nlocal Instances = {}\nlocal Stack = {}\nlocal StackLockId = nil\nlocal PendingStack = {}\nlocal ActiveInstance = nil\nlocal MovingInstance = nil\nlocal IDStack = {}\nlocal idCache = IdCache()\nlocal MenuBarInstance\n\nlocal SizerNone = 0\nlocal SizerN = 1\nlocal SizerE = 2\nlocal SizerS = 3\nlocal SizerW = 4\nlocal SizerNE = 5\nlocal SizerSE = 6\nlocal SizerSW = 7\nlocal SizerNW = 8\n\nlocal SizerCursor =\n{\n\t[SizerNW] = 'sizenwse',\n\t[SizerNE] = 'sizenesw',\n\t[SizerSE] = 'sizenwse',\n\t[SizerSW] = 'sizenesw',\n\t[SizerW]  = 'sizewe',\n\t[SizerE]  = 'sizewe',\n\t[SizerN]  = 'sizens',\n\t[SizerS]  = 'sizens',\n}\n\nlocal EMPTY = {}\n\nlocal TitleRounding = { 0, 0, 0, 0 }\nlocal BodyRounding = { 0, 0, 0, 0 }\n\nlocal function UpdateStackIndex()\n\tfor i = 1, #Stack do\n\t\tStack[i].StackIndex = #Stack - i + 1\n\tend\nend\n\nlocal function PushToTop(instance)\n\tfor i, v in ipairs(Stack) do\n\t\tif instance == v then\n\t\t\tremove(Stack, i)\n\t\t\tbreak\n\t\tend\n\tend\n\n\tinsert(Stack, 1, instance)\n\n\tUpdateStackIndex()\nend\n\nlocal function NewInstance(id)\n\tlocal instance = {\n\t\tId = id,\n\t\tX = 0,\n\t\tY = 0,\n\t\tW = 200,\n\t\tH = 200,\n\t\tContentW = 0,\n\t\tContentH = 0,\n\t\tTitle = \"\",\n\t\tIsMoving = false,\n\t\tTitleDeltaX = 0,\n\t\tTitleDeltaY = 0,\n\t\tAllowResize = true,\n\t\tAllowFocus = true,\n\t\tSizerType = SizerNone,\n\t\tSizerFilter = nil,\n\t\tSizeDeltaX = 0,\n\t\tSizeDeltaY = 0,\n\t\tHasResized = false,\n\t\tDeltaContentW = 0,\n\t\tDeltaContentH = 0,\n\t\tBackgroundColor = Style.WindowBackgroundColor,\n\t\tBorder = 4,\n\t\tChildren = {},\n\t\tLastItem = nil,\n\t\tHotItem = nil,\n\t\tContextHotItem = nil,\n\t\tItems = {},\n\t\tLayer = 'Normal',\n\t\tStackIndex = 0,\n\t\tCanObstruct = true,\n\t\tFrameNumber = 0,\n\t\tLastCursorX = 0,\n\t\tLastCursorY = 0,\n\t\tStatHandle = nil,\n\t\tIsAppearing = false,\n\t\tIsOpen = true,\n\t\tIsContentOpen = true,\n\t\tIsMinimized = false,\n\t\tNoSavedSettings = false,\n\t\tTitleId = id .. '_Title',\n\t\tTitleRegion = {\n\t\t\tNoBackground = true,\n\t\t\tNoOutline = true,\n\t\t\tIgnoreScroll = true,\n\t\t},\n\t\tInstanceRegion = {},\n\t\tShowScrollbarX = false,\n\t\tShowScrollbarY = false,\n\t}\n\treturn instance\nend\n\nlocal function GetInstance(id)\n\tif id == nil then\n\t\treturn ActiveInstance\n\tend\n\n\tfor i, v in ipairs(Instances) do\n\t\tif v.Id == id then\n\t\t\treturn v\n\t\tend\n\tend\n\tlocal instance = NewInstance(id)\n\tinsert(Instances, instance)\n\treturn instance\nend\n\nlocal function Contains(instance, x, y)\n\tif instance ~= nil then\n\t\tlocal titleH = instance.TitleH or 0\n\t\treturn instance.X <= x and x <= instance.X + instance.W and instance.Y - titleH <= y and y <= instance.Y + instance.H\n\tend\n\treturn false\nend\n\nlocal function UpdateTitleBar(instance, isObstructed, allowMove, constrain)\n\tif isObstructed and (instance.IsContentOpen == nil or instance.IsContentOpen) then\n\t\treturn\n\tend\n\n\tif instance ~= nil and instance.Title ~= \"\" and instance.SizerType == SizerNone then\n\t\tlocal w = instance.W\n\t\tlocal h = instance.TitleH\n\t\tlocal x = instance.X\n\t\tlocal y = instance.Y - h\n\t\tlocal isTethered = Dock.IsTethered(instance.Id)\n\n\t\tlocal mouseX, mouseY = Mouse.Position()\n\n\t\tif Mouse.IsClicked(1) then\n\t\t\tif x <= mouseX and mouseX <= x + w and y <= mouseY and mouseY <= y + h then\n\t\t\t\tif allowMove then\n\t\t\t\t\tinstance.IsMoving = true\n\t\t\t\tend\n\n\t\t\t\tif isTethered then\n\t\t\t\t\tDock.BeginTear(instance.Id, mouseX, mouseY)\n\t\t\t\tend\n\n\t\t\t\tif instance.AllowFocus then\n\t\t\t\t\tPushToTop(instance)\n\t\t\t\tend\n\t\t\tend\n\t\telseif Mouse.IsReleased(1) then\n\t\t\tinstance.IsMoving = false\n\n\t\t\t-- Prevent window going behind MenuBar\n\t\t\tif MenuBarInstance and Contains(MenuBarInstance, mouseX, mouseY) then\n\t\t\t\tinstance.TitleDeltaY = MenuBarInstance.H\n\t\t\tend\n\t\tend\n\n\t\tif instance.IsMoving then\n\t\t\tlocal deltaX, deltaY = Mouse.GetDelta()\n\t\t\tlocal titleDeltaX, titleDeltaY = instance.TitleDeltaX, instance.TitleDeltaY\n\t\t\tinstance.TitleDeltaX = instance.TitleDeltaX + deltaX\n\t\t\tinstance.TitleDeltaY = instance.TitleDeltaY + deltaY\n\n\t\t\tif constrain then\n\t\t\t\t-- Constrain the position of the window to the viewport. The position at this point in the code has the delta already applied. This delta will need to be\n\t\t\t\t-- removed to retrieve the original position, and clamp the delta based off of that posiiton.\n\t\t\t\tlocal originX = instance.X - titleDeltaX\n\t\t\t\tlocal originY = instance.Y - titleDeltaY - instance.TitleH\n\t\t\t\tinstance.TitleDeltaX = Utility.Clamp(instance.TitleDeltaX, -originX, Scale.GetScreenWidth() - (originX + instance.W))\n\t\t\t\tinstance.TitleDeltaY = Utility.Clamp(instance.TitleDeltaY, -originY + MenuState.MainMenuBarH, Scale.GetScreenHeight() - (originY + instance.H + instance.TitleH))\n\t\t\tend\n\t\telseif isTethered then\n\t\t\tDock.UpdateTear(instance.Id, mouseX, mouseY)\n\n\t\t\t-- Retrieve the cached options to calculate torn off position. The cached options contain the\n\t\t\t-- desired bounds for this window. The bounds that are a part of the Instance are the altered options\n\t\t\t-- modified by the Dock module.\n\t\t\tlocal options = Dock.GetCachedOptions(instance.Id)\n\t\t\tif not Dock.IsTethered(instance.Id) then\n\t\t\t\tinstance.IsMoving = true\n\n\t\t\t\tif options ~= nil then\n\t\t\t\t\t-- Properly place the window at the mouse position offset by the title width/height.\n\t\t\t\t\tinstance.TitleDeltaX = mouseX - x - floor(w * 0.25)\n\t\t\t\t\tinstance.TitleDeltaY = mouseY - y - floor(h * 0.5)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function IsSizerEnabled(instance, sizer)\n\tif not instance then\n\t\treturn false\n\tend\n\n\tif #instance.SizerFilter == 0 then\n\t\treturn true\n\tend\n\n\tfor i, v in ipairs(instance.SizerFilter) do\n\t\tif v == sizer then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal function UpdateSize(instance, isObstructed)\n\tif not instance or not instance.AllowResize then\n\t\treturn\n\tend\n\n\tif Region.IsHoverScrollBar(instance.Id) then\n\t\treturn\n\tend\n\n\tif instance.SizerType == SizerNone and isObstructed then\n\t\treturn\n\tend\n\n\tif MovingInstance ~= nil then\n\t\treturn\n\tend\n\n\tlocal x, y, w, h = instance.X, instance.Y, instance.W, instance.H\n\n\tif instance.Title ~= \"\" then\n\t\ty = y - instance.TitleH\n\t\th = h + instance.TitleH\n\tend\n\n\tlocal mouseX, mouseY = Mouse.Position()\n\tlocal newSizerType = SizerNone\n\tlocal scrollPad = Region.GetScrollPad()\n\n\tif x <= mouseX and mouseX <= x + w and y <= mouseY and mouseY <= y + h then\n\t\tif x <= mouseX and mouseX <= x + scrollPad and y <= mouseY and mouseY <= y + scrollPad and IsSizerEnabled(instance, \"NW\") then\n\t\t\tnewSizerType = SizerNW\n\t\telseif x + w - scrollPad <= mouseX and mouseX <= x + w and y <= mouseY and mouseY <= y + scrollPad and IsSizerEnabled(instance, \"NE\") then\n\t\t\tnewSizerType = SizerNE\n\t\telseif x + w - scrollPad <= mouseX and mouseX <= x + w and y + h - scrollPad <= mouseY and mouseY <= y + h and IsSizerEnabled(instance, \"SE\") then\n\t\t\tnewSizerType = SizerSE\n\t\telseif x <= mouseX and mouseX <= x + scrollPad and y + h - scrollPad <= mouseY and mouseY <= y + h and IsSizerEnabled(instance, \"SW\") then\n\t\t\tnewSizerType = SizerSW\n\t\telseif x <= mouseX and mouseX <= x + scrollPad and IsSizerEnabled(instance, \"W\") then\n\t\t\tnewSizerType = SizerW\n\t\telseif x + w - scrollPad <= mouseX and mouseX <= x + w and IsSizerEnabled(instance, \"E\") then\n\t\t\tnewSizerType = SizerE\n\t\telseif y <= mouseY and mouseY <= y + scrollPad and IsSizerEnabled(instance, \"N\") then\n\t\t\tnewSizerType = SizerN\n\t\telseif y + h - scrollPad <= mouseY and mouseY <= y + h and IsSizerEnabled(instance, \"S\") then\n\t\t\tnewSizerType = SizerS\n\t\tend\n\n\t\tif SizerCursor[newSizerType] then\n\t\t\tMouse.SetCursor(SizerCursor[newSizerType])\n\t\tend\n\tend\n\n\tif Mouse.IsClicked(1) then\n\t\tinstance.SizerType = newSizerType\n\telseif Mouse.IsReleased(1) then\n\t\tinstance.SizerType = SizerNone\n\tend\n\n\tif instance.SizerType ~= SizerNone then\n\t\tlocal deltaX, deltaY = Mouse.GetDelta()\n\n\t\tif instance.W <= instance.Border then\n\t\t\tif (instance.SizerType == SizerW or\n\t\t\t\tinstance.SizerType == SizerNW or\n\t\t\t\tinstance.SizerType == SizerSW) and\n\t\t\t\tdeltaX > 0 then\n\t\t\t\tdeltaX = 0\n\t\t\tend\n\n\t\t\tif (instance.SizerType == SizerE or\n\t\t\t\tinstance.SizerType == SizerNE or\n\t\t\t\tinstance.SizerType == SizerSE) and\n\t\t\t\tdeltaX < 0 then\n\t\t\t\tdeltaX = 0\n\t\t\tend\n\t\tend\n\n\t\tif instance.H <= instance.Border then\n\t\t\tif (instance.SizerType == SizerN or\n\t\t\t\tinstance.SizerType == SizerNW or\n\t\t\t\tinstance.SizerType == SizerNE) and\n\t\t\t\tdeltaY > 0 then\n\t\t\t\tdeltaY = 0\n\t\t\tend\n\n\t\t\tif (instance.SizerType == SizerS or\n\t\t\t\tinstance.SizerType == SizerSE or\n\t\t\t\tinstance.SizerType == SizerSW) and\n\t\t\t\tdeltaY < 0 then\n\t\t\t\tdeltaY = 0\n\t\t\tend\n\t\tend\n\n\t\tif deltaX ~= 0 or deltaY ~= 0 then\n\t\t\tinstance.HasResized = true\n\t\t\tinstance.DeltaContentW = 0\n\t\t\tinstance.DeltaContentH = 0\n\t\tend\n\n\t\tif instance.SizerType == SizerN then\n\t\t\tinstance.TitleDeltaY = instance.TitleDeltaY + deltaY\n\t\t\tinstance.SizeDeltaY = instance.SizeDeltaY - deltaY\n\t\telseif instance.SizerType == SizerE then\n\t\t\tinstance.SizeDeltaX = instance.SizeDeltaX + deltaX\n\t\telseif instance.SizerType == SizerS then\n\t\t\tinstance.SizeDeltaY = instance.SizeDeltaY + deltaY\n\t\telseif instance.SizerType == SizerW then\n\t\t\tinstance.TitleDeltaX = instance.TitleDeltaX + deltaX\n\t\t\tinstance.SizeDeltaX = instance.SizeDeltaX - deltaX\n\t\telseif instance.SizerType == SizerNW then\n\t\t\tinstance.TitleDeltaX = instance.TitleDeltaX + deltaX\n\t\t\tinstance.SizeDeltaX = instance.SizeDeltaX - deltaX\n\t\t\tinstance.TitleDeltaY = instance.TitleDeltaY + deltaY\n\t\t\tinstance.SizeDeltaY = instance.SizeDeltaY - deltaY\n\t\telseif instance.SizerType == SizerNE then\n\t\t\tinstance.SizeDeltaX = instance.SizeDeltaX + deltaX\n\t\t\tinstance.TitleDeltaY = instance.TitleDeltaY + deltaY\n\t\t\tinstance.SizeDeltaY = instance.SizeDeltaY - deltaY\n\t\telseif instance.SizerType == SizerSE then\n\t\t\tinstance.SizeDeltaX = instance.SizeDeltaX + deltaX\n\t\t\tinstance.SizeDeltaY = instance.SizeDeltaY + deltaY\n\t\telseif instance.SizerType == SizerSW then\n\t\t\tinstance.TitleDeltaX = instance.TitleDeltaX + deltaX\n\t\t\tinstance.SizeDeltaX = instance.SizeDeltaX - deltaX\n\t\t\tinstance.SizeDeltaY = instance.SizeDeltaY + deltaY\n\t\tend\n\n\t\tif SizerCursor[instance.SizerType] then\n\t\t\tMouse.SetCursor(SizerCursor[instance.SizerType])\n\t\tend\n\tend\nend\n\nlocal function DrawButton(type, activeInstance, options, radius, offsetX, offsetY, hoverColor, color)\n\tlocal isClicked = false\n\tlocal mouseX, mouseY = Mouse.Position()\n\tlocal isObstructed = false\n\tif type == \"Close\" then\n\t\tisObstructed = Window.IsObstructed(mouseX, mouseY, true)\n\tend\n\tlocal size = radius * 0.5\n\tlocal x = activeInstance.X + activeInstance.W - radius * offsetX\n\tlocal y = activeInstance.Y - offsetY * 0.5\n\tlocal isHovered =\n\t\tx - radius <= mouseX and mouseX <= x + radius and\n\t\ty - offsetY * 0.5 <= mouseY and mouseY <= y + radius and\n\t\tnot isObstructed\n\n\tif isHovered then\n\t\tDrawCommands.Circle('fill', x, y, radius, hoverColor)\n\n\t\tif Mouse.IsClicked(1) then\n\t\t\tisClicked = true\n\t\tend\n\tend\n\n\tif type == \"Close\" then\n\t\tDrawCommands.Cross(x, y, size, color)\n\telseif type == \"Minimize\" then\n\t\tif activeInstance.IsMinimized then\n\t\t\tDrawCommands.Rectangle(\"line\", x - size, y - size, size * 2, size * 2, color)\n\t\telse\n\t\t\tDrawCommands.Line(x - size, y, x + size, y, 2, color)\n\t\tend\n\tend\n\n\treturn isClicked\nend\n\nfunction Window.Top()\n\treturn ActiveInstance\nend\n\nfunction Window.IsObstructed(x, y, skipScrollCheck)\n\tif Region.IsScrolling() then\n\t\treturn true\n\tend\n\n\t-- If there are no windows, then nothing can obstruct.\n\tif #Stack == 0 then\n\t\treturn false\n\tend\n\n\tif ActiveInstance ~= nil then\n\t\tif not ActiveInstance.IsOpen then\n\t\t\treturn true\n\t\tend\n\n\t\tif ActiveInstance.IsContentOpen == false then\n\t\t\treturn true\n\t\tend\n\n\t\tif ActiveInstance.IsMoving then\n\t\t\treturn false\n\t\tend\n\n\t\tif ActiveInstance.IsAppearing then\n\t\t\treturn true\n\t\tend\n\n\t\t-- Gather all potential windows that can obstruct the given position.\n\t\tlocal list = {}\n\t\tfor i, v in ipairs(Stack) do\n\t\t\t-- Stack locks prevents other windows to be considered.\n\t\t\tif v.Id == StackLockId then\n\t\t\t\tinsert(list, v)\n\t\t\t\tbreak\n\t\t\tend\n\n\t\t\tif Contains(v, x, y) and v.CanObstruct then\n\t\t\t\tinsert(list, v)\n\t\t\tend\n\t\tend\n\n\t\t-- Certain layers are rendered on top of 'Normal' windows. Consider these windows first.\n\t\tlocal top = nil\n\t\tfor i, v in ipairs(list) do\n\t\t\tif v.Layer ~= 'Normal' then\n\t\t\t\ttop = v\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\t-- If all windows are considered the normal layer, then just grab the window at the top of the stack.\n\t\tif top == nil then\n\t\t\ttop = list[1]\n\t\tend\n\n\t\tif top ~= nil then\n\t\t\tif ActiveInstance == top then\n\t\t\t\tif not skipScrollCheck and Region.IsHoverScrollBar(ActiveInstance.Id) then\n\t\t\t\t\treturn true\n\t\t\t\tend\n\n\t\t\t\treturn false\n\t\t\telseif top.IsOpen then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction Window.IsObstructedAtMouse()\n\tlocal x, y = Mouse.Position()\n\treturn Window.IsObstructed(x, y)\nend\n\nfunction Window.Reset()\n\tfor i = 1, #PendingStack do\n\t\tPendingStack[i] = nil\n\tend\n\n\tActiveInstance = GetInstance('Global')\n\tActiveInstance.W, ActiveInstance.H = Scale.GetScreenDimensions()\n\tActiveInstance.Border = 0\n\tActiveInstance.NoSavedSettings = true\n\tinsert(PendingStack, 1, ActiveInstance)\n\tMenuBarInstance = nil\nend\n\nfunction Window.Begin(id, options)\n\tlocal statHandle = Stats.Begin('Window', 'Slab')\n\n\toptions = options or EMPTY\n\n\tif not Mouse.IsDragging(1) then\n\t\tDock.AlterOptions(id, options)\n\tend\n\n\tlocal x = options.X or 50\n\tlocal y = options.Y or 50\n\tlocal w = options.W or 200\n\tlocal h = options.H or 200\n\tlocal contentW = options.ContentW or 0\n\tlocal contentH = options.ContentH or 0\n\tlocal bgColor = options.BgColor or Style.WindowBackgroundColor\n\tlocal title = options.Title or \"\"\n\tlocal titleAlignX = options.TitleAlignX or 'center'\n\tlocal titleAlignY = options.TitleAlignY or 'center'\n\tlocal titleH = options.TitleH == nil and ((title ~= nil and title ~= \"\") and max(Style.WindowTitleH, Style.Font:getHeight()) or 0) or options.TitleH\n\tlocal allowMove = options.AllowMove == nil or options.AllowMove\n\tlocal allowResize = options.AllowResize == nil or options.AllowResize\n\tlocal allowFocus = options.AllowFocus == nil or options.AllowFocus\n\tlocal border = options.Border or Style.WindowBorder\n\tlocal autoSizeWindow = options.AutoSizeWindow == nil or options.AutoSizeWindow\n\tlocal autoSizeWindowW = options.AutoSizeWindowW or autoSizeWindow\n\tlocal autoSizeWindowH = options.AutoSizeWindowH or autoSizeWindow\n\tlocal autoSizeContent = options.AutoSizeContent == nil or options.AutoSizeContent\n\tlocal layer = options.Layer or 'Normal'\n\tlocal resetSize = options.ResetSize or autoSizeWindow\n\tlocal resetContent = options.ResetContent or autoSizeContent\n\tlocal sizerFilter = options.SizerFilter or EMPTY\n\tlocal canObstruct = options.CanObstruct == nil or options.CanObstruct\n\tlocal rounding = options.Rounding or Style.WindowRounding\n\tlocal showMinimize = options.ShowMinimize == nil or options.ShowMinimize\n\tlocal showScrollbarX = not not options.ShowScrollbarX\n\tlocal showScrollbarY = not not options.ShowScrollbarY\n\n\tTitleRounding[1], TitleRounding[2] = rounding, rounding\n\tBodyRounding[3], BodyRounding[4] = rounding, rounding\n\n\tlocal titleRounding, bodyRounding = TitleRounding, BodyRounding\n\n\tif type(rounding) == 'table' then\n\t\ttitleRounding = rounding\n\t\tbodyRounding = rounding\n\telseif title == \"\" then\n\t\tbodyRounding = rounding\n\tend\n\n\tlocal instance = GetInstance(id)\n\tinsert(PendingStack, 1, instance)\n\n\tif options.IsMenuBar then\n\t\tMenuBarInstance = instance\n\tend\n\n\tif ActiveInstance ~= nil then\n\t\tActiveInstance.Children[id] = instance\n\tend\n\n\tActiveInstance = instance\n\tif autoSizeWindowW then\n\t\tw = 0\n\tend\n\n\tif autoSizeWindowH then\n\t\th = 0\n\tend\n\n\tif options.ResetPosition or options.ResetLayout then\n\t\tActiveInstance.TitleDeltaX = 0\n\t\tActiveInstance.TitleDeltaY = 0\n\tend\n\n\tif ActiveInstance.AutoSizeWindow ~= autoSizeWindow and autoSizeWindow then\n\t\tresetSize = true\n\tend\n\n\tif ActiveInstance.Border ~= border then\n\t\tresetSize = true\n\tend\n\n\tActiveInstance.X = ActiveInstance.TitleDeltaX + x\n\tActiveInstance.Y = ActiveInstance.TitleDeltaY + y\n\tActiveInstance.W = max(ActiveInstance.SizeDeltaX + w + border, border)\n\tActiveInstance.H = max(ActiveInstance.SizeDeltaY + h + border, border)\n\tActiveInstance.ContentW = contentW\n\tActiveInstance.ContentH = contentH\n\tActiveInstance.BackgroundColor = bgColor\n\tActiveInstance.Title = title\n\tActiveInstance.TitleH = titleH\n\tActiveInstance.AllowResize = allowResize and not autoSizeWindow\n\tActiveInstance.AllowFocus = allowFocus\n\tActiveInstance.Border = border\n\tActiveInstance.IsMenuBar = options.IsMenuBar\n\tActiveInstance.AutoSizeWindow = autoSizeWindow\n\tActiveInstance.AutoSizeWindowW = autoSizeWindowW\n\tActiveInstance.AutoSizeWindowH = autoSizeWindowH\n\tActiveInstance.AutoSizeContent = autoSizeContent\n\tActiveInstance.Layer = layer\n\tActiveInstance.HotItem = nil\n\tActiveInstance.SizerFilter = sizerFilter\n\tActiveInstance.HasResized = false\n\tActiveInstance.CanObstruct = canObstruct\n\tActiveInstance.StatHandle = statHandle\n\tActiveInstance.NoSavedSettings = options.NoSavedSettings\n\tActiveInstance.ShowMinimize = showMinimize\n\tActiveInstance.ShowScrollbarX = showScrollbarX\n\tActiveInstance.ShowScrollbarY = showScrollbarY\n\n\tlocal showClose = false\n\tif options.IsOpen ~= nil and type(options.IsOpen) == 'boolean' then\n\t\tActiveInstance.IsOpen = options.IsOpen\n\t\tshowClose = true\n\tend\n\n\tif options.IsContentOpen ~= nil and type(options.IsContentOpen) == \"boolean\" then\n\t\tActiveInstance.IsContentOpen = options.IsContentOpen\n\tend\n\n\tif ActiveInstance.IsOpen then\n\t\tlocal currentFrameNumber = Stats.GetFrameNumber()\n\t\tActiveInstance.IsAppearing = currentFrameNumber - ActiveInstance.FrameNumber > 1\n\t\tActiveInstance.FrameNumber = currentFrameNumber\n\n\t\tif ActiveInstance.StackIndex == 0 then\n\t\t\tinsert(Stack, 1, ActiveInstance)\n\t\t\tUpdateStackIndex()\n\t\tend\n\tend\n\n\tif ActiveInstance.AutoSizeContent then\n\t\tActiveInstance.ContentW = max(contentW, ActiveInstance.DeltaContentW)\n\t\tActiveInstance.ContentH = max(contentH, ActiveInstance.DeltaContentH)\n\tend\n\n\tlocal offsetY = ActiveInstance.TitleH\n\tif ActiveInstance.Title ~= \"\" then\n\t\tActiveInstance.Y = ActiveInstance.Y + offsetY\n\n\t\tif autoSizeWindow then\n\t\t\tlocal titleW = Style.Font:getWidth(ActiveInstance.Title) + ActiveInstance.Border * 2\n\t\t\tActiveInstance.W = max(ActiveInstance.W, titleW)\n\t\tend\n\tend\n\n\tlocal mouseX, mouseY = Mouse.Position()\n\tlocal isObstructed = Window.IsObstructed(mouseX, mouseY, true)\n\tif (ActiveInstance.AllowFocus and Mouse.IsClicked(1) and not isObstructed and Contains(ActiveInstance, mouseX, mouseY)) or\n\t\tActiveInstance.IsAppearing then\n\t\tPushToTop(ActiveInstance)\n\tend\n\n\tinstance.LastCursorX, instance.LastCursorY = Cursor.GetPosition()\n\tCursor.SetPosition(ActiveInstance.X + ActiveInstance.Border, ActiveInstance.Y + ActiveInstance.Border)\n\tCursor.SetAnchor(ActiveInstance.X + ActiveInstance.Border, ActiveInstance.Y + ActiveInstance.Border)\n\n\tUpdateSize(ActiveInstance, isObstructed)\n\tUpdateTitleBar(ActiveInstance, isObstructed, allowMove, options.ConstrainPosition)\n\n\tDrawCommands.SetLayer(ActiveInstance.Layer)\n\n\tDrawCommands.Begin(ActiveInstance.StackIndex)\n\tif ActiveInstance.Title ~= \"\" then\n\t\tlocal closeBgRadius = offsetY * 0.4\n\t\tlocal minimizeBgRadius = offsetY * 0.4\n\t\tlocal titleX = floor(ActiveInstance.X + (ActiveInstance.W * 0.5) - (Style.Font:getWidth(ActiveInstance.Title) * 0.5))\n\t\tlocal titleY = floor(ActiveInstance.Y - offsetY * 0.5 - Style.Font:getHeight() * 0.5)\n\n\t\t-- Check for horizontal alignment.\n\t\tif titleAlignX == 'left' then\n\t\t\ttitleX = floor(ActiveInstance.X + ActiveInstance.Border)\n\t\telseif titleAlignX == 'right' then\n\t\t\ttitleX = floor(ActiveInstance.X + ActiveInstance.W - Style.Font:getWidth(ActiveInstance.Title) - ActiveInstance.Border)\n\n\t\t\tif showClose then\n\t\t\t\ttitleX = floor(titleX - closeBgRadius * 2)\n\t\t\tend\n\t\t\tif showMinimize then\n\t\t\t\ttitleX = floor(titleX - minimizeBgRadius * 2)\n\t\t\tend\n\t\tend\n\n\t\t-- Check for vertical alignment\n\t\tif titleAlignY == 'top' then\n\t\t\ttitleY = floor(ActiveInstance.Y - offsetY)\n\t\telseif titleAlignY == 'bottom' then\n\t\t\ttitleY = floor(ActiveInstance.Y - Style.Font:getHeight())\n\t\tend\n\n\t\tlocal titleColor = ActiveInstance.BackgroundColor\n\t\tif ActiveInstance == Stack[1] then\n\t\t\ttitleColor = Style.WindowTitleFocusedColor\n\t\tend\n\t\tDrawCommands.Rectangle('fill', ActiveInstance.X, ActiveInstance.Y - offsetY, ActiveInstance.W, offsetY, titleColor, titleRounding)\n\t\tDrawCommands.Rectangle('line', ActiveInstance.X, ActiveInstance.Y - offsetY, ActiveInstance.W, offsetY, nil, titleRounding)\n\t\tDrawCommands.Line(ActiveInstance.X, ActiveInstance.Y, ActiveInstance.X + ActiveInstance.W, ActiveInstance.Y, 1)\n\n\t\tdo\n\t\t\tlocal titleRegion = ActiveInstance.TitleRegion\n\t\t\ttitleRegion.X = ActiveInstance.X\n\t\t\ttitleRegion.MouseX = mouseX\n\t\t\ttitleRegion.MouseY = mouseY\n\t\t\ttitleRegion.IsObstructed = isObstructed\n\t\t\ttitleRegion.Y = ActiveInstance.Y - offsetY\n\t\t\ttitleRegion.W = ActiveInstance.W\n\t\t\ttitleRegion.H = offsetY\n\n\t\t\tRegion.Begin(ActiveInstance.TitleId, titleRegion)\n\t\tend\n\n\t\tDrawCommands.Print(ActiveInstance.Title, titleX, titleY, Style.TextColor, Style.Font)\n\n\t\tlocal offsetX = 1\n\t\tif showMinimize then\n\t\t\toffsetX = showClose and 5 or 2\n\t\t\tlocal isClicked = DrawButton(\n\t\t\t\t\"Minimize\",\n\t\t\t\tActiveInstance,\n\t\t\t\toptions,\n\t\t\t\tminimizeBgRadius,\n\t\t\t\toffsetX,\n\t\t\t\toffsetY,\n\t\t\t\tStyle.WindowMinimizeColorBgColor or Style.WindowCloseBgColor,\n\t\t\t\tStyle.WindowMinimizeColor or Style.WindowCloseColor\n\t\t\t)\n\t\t\tif isClicked then\n\t\t\t\tActiveInstance.IsContentOpen = not ActiveInstance.IsContentOpen\n\t\t\t\tActiveInstance.IsMoving = false\n\t\t\t\tActiveInstance.IsMinimized = not ActiveInstance.IsMinimized\n\t\t\tend\n\t\tend\n\n\t\tif showClose then\n\t\t\toffsetX = 2\n\t\t\tlocal isClicked = DrawButton(\n\t\t\t\t\"Close\",\n\t\t\t\tActiveInstance,\n\t\t\t\toptions,\n\t\t\t\tcloseBgRadius,\n\t\t\t\toffsetX,\n\t\t\t\toffsetY,\n\t\t\t\tStyle.WindowCloseBgColor,\n\t\t\t\tStyle.WindowCloseColor\n\t\t\t)\n\t\t\tif isClicked then\n\t\t\t\tActiveInstance.IsOpen = false\n\t\t\t\tActiveInstance.IsMoving = false\n\t\t\t\toptions.IsOpen = false\n\t\t\tend\n\t\tend\n\n\t\tRegion.End()\n\tend\n\n\tlocal regionW = ActiveInstance.W\n\tlocal regionH = ActiveInstance.H\n\n\tif ActiveInstance.X + ActiveInstance.W > Scale.GetScreenWidth() then regionW = Scale.GetScreenWidth() - ActiveInstance.X end\n\tif ActiveInstance.Y + ActiveInstance.H > Scale.GetScreenHeight() then regionH = Scale.GetScreenHeight() - ActiveInstance.Y end\n\n\tif ActiveInstance.IsContentOpen == false then\n\t\tregionW = 0\n\t\tregionH = 0\n\t\tActiveInstance.ContentW = 0\n\t\tActiveInstance.ContentH = 0\n\tend\n\n\tdo\n\t\tlocal region = ActiveInstance.InstanceRegion\n\t\tregion.X = ActiveInstance.X\n\t\tregion.Y = ActiveInstance.Y\n\t\tregion.W = regionW\n\t\tregion.H = regionH\n\t\tregion.ContentW = ActiveInstance.ContentW + ActiveInstance.Border\n\t\tregion.ContentH = ActiveInstance.ContentH + ActiveInstance.Border\n\t\tregion.BgColor = ActiveInstance.BackgroundColor\n\t\tregion.IsObstructed = isObstructed\n\t\tregion.MouseX = mouseX\n\t\tregion.MouseY = mouseY\n\t\tregion.ResetContent = ActiveInstance.HasResized\n\t\tregion.Rounding = bodyRounding\n\t\tregion.NoOutline = options.NoOutline\n\n\t\tif ActiveInstance.ShowScrollbarX or ActiveInstance.ShowScrollbarY then\n\t\t\tregion.IsObstructed = false\n\t\tend\n\t\tregion.ForceShowX = showScrollbarX\n\t\tregion.ForceShowY = showScrollbarY\n\n\t\tRegion.Begin(ActiveInstance.Id, region)\n\tend\n\n\tif resetSize or options.ResetLayout then\n\t\tActiveInstance.SizeDeltaX = 0\n\t\tActiveInstance.SizeDeltaY = 0\n\tend\n\n\tif resetContent or options.ResetLayout then\n\t\tActiveInstance.DeltaContentW = 0\n\t\tActiveInstance.DeltaContentH = 0\n\tend\n\n\treturn ActiveInstance.IsOpen\nend\n\nfunction Window.End()\n\tif not ActiveInstance then\n\t\treturn\n\tend\n\n\tlocal handle = ActiveInstance.StatHandle\n\n\t-- Clear the ID stack for use with other windows. This information can be kept transient\n\tfor i = 1, #IDStack do\n\t\tIDStack[i] = nil\n\tend\n\n\tRegion.End()\n\tDrawCommands.End(not ActiveInstance.IsOpen)\n\tremove(PendingStack, 1)\n\n\tCursor.SetPosition(ActiveInstance.LastCursorX, ActiveInstance.LastCursorY)\n\tActiveInstance = nil\n\tif #PendingStack > 0 then\n\t\tActiveInstance = PendingStack[1]\n\t\tCursor.SetAnchor(ActiveInstance.X + ActiveInstance.Border, ActiveInstance.Y + ActiveInstance.Border)\n\t\tDrawCommands.SetLayer(ActiveInstance.Layer)\n\t\tRegion.ApplyScissor()\n\tend\n\n\tStats.End(handle)\nend\n\nfunction Window.GetMousePosition()\n\tlocal x, y = Mouse.Position()\n\tif ActiveInstance ~= nil then\n\t\tx, y = Region.InverseTransform(nil, x, y)\n\tend\n\treturn x, y\nend\n\nfunction Window.GetWidth()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.W\n\tend\n\treturn 0\nend\n\nfunction Window.GetHeight()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.H\n\tend\n\treturn 0\nend\n\nfunction Window.GetBorder()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.Border\n\tend\n\treturn 0\nend\n\nfunction Window.GetBounds(IgnoreTitleBar)\n\tif ActiveInstance ~= nil then\n\t\tIgnoreTitleBar = IgnoreTitleBar == nil and false or IgnoreTitleBar\n\t\tlocal offsetY = (ActiveInstance.Title ~= \"\" and not IgnoreTitleBar) and ActiveInstance.TitleH or 0\n\t\treturn ActiveInstance.X, ActiveInstance.Y - offsetY, ActiveInstance.W, ActiveInstance.H + offsetY\n\tend\n\treturn 0, 0, 0, 0\nend\n\nfunction Window.GetPosition()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.X, ActiveInstance.Y - ActiveInstance.TitleH\n\tend\n\treturn 0, 0\nend\n\nfunction Window.GetSize()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.W, ActiveInstance.H\n\tend\n\treturn 0, 0\nend\n\nfunction Window.GetContentSize()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.ContentW, ActiveInstance.ContentH\n\tend\n\treturn 0, 0\nend\n\n--[[\n\tThis function is used to help other controls retrieve the available real estate needed to expand their\n\tbounds without expanding the bounds of the window by removing borders.\n--]]\nfunction Window.GetBorderlessSize()\n\tlocal w, h = 0, 0\n\n\tif ActiveInstance ~= nil then\n\t\tw = max(ActiveInstance.W, ActiveInstance.ContentW)\n\t\th = max(ActiveInstance.H, ActiveInstance.ContentH)\n\n\t\tw = max(0, w - ActiveInstance.Border * 2)\n\t\th = max(0, h - ActiveInstance.Border * 2)\n\tend\n\n\treturn w, h\nend\n\nfunction Window.GetRemainingSize()\n\tlocal w, h = Window.GetBorderlessSize()\n\n\tif ActiveInstance ~= nil then\n\t\tw = w - (Cursor.GetX() - ActiveInstance.X - ActiveInstance.Border)\n\t\th = h - (Cursor.GetY() - ActiveInstance.Y - ActiveInstance.Border)\n\tend\n\n\treturn w, h\nend\n\nfunction Window.IsMenuBar()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.IsMenuBar\n\tend\n\treturn false\nend\n\nfunction Window.GetId()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.Id\n\tend\n\treturn ''\nend\n\nfunction Window.AddItem(x, y, w, h, id)\n\tif ActiveInstance ~= nil then\n\t\tActiveInstance.LastItem = id\n\t\tif Region.IsActive(ActiveInstance.Id) then\n\t\t\tif ActiveInstance.AutoSizeWindowW then\n\t\t\t\tActiveInstance.SizeDeltaX = max(ActiveInstance.SizeDeltaX, x + w - ActiveInstance.X)\n\t\t\tend\n\n\t\t\tif ActiveInstance.AutoSizeWindowH then\n\t\t\t\tActiveInstance.SizeDeltaY = max(ActiveInstance.SizeDeltaY, y + h - ActiveInstance.Y)\n\t\t\tend\n\n\t\t\tif ActiveInstance.AutoSizeContent then\n\t\t\t\tActiveInstance.DeltaContentW = max(ActiveInstance.DeltaContentW, x + w - ActiveInstance.X)\n\t\t\t\tActiveInstance.DeltaContentH = max(ActiveInstance.DeltaContentH, y + h - ActiveInstance.Y)\n\t\t\tend\n\t\telse\n\t\t\tRegion.AddItem(x, y, w, h)\n\t\tend\n\tend\nend\n\nfunction Window.WheelMoved(x, y)\n\tRegion.WheelMoved(x, y)\nend\n\nfunction Window.TransformPoint(x, y)\n\tif ActiveInstance ~= nil then\n\t\treturn Region.Transform(ActiveInstance.Id, x, y)\n\tend\n\treturn 0, 0\nend\n\nfunction Window.ResetContentSize()\n\tif ActiveInstance ~= nil then\n\t\tActiveInstance.DeltaContentW = 0\n\t\tActiveInstance.DeltaContentH = 0\n\tend\nend\n\nfunction Window.SetHotItem(HotItem)\n\tif ActiveInstance ~= nil then\n\t\tActiveInstance.HotItem = HotItem\n\tend\nend\n\nfunction Window.SetContextHotItem(HotItem)\n\tif ActiveInstance ~= nil then\n\t\tActiveInstance.ContextHotItem = HotItem\n\tend\nend\n\nfunction Window.GetHotItem()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.HotItem\n\tend\n\treturn nil\nend\n\nfunction Window.IsItemHot()\n\tif ActiveInstance ~= nil and ActiveInstance.LastItem ~= nil then\n\t\treturn ActiveInstance.HotItem == ActiveInstance.LastItem\n\tend\n\treturn false\nend\n\nfunction Window.GetContextHotItem()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.ContextHotItem\n\tend\n\treturn nil\nend\n\nfunction Window.IsMouseHovered()\n\tif ActiveInstance ~= nil then\n\t\tlocal x, y = Mouse.Position()\n\t\treturn Contains(ActiveInstance, x, y)\n\tend\n\treturn false\nend\n\nfunction Window.GetItemId(id)\n\tif ActiveInstance ~= nil then\n\t\tif ActiveInstance.Items[id] == nil then\n\t\t\tActiveInstance.Items[id] = idCache:get(ActiveInstance.Id, id)\n\t\tend\n\n\t\t-- Apply any custom ID to the current item.\n\t\tlocal result = ActiveInstance.Items[id]\n\t\tif #IDStack > 0 then\n\t\t\tresult = result .. IDStack[#IDStack]\n\t\tend\n\n\t\treturn result\n\tend\n\treturn nil\nend\n\nfunction Window.GetLastItem()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.LastItem\n\tend\n\treturn nil\nend\n\nfunction Window.Validate()\n\tif #PendingStack > 1 then\n\t\tassert(false, \"EndWindow was not called for: \" .. PendingStack[1].Id)\n\tend\n\n\tMovingInstance = nil\n\tlocal shouldUpdate = false\n\tfor i = #Stack, 1, -1 do\n\t\tif Stack[i].IsMoving then\n\t\t\tMovingInstance = Stack[i]\n\t\tend\n\n\t\tif Stack[i].FrameNumber ~= Stats.GetFrameNumber() then\n\t\t\tStack[i].StackIndex = 0\n\t\t\tRegion.ClearHotInstance(Stack[i].Id)\n\t\t\tRegion.ClearHotInstance(Stack[i].TitleId)\n\t\t\tremove(Stack, i)\n\t\t\tshouldUpdate = true\n\t\tend\n\tend\n\n\tif shouldUpdate then\n\t\tUpdateStackIndex()\n\tend\nend\n\nfunction Window.HasResized()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.HasResized\n\tend\n\treturn false\nend\n\nfunction Window.SetStackLock(id)\n\tStackLockId = id\nend\n\nfunction Window.PushToTop(id)\n\tlocal instance = GetInstance(id)\n\n\tif instance ~= nil then\n\t\tPushToTop(instance)\n\tend\nend\n\nfunction Window.IsAppearing()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.IsAppearing\n\tend\n\n\treturn false\nend\n\nfunction Window.GetLayer()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.Layer\n\tend\n\treturn 'Normal'\nend\n\nfunction Window.GetInstanceIds()\n\tlocal result = {}\n\n\tfor i, v in ipairs(Instances) do\n\t\tinsert(result, v.Id)\n\tend\n\n\treturn result\nend\n\nfunction Window.GetInstanceInfo(id)\n\tlocal result = {}\n\n\tlocal instance = nil\n\tfor i, v in ipairs(Instances) do\n\t\tif v.Id == id then\n\t\t\tinstance = v\n\t\t\tbreak\n\t\tend\n\tend\n\n\tinsert(result, \"MovingInstance: \" .. (MovingInstance ~= nil and MovingInstance.Id or \"nil\"))\n\n\tif instance ~= nil then\n\t\tinsert(result, \"Title: \" .. instance.Title)\n\t\tinsert(result, \"TitleH: \" .. instance.TitleH)\n\t\tinsert(result, \"X: \" .. instance.X)\n\t\tinsert(result, \"Y: \" .. instance.Y)\n\t\tinsert(result, \"W: \" .. instance.W)\n\t\tinsert(result, \"H: \" .. instance.H)\n\t\tinsert(result, \"ContentW: \" .. instance.ContentW)\n\t\tinsert(result, \"ContentH: \" .. instance.ContentH)\n\t\tinsert(result, \"TitleDeltaX: \" .. instance.TitleDeltaX)\n\t\tinsert(result, \"TitleDeltaY: \" .. instance.TitleDeltaY)\n\t\tinsert(result, \"SizeDeltaX: \" .. instance.SizeDeltaX)\n\t\tinsert(result, \"SizeDeltaY: \" .. instance.SizeDeltaY)\n\t\tinsert(result, \"DeltaContentW: \" .. instance.DeltaContentW)\n\t\tinsert(result, \"DeltaContentH: \" .. instance.DeltaContentH)\n\t\tinsert(result, \"Border: \" .. instance.Border)\n\t\tinsert(result, \"Layer: \" .. instance.Layer)\n\t\tinsert(result, \"Stack Index: \" .. instance.StackIndex)\n\t\tinsert(result, \"AutoSizeWindow: \" .. tostring(instance.AutoSizeWindow))\n\t\tinsert(result, \"AutoSizeContent: \" .. tostring(instance.AutoSizeContent))\n\t\tinsert(result, \"Hot Item: \" .. tostring(instance.HotItem))\n\tend\n\n\treturn result\nend\n\nfunction Window.GetStackDebug()\n\tlocal result = {}\n\n\tfor i, v in ipairs(Stack) do\n\t\tresult[i] = tostring(v.StackIndex) .. \": \" .. v.Id\n\n\t\tif v.Id == StackLockId then\n\t\t\tresult[i] = result[i] .. \" (Locked)\"\n\t\tend\n\tend\n\n\treturn result\nend\n\nfunction Window.IsAutoSize()\n\tif ActiveInstance ~= nil then\n\t\treturn ActiveInstance.AutoSizeWindowW or ActiveInstance.AutoSizeWindowH\n\tend\n\n\treturn false\nend\n\nfunction Window.Save(Table)\n\tif Table ~= nil then\n\t\tlocal settings = {}\n\t\tfor i, v in ipairs(Instances) do\n\t\t\tif not v.NoSavedSettings then\n\t\t\t\tsettings[v.Id] = {\n\t\t\t\t\tX = v.TitleDeltaX,\n\t\t\t\t\tY = v.TitleDeltaY,\n\t\t\t\t\tW = v.SizeDeltaX,\n\t\t\t\t\tH = v.SizeDeltaY\n\t\t\t\t}\n\t\t\tend\n\t\tend\n\t\tTable['Window'] = settings\n\tend\nend\n\nfunction Window.Load(Table)\n\tif Table ~= nil then\n\t\tlocal settings = Table['Window']\n\t\tif settings ~= nil then\n\t\t\tfor k, v in pairs(settings) do\n\t\t\t\tlocal instance = GetInstance(k)\n\t\t\t\tinstance.TitleDeltaX = v.X\n\t\t\t\tinstance.TitleDeltaY = v.Y\n\t\t\t\tinstance.SizeDeltaX = v.W\n\t\t\t\tinstance.SizeDeltaY = v.H\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction Window.GetMovingInstance()\n\treturn MovingInstance\nend\n\n--[[\n\tAllow developers to push/pop a custom ID to the stack. This can help with differentiating between controls with identical IDs i.e. text fields.\n--]]\nfunction Window.PushID(id)\n\tif ActiveInstance ~= nil then\n\t\tinsert(IDStack, id)\n\tend\nend\n\nfunction Window.PopID()\n\tif #IDStack > 0 then\n\t\treturn remove(IDStack)\n\tend\n\n\treturn nil\nend\n\nfunction Window.ToDock(type)\n\tlocal activeInstance = GetInstance()\n\tactiveInstance.W = 720\n\tactiveInstance.H = 720\n\tDock.SetPendingWindow(activeInstance, type)\n\tDock.Override()\nend\n\nreturn Window\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "![](https://github.com/flamendless/Slab/blob/master/assets/slab.png)\n\n# Slab\n\nSlab is an immediate mode GUI toolkit for the Love 2D framework. This library is designed to\nallow users to easily add this library to their existing Love 2D projects and quickly create\ntools to enable them to iterate on their ideas quickly. The user should be able to utilize this\nlibrary with minimal integration steps and is completely written in Lua and utilizes\nthe Love 2D API. No compiled binaries are required and the user will have access to the source\nso that they may make adjustments that meet the needs of their own projects and tools. Refer\nto main.lua and SlabTest.lua for example usage of this library.\n\n### Usage\n\nIntegrating this library into existing projects is very simple.\n\n```lua\nlocal Slab = require 'Slab'\n\nfunction love.load(args)\n\tlove.graphics.setBackgroundColor(0.4, 0.88, 1.0)\n\tSlab.Initialize(args)\nend\n\nfunction love.update(dt)\n\tSlab.Update(dt)\n  \n\tSlab.BeginWindow('MyFirstWindow', {Title = \"My First Window\"})\n\tSlab.Text(\"Hello World\")\n\tSlab.EndWindow()\nend\n\nfunction love.draw()\n\tSlab.Draw()\nend\n```\n![](https://github.com/coding-jackalope/Slab/wiki/Images/Slab_HelloWorld.png)\n\nFor more detailed information on usage of this library, refer to the [Wiki](https://github.com/coding-jackalope/Slab/wiki).\n\n[LOVE forum](https://love2d.org/forums/viewtopic.php?t=86410)\n\n### License\n\nSlab is licensed under the MIT license. Please see the LICENSE file for more information.\n\n### Credits\n* [coding-jackalope](https://github.com/coding-jackalope) original developer of this library.\n* [Dear ImGui](https://github.com/ocornut/imgui) project built by Omar Cornut and various contributors. This project was the inspiration for building an Immediate Mode GUI for Love2D specifically. If anyone is building a game or application in C++, I highly recommend using this library and its rich toolset to speed up development.\n* [Kenney.nl](https://kenney.nl/) and the [Tango Desktop Project](https://opengameart.org/content/tango-desktop-icons) for providing icons used in this project.\n* [lovefs](https://github.com/linux-man/lovefs) provides some FFI code for the filesystem.\n* [luapower/fs](https://github.com/luapower/fs) provides cross platform FFI code for the filesystem.\n"
  },
  {
    "path": "Slab.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\n-- This file is for running a project within the Slab folder. This file\n-- should not be used when using the Slab folder within another project.\nif SLAB_PATH == nil then\n\tSLAB_PATH = (...):match(\"(.-)[^%.]+$\") \nend\n\n---@type Slab\nlocal Slab = require(SLAB_PATH .. '.API')\n\n---@type Slab\nreturn Slab\n"
  },
  {
    "path": "SlabDebug.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Slab = require(SLAB_PATH .. '.Slab')\nlocal DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')\nlocal Input = require(SLAB_PATH .. '.Internal.UI.Input')\nlocal LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')\nlocal Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')\nlocal Region = require(SLAB_PATH .. '.Internal.UI.Region')\nlocal Stats = require(SLAB_PATH .. '.Internal.Core.Stats')\nlocal Style = require(SLAB_PATH .. '.Style')\nlocal Tooltip = require(SLAB_PATH .. '.Internal.UI.Tooltip')\nlocal Tree = require(SLAB_PATH .. '.Internal.UI.Tree')\nlocal Window = require(SLAB_PATH .. '.Internal.UI.Window')\n\nlocal SlabDebug = {}\nlocal SlabDebug_About = 'SlabDebug_About'\nlocal SlabDebug_Mouse = {Title = \"Mouse\", IsOpen = false}\nlocal SlabDebug_Windows = {Title = \"Windows\", IsOpen = false}\nlocal SlabDebug_Regions = {Title = \"Regions\", IsOpen = false}\nlocal SlabDebug_Tooltip = {Title = \"Tooltip\", IsOpen = false}\nlocal SlabDebug_DrawCommands = {Title = \"DrawCommands\", IsOpen = false}\nlocal SlabDebug_Performance = {Title = \"Performance\", IsOpen = false}\nlocal SlabDebug_StyleEditor = {Title = \"Style Editor\", IsOpen = false, AutoSizeWindow = false, AllowResize = true, W = 700.0, H = 500.0}\nlocal SlabDebug_Input = {Title = \"Input\", IsOpen = false}\nlocal SlabDebug_MultiLine = {Title = \"Multi-Line Input\", IsOpen = false}\nlocal SlabDebug_MultiLine_FileDialog = false\nlocal SlabDebug_MultiLine_FileName = \"\"\nlocal SlabDebug_MultiLine_Contents = \"\"\nlocal SlabDebug_Tree = {Title = \"Tree\", IsOpen = false, AutoSizeWindow = false, AllowResize = true}\nlocal SlabDebug_LayoutManager = {Title = \"Layout Manager\", IsOpen = false, AutoSizeWindow = false, AllowResize = true}\n\nlocal SlabDebug_Windows_Categories = {\"Inspector\", \"Stack\"}\nlocal SlabDebug_Windows_Category = \"Inspector\"\nlocal SlabDebug_Regions_Selected = \"\"\n\nlocal Selected_Window = \"\"\n\nlocal Style_EditingColor = nil\nlocal Style_ColorStore = nil\nlocal Style_FileDialog = nil\n\nlocal function Window_Inspector()\n\tlocal Ids = Window.GetInstanceIds()\n\tif Slab.BeginComboBox('SlabDebug_Windows_Inspector', {Selected = Selected_Window}) then\n\t\tfor I, V in ipairs(Ids) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tSelected_Window = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tlocal Info = Window.GetInstanceInfo(Selected_Window)\n\tfor I, V in ipairs(Info) do\n\t\tSlab.Text(V)\n\tend\nend\n\nlocal function Window_Stack()\n\tlocal Stack = Window.GetStackDebug()\n\tSlab.Text(\"Stack: \" .. #Stack)\n\tfor I, V in ipairs(Stack) do\n\t\tSlab.Text(V)\n\tend\nend\n\nlocal function DrawCommands_Item(Root, Label)\n\tif type(Root) == \"table\" then\n\t\tif Slab.BeginTree(Label) then\n\t\t\tfor K, V in pairs(Root) do\n\t\t\t\tDrawCommands_Item(V, K)\n\t\t\tend\n\n\t\t\tSlab.EndTree()\n\t\tend\n\telse\n\t\tSlab.BeginTree(Label .. \" \" .. tostring(Root), {IsLeaf = true})\n\tend\nend\n\nlocal DrawPerformance_Category = nil\nlocal DrawPerformance_WinX = 50.0\nlocal DrawPerformance_WinY = 50.0\nlocal DrawPerformance_ResetPosition = false\nlocal DrawPerformance_Init = false\nlocal DrawPerformance_W = 200.0\n\nlocal function DrawPerformance()\n\tif not DrawPerformance_Init then\n\t\tSlab.EnableStats(true)\n\t\tDrawPerformance_Init = true\n\tend\n\n\tSlabDebug_Performance.X = DrawPerformance_WinX\n\tSlabDebug_Performance.Y = DrawPerformance_WinY\n\tSlabDebug_Performance.ResetPosition = DrawPerformance_ResetPosition\n\n\tSlab.BeginWindow('SlabDebug_Performance', SlabDebug_Performance)\n\tDrawPerformance_ResetPosition = false\n\n\tlocal Categories = Stats.GetCategories()\n\n\tif DrawPerformance_Category == nil then\n\t\tDrawPerformance_Category = Categories[1]\n\tend\n\n\tif Slab.BeginComboBox('DrawPerformance_Categories', {Selected = DrawPerformance_Category, W = DrawPerformance_W}) then\n\t\tfor I, V in ipairs(Categories) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawPerformance_Category = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tif Slab.CheckBox(Slab.IsStatsEnabled(), \"Enabled\") then\n\t\tSlab.EnableStats(not Slab.IsStatsEnabled())\n\tend\n\n\tSlab.SameLine()\n\n\tif Slab.Button(\"Flush\") then\n\t\tSlab.FlushStats()\n\tend\n\n\tSlab.Separator()\n\n\tif DrawPerformance_Category ~= nil then\n\t\tlocal Items = Stats.GetItems(DrawPerformance_Category)\n\n\t\tlocal Pad = 50.0\n\t\tlocal MaxW = 0.0\n\t\tfor I, V in ipairs(Items) do\n\t\t\tMaxW = math.max(MaxW, Slab.GetTextWidth(V))\n\t\tend\n\n\t\tlocal CursorX, CursorY = Slab.GetCursorPos()\n\t\tSlab.SetCursorPos(MaxW * 0.5 - Slab.GetTextWidth(\"Stat\") * 0.5)\n\t\tSlab.Text(\"Stat\")\n\n\t\tlocal TimeX = MaxW + Pad\n\t\tlocal TimeW = Slab.GetTextWidth(\"Time\")\n\t\tlocal TimeItemW = Slab.GetTextWidth(string.format(\"%.4f\", 0.0))\n\t\tSlab.SetCursorPos(TimeX, CursorY)\n\t\tSlab.Text(\"Time\")\n\n\t\tlocal MaxTimeX = TimeX + TimeW + Pad\n\t\tlocal MaxTimeW = Slab.GetTextWidth(\"Max Time\")\n\t\tSlab.SetCursorPos(MaxTimeX, CursorY)\n\t\tSlab.Text(\"Max Time\")\n\n\t\tlocal CallCountX = MaxTimeX + MaxTimeW + Pad\n\t\tlocal CallCountW = Slab.GetTextWidth(\"Call Count\")\n\t\tSlab.SetCursorPos(CallCountX, CursorY)\n\t\tSlab.Text(\"Call Count\")\n\n\t\tDrawPerformance_W = CallCountX + CallCountW\n\n\t\tSlab.Separator()\n\n\t\tfor I, V in ipairs(Items) do\n\t\t\tlocal Time = Stats.GetTime(V, DrawPerformance_Category)\n\t\t\tlocal MaxTime = Stats.GetMaxTime(V, DrawPerformance_Category)\n\t\t\tlocal CallCount = Stats.GetCallCount(V, DrawPerformance_Category)\n\n\t\t\tCursorX, CursorY = Slab.GetCursorPos()\n\t\t\tSlab.SetCursorPos(MaxW * 0.5 - Slab.GetTextWidth(V) * 0.5)\n\t\t\tSlab.Text(V)\n\n\t\t\tSlab.SetCursorPos(TimeX + TimeW * 0.5 - TimeItemW * 0.5, CursorY)\n\t\t\tSlab.Text(string.format(\"%.4f\", Time))\n\n\t\t\tSlab.SetCursorPos(MaxTimeX + MaxTimeW * 0.5 - TimeItemW * 0.5, CursorY)\n\t\t\tSlab.Text(string.format(\"%.4f\", MaxTime))\n\n\t\t\tSlab.SetCursorPos(CallCountX + CallCountW * 0.5 - Slab.GetTextWidth(CallCount) * 0.5, CursorY)\n\t\t\tSlab.Text(CallCount)\n\t\tend\n\tend\n\n\tSlab.EndWindow()\nend\n\nlocal function EditColor(Color)\n\tStyle_EditingColor = Color\n\tStyle_ColorStore = {Color[1], Color[2], Color[3], Color[4]}\nend\n\nlocal function RestoreEditColor()\n\tStyle_EditingColor[1] = Style_ColorStore[1]\n\tStyle_EditingColor[2] = Style_ColorStore[2]\n\tStyle_EditingColor[3] = Style_ColorStore[3]\n\tStyle_EditingColor[4] = Style_ColorStore[4]\nend\n\nlocal function DrawStyleEditor()\n\tSlab.BeginWindow('SlabDebug_StyleEditor', SlabDebug_StyleEditor)\n\tlocal X, Y = Slab.GetWindowPosition()\n\tlocal W, H = Slab.GetWindowSize()\n\n\tlocal Style = Slab.GetStyle()\n\tlocal Names = Style.API.GetStyleNames()\n\tlocal CurrentStyle = Style.API.GetCurrentStyleName()\n\tSlab.BeginLayout('SlabDebug_StyleEditor_Styles_Layout', {ExpandW = true})\n\tif Slab.BeginComboBox('SlabDebug_StyleEditor_Styles', {Selected = CurrentStyle}) then\n\t\tfor I, V in ipairs(Names) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tStyle.API.SetStyle(V)\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tif Slab.Button(\"New\") then\n\t\tStyle_FileDialog = 'new'\n\tend\n\n\tSlab.SameLine()\n\n\tif Slab.Button(\"Load\") then\n\t\tStyle_FileDialog = 'load'\n\tend\n\n\tSlab.SameLine()\n\n\tlocal SaveDisabled = Style.API.IsDefaultStyle(CurrentStyle)\n\tif Slab.Button(\"Save\", {Disabled = SaveDisabled}) then\n\t\tStyle.API.SaveCurrentStyle()\n\tend\n\tSlab.EndLayout()\n\n\tSlab.Separator()\n\n\tlocal Refresh = false\n\tSlab.BeginLayout('SlabDebug_StyleEditor_Content_Layout', {Columns = 2, ExpandW = true})\n\tfor K, V in pairs(Style) do\n\t\tif type(V) == \"table\" and K ~= \"Font\" and K ~= \"API\" then\n\t\t\tSlab.SetLayoutColumn(1)\n\t\t\tSlab.Text(K)\n\t\t\tSlab.SetLayoutColumn(2)\n\t\t\tlocal W, H = Slab.GetLayoutSize()\n\t\t\tH = Slab.GetTextHeight()\n\t\t\tSlab.Rectangle({W = W, H = H, Color = V, Outline = true})\n\t\t\tif Slab.IsControlClicked() then\n\t\t\t\tif Style_EditingColor ~= nil then\n\t\t\t\t\tRestoreEditColor()\n\t\t\t\t\tRefresh = true\n\t\t\t\tend\n\n\t\t\t\tEditColor(V)\n\t\t\tend\n\t\tend\n\tend\n\n\tfor K, V in pairs(Style) do\n\t\tif type(V) == \"number\" and K ~= \"FontSize\" then\n\t\t\tSlab.SetLayoutColumn(1)\n\t\t\tSlab.Text(K)\n\t\t\tSlab.SetLayoutColumn(2)\n\t\t\tif Slab.Input('SlabDebug_Style_' .. K, {Text = tostring(V), ReturnOnText = false, NumbersOnly = true}) then\n\t\t\t\tStyle[K] = Slab.GetInputNumber()\n\t\t\tend\n\t\tend\n\tend\n\tSlab.EndLayout()\n\tSlab.EndWindow()\n\n\tif Style_EditingColor ~= nil then\n\t\tlocal Result = Slab.ColorPicker({Color = Style_ColorStore, X = X + W, Y = Y})\n\t\tStyle_EditingColor[1] = Result.Color[1]\n\t\tStyle_EditingColor[2] = Result.Color[2]\n\t\tStyle_EditingColor[3] = Result.Color[3]\n\t\tStyle_EditingColor[4] = Result.Color[4]\n\n\t\tif Result.Button ~= 0 then\n\t\t\tif Result.Button == 1 then\n\t\t\t\tStyle.API.StoreCurrentStyle()\n\t\t\tend\n\n\t\t\tif Result.Button == -1 then\n\t\t\t\tRestoreEditColor()\n\t\t\tend\n\n\t\t\tStyle_EditingColor = nil\n\t\tend\n\tend\n\n\tif Style_FileDialog ~= nil then\n\t\tlocal Type = Style_FileDialog == 'new' and 'savefile' or Style_FileDialog == 'load' and 'openfile' or nil\n\n\t\tif Type ~= nil then\n\t\t\tlocal Path = love.filesystem.getRealDirectory(SLAB_FILE_PATH) .. \"/\" .. SLAB_FILE_PATH .. \"Internal/Resources/Styles\"\n\t\t\tlocal Result = Slab.FileDialog({AllowMultiSelect = false, Directory = Path, Type = Type, Filters = {{\"*.style\", \"Styles\"}}})\n\n\t\t\tif Result.Button ~= \"\" then\n\t\t\t\tif Result.Button == \"OK\" then\n\t\t\t\t\tif Style_FileDialog == 'new' then\n\t\t\t\t\t\tStyle.API.CopyCurrentStyle(Result.Files[1])\n\t\t\t\t\telse\n\t\t\t\t\t\tStyle.API.LoadStyle(Result.Files[1], true)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tStyle_FileDialog = nil\n\t\t\tend\n\t\telse\n\t\t\tStyle_FileDialog = nil\n\t\tend\n\tend\nend\n\nfunction SlabDebug.About()\n\tif Slab.BeginDialog(SlabDebug_About, {Title = \"About\"}) then\n\t\tSlab.Text(\"Slab Version: \" .. Slab.GetVersion())\n\t\tSlab.Text(\"Love Version: \" .. Slab.GetLoveVersion())\n\t\tSlab.NewLine()\n\t\tSlab.BeginLayout(SlabDebug_About .. '.Buttons_Layout', {AlignX = 'center'})\n\t\tif Slab.Button(\"OK\") then\n\t\t\tSlab.CloseDialog()\n\t\tend\n\t\tSlab.EndLayout()\n\t\tSlab.EndDialog()\n\tend\nend\n\nfunction SlabDebug.OpenAbout()\n\tSlab.OpenDialog(SlabDebug_About)\nend\n\nfunction SlabDebug.Mouse()\n\tSlab.BeginWindow('SlabDebug_Mouse', SlabDebug_Mouse)\n\tlocal X, Y = Mouse.Position()\n\tSlab.Text(\"X: \" .. X)\n\tSlab.Text(\"Y: \" .. Y)\n\n\tlocal DeltaX, DeltaY = Mouse.GetDelta()\n\tSlab.Text(\"Delta X: \" .. DeltaX)\n\tSlab.Text(\"Delta Y: \" .. DeltaY)\n\n\tfor I = 1, 3, 1 do\n\t\tSlab.Text(\"Button \" .. I .. \": \" .. (Mouse.IsDown(I) and \"Pressed\" or \"Released\"))\n\tend\n\n\tSlab.Text(\"Hot Region: \" .. Region.GetHotInstanceId())\n\tSlab.EndWindow()\nend\n\nfunction SlabDebug.Windows()\n\tSlab.BeginWindow('SlabDebug_Windows', SlabDebug_Windows)\n\n\tif Slab.BeginComboBox('SlabDebug_Windows_Categories', {Selected = SlabDebug_Windows_Category}) then\n\t\tfor I, V in ipairs(SlabDebug_Windows_Categories) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tSlabDebug_Windows_Category = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tif SlabDebug_Windows_Category == \"Inspector\" then\n\t\tWindow_Inspector()\n\telseif SlabDebug_Windows_Category == \"Stack\" then\n\t\tWindow_Stack()\n\tend\n\n\tSlab.EndWindow()\nend\n\nfunction SlabDebug.Regions()\n\tSlab.BeginWindow('SlabDebug_Regions', SlabDebug_Regions)\n\n\tlocal Ids = Region.GetInstanceIds()\n\tif Slab.BeginComboBox('SlabDebug_Regions_Ids', {Selected = SlabDebug_Regions_Selected}) then\n\t\tfor I, V in ipairs(Ids) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tSlabDebug_Regions_Selected = V\n\t\t\tend\n\t\tend\n\t\tSlab.EndComboBox()\n\tend\n\n\tlocal Info = Region.GetDebugInfo(SlabDebug_Regions_Selected)\n\tfor I, V in ipairs(Info) do\n\t\tSlab.Text(V)\n\tend\n\n\tSlab.EndWindow()\nend\n\nfunction SlabDebug.Tooltip()\n\tSlab.BeginWindow('SlabDebug_Tooltip', SlabDebug_Tooltip)\n\n\tlocal Info = Tooltip.GetDebugInfo()\n\tfor I, V in ipairs(Info) do\n\t\tSlab.Text(V)\n\tend\n\n\tSlab.EndWindow()\nend\n\nfunction SlabDebug.DrawCommands()\n\tSlab.BeginWindow('SlabDebug_DrawCommands', SlabDebug_DrawCommands)\n\n\tlocal Info = DrawCommands.GetDebugInfo()\n\tfor K, V in pairs(Info) do\n\t\tDrawCommands_Item(V, K)\n\tend\n\n\tSlab.EndWindow()\nend\n\nfunction SlabDebug.Performance()\n\tDrawPerformance()\nend\n\nfunction SlabDebug.Performance_SetPosition(X, Y)\n\tDrawPerformance_WinX = X ~= nil and X or 50.0\n\tDrawPerformance_WinY = Y ~= nil and Y or 50.0\n\tDrawPerformance_ResetPosition = true\nend\n\nfunction SlabDebug.StyleEditor()\n\tDrawStyleEditor()\nend\n\nfunction SlabDebug.Input()\n\tSlab.BeginWindow('SlabDebug_Input', SlabDebug_Input)\n\n\tlocal Info = Input.GetDebugInfo()\n\tSlab.Text(\"Focused: \" .. Info['Focused'])\n\tSlab.Text(\"Width: \" .. Info['Width'])\n\tSlab.Text(\"Height: \" .. Info['Height'])\n\tSlab.Text(\"Cursor X: \" .. Info['CursorX'])\n\tSlab.Text(\"Cursor Y: \" .. Info['CursorY'])\n\tSlab.Text(\"Cursor Position: \" .. Info['CursorPos'])\n\tSlab.Text(\"Character: \" .. Info['Character'])\n\tSlab.Text(\"Line Position: \" .. Info['LineCursorPos'])\n\tSlab.Text(\"Line Position Max: \" .. Info['LineCursorPosMax'])\n\tSlab.Text(\"Line Number: \" .. Info['LineNumber'])\n\tSlab.Text(\"Line Length: \" .. Info['LineLength'])\n\n\tlocal Lines = Info['Lines']\n\tif Lines ~= nil then\n\t\tSlab.Text(\"Lines: \" .. #Lines)\n\tend\n\n\tSlab.EndWindow()\nend\n\nlocal SlabDebug_MultiLine_Highlight = {\n\t['function'] = {1, 0, 0, 1},\n\t['end'] = {1, 0, 0, 1},\n\t['if'] = {1, 0, 0, 1},\n\t['then'] = {1, 0, 0, 1},\n\t['local'] = {1, 0, 0, 1},\n\t['for'] = {1, 0, 0, 1},\n\t['do'] = {1, 0, 0, 1},\n\t['not'] = {1, 0, 0, 1},\n\t['while'] = {1, 0, 0, 1},\n\t['repeat'] = {1, 0, 0, 1},\n\t['until'] = {1, 0, 0, 1},\n\t['break'] = {1, 0, 0, 1},\n\t['else'] = {1, 0, 0, 1},\n\t['elseif'] = {1, 0, 0, 1},\n\t['in'] = {1, 0, 0, 1},\n\t['and'] = {1, 0, 0, 1},\n\t['or'] = {1, 0, 0, 1},\n\t['true'] = {1, 0, 0, 1},\n\t['false'] = {1, 0, 0, 1},\n\t['nil'] = {1, 0, 0, 1},\n\t['return'] = {1, 0, 0, 1}\n}\n\nlocal SlabDebug_MultiLine_ShouldHighlight = true\n\nfunction SlabDebug.MultiLine()\n\tSlab.BeginWindow('SlabDebug_MultiLine', SlabDebug_MultiLine)\n\n\tif Slab.Button(\"Load\") then\n\t\tSlabDebug_MultiLine_FileDialog = true\n\tend\n\n\tSlab.SameLine()\n\n\tif Slab.Button(\"Save\", {Disabled = SlabDebug_MultiLine_FileName == \"\"}) then\n\t\tlocal Handle, Error = io.open(SlabDebug_MultiLine_FileName, \"w\")\n\n\t\tif Handle ~= nil then\n\t\t\tHandle:write(SlabDebug_MultiLine_Contents)\n\t\t\tHandle:close()\n\t\tend\n\tend\n\n\tlocal ItemW, ItemH = Slab.GetControlSize()\n\n\tSlab.SameLine()\n\tif Slab.CheckBox(SlabDebug_MultiLine_ShouldHighlight, \"Use Lua Highlight\", {Size = ItemH}) then\n\t\tSlabDebug_MultiLine_ShouldHighlight = not SlabDebug_MultiLine_ShouldHighlight\n\tend\n\n\tSlab.Separator()\n\n\tSlab.Text(\"File: \" .. SlabDebug_MultiLine_FileName)\n\n\tif Slab.Input('SlabDebug_MultiLine', {\n\t\tMultiLine = true,\n\t\tText = SlabDebug_MultiLine_Contents,\n\t\tW = 500.0,\n\t\tH = 500.0,\n\t\tHighlight = SlabDebug_MultiLine_ShouldHighlight and SlabDebug_MultiLine_Highlight or nil\n\t}) then\n\t\tSlabDebug_MultiLine_Contents = Slab.GetInputText()\n\tend\n\n\tSlab.EndWindow()\n\n\tif SlabDebug_MultiLine_FileDialog then\n\t\tlocal Result = Slab.FileDialog({AllowMultiSelect = false, Type = 'openfile'})\n\n\t\tif Result.Button ~= \"\" then\n\t\t\tSlabDebug_MultiLine_FileDialog = false\n\n\t\t\tif Result.Button == \"OK\" then\n\t\t\t\tSlabDebug_MultiLine_FileName = Result.Files[1]\n\t\t\t\tlocal Handle, Error = io.open(SlabDebug_MultiLine_FileName, \"r\")\n\n\t\t\t\tif Handle ~= nil then\n\t\t\t\t\tSlabDebug_MultiLine_Contents = Handle:read(\"*a\")\n\t\t\t\t\tHandle:close()\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction SlabDebug.Tree()\n\tif not SlabDebug_Tree.IsOpen then\n\t\treturn\n\tend\n\n\tlocal Info = Tree.GetDebugInfo()\n\n\tSlab.BeginWindow('Tree', SlabDebug_Tree)\n\tSlab.Text(\"Instances: \" .. #Info)\n\n\tSlab.BeginLayout('Tree_List_Layout', {ExpandW = true, ExpandH = true})\n\tSlab.BeginListBox('Tree_List')\n\tfor I, V in ipairs(Info) do\n\t\tSlab.BeginListBoxItem('Item_' .. I)\n\t\tSlab.Text(V)\n\t\tSlab.EndListBoxItem()\n\tend\n\tSlab.EndListBox()\n\tSlab.EndLayout()\n\n\tSlab.EndWindow()\nend\n\nlocal SlabDebug_LayoutManager_Selected = nil\n\nfunction SlabDebug.LayoutManager()\n\tlocal Info = LayoutManager.GetDebugInfo()\n\n\tSlab.BeginWindow('LayoutManager', SlabDebug_LayoutManager)\n\n\tSlab.BeginLayout('LayoutManager_Layout', {ExpandW = true})\n\tif Slab.BeginComboBox('LayoutManager_ID', {Selected = SlabDebug_LayoutManager_Selected}) then\n\t\tfor K, V in pairs(Info) do\n\t\t\tif SlabDebug_LayoutManager_Selected == nil then\n\t\t\t\tSlabDebug_LayoutManager_Selected = K\n\t\t\tend\n\n\t\t\tif Slab.TextSelectable(K) then\n\t\t\t\tSlabDebug_LayoutManager_Selected = K\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\tSlab.EndLayout()\n\n\tif SlabDebug_LayoutManager_Selected ~= nil then\n\t\tlocal Items = Info[SlabDebug_LayoutManager_Selected]\n\n\t\tfor I, V in ipairs(Items) do\n\t\t\tSlab.Text(V)\n\t\tend\n\tend\n\n\tSlab.EndWindow()\nend\n\nlocal function MenuItemWindow(Options)\n\tif Slab.MenuItemChecked(Options.Title, Options.IsOpen) then\n\t\tOptions.IsOpen = not Options.IsOpen\n\tend\nend\n\nfunction SlabDebug.Menu()\n\tif Slab.BeginMenu(\"Debug\") then\n\t\tif Slab.MenuItem(\"About\") then\n\t\t\tSlabDebug.OpenAbout()\n\t\tend\n\n\t\tMenuItemWindow(SlabDebug_Mouse)\n\t\tMenuItemWindow(SlabDebug_Windows)\n\t\tMenuItemWindow(SlabDebug_Regions)\n\t\tMenuItemWindow(SlabDebug_Tooltip)\n\t\tMenuItemWindow(SlabDebug_DrawCommands)\n\t\tMenuItemWindow(SlabDebug_Performance)\n\t\tMenuItemWindow(SlabDebug_StyleEditor)\n\t\tMenuItemWindow(SlabDebug_Input)\n\t\tMenuItemWindow(SlabDebug_MultiLine)\n\t\tMenuItemWindow(SlabDebug_Tree)\n\t\tMenuItemWindow(SlabDebug_LayoutManager)\n\n\t\tStats.SetEnabled(SlabDebug_Performance.IsOpen)\n\n\t\tSlab.EndMenu()\n\tend\nend\n\nfunction SlabDebug.Begin()\n\tSlabDebug.About()\n\tSlabDebug.Mouse()\n\tSlabDebug.Windows()\n\tSlabDebug.Regions()\n\tSlabDebug.Tooltip()\n\tSlabDebug.DrawCommands()\n\tSlabDebug.Performance()\n\tSlabDebug.StyleEditor()\n\tSlabDebug.Input()\n\tSlabDebug.MultiLine()\n\tSlabDebug.Tree()\n\tSlabDebug.LayoutManager()\nend\n\nreturn SlabDebug\n"
  },
  {
    "path": "SlabDefinition.lua",
    "content": "---@meta\n\n---@class Slab\nlocal Slab = {}\n\n\n---@class Slab.BeginWindowOptions\n---@field X? number\n---@field Y? number\n---@field W? number\n---@field H? number\n---@field ContentW? number\n---@field ContentH? number\n---@field BgColor? table\n---@field Title? string\n---@field TitleH? number\n---@field TitleAlignX? \"left\" | \"center\" | \"right\"\n---@field TitleAlignY? \"top\" | \"center\" | \"bottom\"\n---@field AllowMove? boolean\n---@field AllowResize? boolean\n---@field AllowFocus? boolean\n---@field Border? number\n---@field NoOutline? boolean\n---@field IsMenuBar? boolean\n---@field AutoSizeWindow? boolean\n---@field AutoSizeWindowW? boolean\n---@field AutoSizeWindowH? boolean\n---@field AutoSizeContent? boolean\n---@field Layer? string\n---@field ResetPosition? boolean\n---@field ResetSize? boolean\n---@field ResetContent? boolean\n---@field ResetLayout? boolean\n---@field SizerFilter? table\n---@field CanObstruct? boolean\n---@field Rounding? number\n---@field IsOpen? boolean\n---@field NoSaveSettings? boolean\n---@field ConstrainPosition? boolean\n---@field ShowMinimize? boolean\n---@field ShowScrollbarX? boolean\n---@field ShowScrollbarY? boolean\n\n---@param id string\n---@param options? Slab.BeginWindowOptions\n---@return boolean\nSlab.BeginWindow = function(id, options)\nend\n\nSlab.EndWindow = function()\nend\n\n---@return number, number\nSlab.GetWindowPosition = function()\nend\n\n---@return number, number\nSlab.GetWindowContentSize = function()\nend\n\n---@return number, number\nSlab.GetWindowActiveSize = function()\nend\n\n---@return boolean\nSlab.IsWindowAppearing = function()\nend\n\n---@param id string\nSlab.PushID = function(id)\nend\n\nSlab.PopID = function()\nend\n\n\n---@return table\nSlab.GetStyle = function()\nend\n\n---@param font love.graphics.Font\nSlab.PushFont = function(font)\nend\n\nSlab.PopFont = function()\nend\n\n---@param path string\n---@param set boolean\n---@return table\nSlab.LoadStyle = function(path, set)\nend\n\n---@param name string\n---@return boolean\nSlab.SetStyle = function(name)\nend\n\n---@return table\nSlab.GetStyleNames = function()\nend\n\n---@return string\nSlab.GetCurrentStyleName = function()\nend\n\n---@return boolean\nSlab.BeginMainMenuBar = function()\nend\n\nSlab.EndMainMenuBar = function()\nend\n\n---@param isMainMenuBar boolean\n---@return boolean\nSlab.BeginMenuBar = function(isMainMenuBar)\nend\n\n---@class Slab.BeginMenuOptions\n---@field Enabled? boolean\n\n---@param label string\n---@param options? Slab.BeginMenuOptions\n---@return boolean\nSlab.BeginMenu = function(label, options)\nend\n\nSlab.EndMenu = function()\nend\n\n---@param button number\n---@return boolean\nSlab.BeginContextMenuItem = function(button)\nend\n\n---@param button number\n---@return boolean\nSlab.BeginContextMenuWindow = function(button)\nend\n\nSlab.EndContextMenu = function()\nend\n\n---@class Slab.MenuItemOptions\n---@field Enabled? boolean\n\n---@param label string\n---@param options? Slab.MenuItemOptions\nSlab.MenuItem = function(label, options)\nend\n\n---@class Slab.MenuItemCheckedOptions\n---@field Enabled? boolean\n\n---@param label string\n---@param isChecked boolean\n---@param options? Slab.MenuItemCheckedOptions\n---@return boolean\nSlab.MenuItemChecked = function(label, isChecked, options)\nend\n\n---@param id string\nSlab.OpenDialog = function(id)\nend\n\n---@param id string\n---@param options Slab.BeginWindowOptions\nSlab.BeginDialog = function(id, options)\nend\n\nSlab.EndDialog = function()\nend\n\nSlab.CloseDialog = function()\nend\n\n---@class Slab.MessageBoxOptions\n---@field Button? table\n\n---@param title string\n---@param message string\n---@param options? Slab.MessageBoxOptions\n---@return string\nSlab.MessageBox = function(title, message, options)\nend\n\n---@class Slab.FileDialogOptions\n---@field Directry? string\n---@field Type? string\n---@field Filters? table\n---@field IncludeParent? boolean\n\n---@param options Slab.FileDialogOptions\n---@return table\nSlab.FileDialog = function(options)\nend\n\n---@class Slab.ColorPickerOptions\n---@field Color? table\n\n---@param options Slab.ColorPickerOptions\n---@return table\nSlab.ColorPicker = function(options)\nend\n\n---@param list string | table\nSlab.EnableDocks = function(list)\nend\n\n---@param list string | table\nSlab.DisableDocks = function(list)\nend\n\n---@class Slab.SetDockOptionsOptiond\n---@field NoSaveSettings? boolean\n\n---@param options Slab.SetDockOptionsOptiond\nSlab.SetDockOptions = function(options)\nend\n\n---@param type string\nSlab.WindowToDock = function(type)\nend\n\n---@class Slab.ButtonOptions\n---@field Tooltip? string\n---@field Rounding? number\n---@field Invisible? boolean\n---@field W? number\n---@field H? number\n---@field Disable? boolean\n---@field Image? table\n---@field Color? table\n---@field HoverColor? table\n---@field PressColor? table\n---@field PadX? number\n---@field PadY? number\n---@field VLines? number\n\n---@param label string\n---@param options? Slab.ButtonOptions\n---@return boolean\nSlab.Button = function(label, options)\nend\n\n---@class Slab.RadioButtonOptions\n---@field Index? number\n---@field SelectedInxex? number\n---@field Tooltip? string\n\n---@param label string\n---@param options? Slab.RadioButtonOptions\n---@return boolean\nSlab.RadioButton = function(label, options)\nend\n\n---@class Slab.TextOptions\n---@field Color? table\n---@field Pad? number\n---@field IsSelectable? boolean\n---@field IsSelectableTextOnly? boolean\n---@field IsSelected? boolean\n---@field SleectOnHover? boolean\n---@field HoverColor? table\n\n---@param label string\n---@param options? Slab.TextOptions\n---@return boolean\nSlab.Text = function(label, options)\nend\n\n---@param label string\n---@param options Slab.TextOptions\n---@return boolean\nSlab.TextSelectable = function(label, options)\nend\n\n---@class Slab.TextfOptions\n---@field Color table\n---@field W number\n---@field Align \"left\" | \"center\" | \"right\"\n\n---@param label string\n---@param options Slab.TextOptions\nSlab.Textf = function(label, options)\nend\n\n---@param label string\n---@return number, number\nSlab.GetTextSize = function(label)\nend\n\n---@param label string\n---@return number\nSlab.GetTextWidth = function(label)\nend\n\n---@return number\nSlab.GetTextHeight = function()\nend\n\n---@class Slab.CheckBoxOptions\n---@field Tooltip? string\n---@field Id? string\n---@field Rounding? number\n---@field Size? number\n---@field Disable? boolean\n\n---@param enabled boolean\n---@param label string\n---@param options? Slab.CheckBoxOptions\n---@return boolean\nSlab.CheckBox = function(enabled, label, options)\nend\n\n---@class Slab.InputOptions\n---@field Tooltip? string\n---@field ReturnOnTexy? boolean\n---@field Text? string | number\n---@field TextColor? table\n---@field BgColor? table\n---@field SelectColor? table\n---@field SelectOnFocus? boolean\n---@field NumbersOnly? boolean\n---@field W? number\n---@field H? number\n---@field ReadOnly? boolean\n---@field Align? \"left\" | \"center\" | \"right\"\n---@field Rounding? number\n---@field MinNumber? number\n---@field MaxNumber? number\n---@field MultiLine? boolean\n---@field MultiLineW? number\n---@field Highlight? table\n---@field Step? number\n---@field NoDrag? boolean\n---@field UseSlider? boolean\n---@field IsPassword? boolean\n---@field PasswordChar? string\n\n---@param id string\n---@param options? Slab.InputOptions\n---@return boolean\nSlab.Input = function(id, options)\nend\n\n---@param id string\n---@param value number\n---@param min number\n---@param max number\n---@param step number\n---@param options Slab.InputOptions\nSlab.InputNumberDrag = function(id, value, min, max, step, options)\nend\n\n---@return string\nSlab.GetInputText = function()\nend\n\n---@return number\nSlab.GetInputNumber = function()\nend\n\n---@return number, number, number\nSlab.GetInputCursorPos = function()\nend\n\n---@param id string\n---@return boolean\nSlab.IsInputFocused = function(id)\nend\n\n---@return boolean\nSlab.IsAnyInputFocused = function()\nend\n\n---@param id string\nSlab.SetInputFocus = function(id)\nend\n\n---@param pos number\nSlab.SetInputCursorPos = function(pos)\nend\n\n---@param column number\n---@param line number\nSlab.SetInputCursorPosLine = function(column, line)\nend\n\n---@class Slab.BeginTreeOptions\n---@field Label? string\n---@field Tooltip? string\n---@field IsLeaf? boolean\n---@field OpenWithHighlight? boolean\n---@field icon? table\n---@field IsSelected? boolean\n---@field IsOpen? boolean\n---@field NoSaveSettings? boolean\n\n---@param id string\n---@param options? Slab.BeginTreeOptions\n---@return boolean\nSlab.BeginTree = function(id, options)\nend\n\nSlab.EndTree = function()\nend\n\n---@class Slab.BeginComboBoxOptions\n---@field Tooltip? string\n---@field Selected? string\n---@field W? number\n---@field Rounding? number\n\n---@param id string\n---@param options? Slab.BeginComboBoxOptions\n---@return boolean\nSlab.BeginComboBox = function(id, options)\nend\n\nSlab.EndComboBox = function()\nend\n\n---@class Slab.ImageOptions\n---@field Image? love.graphics.Image\n---@field Path? string\n---@field Rotation? number\n---@field Scale? number\n---@field ScaleY? number\n---@field ScaleX? number\n---@field Color? table\n---@field SubX? number\n---@field SubY? number\n---@field SubW? number\n---@field SubH? number\n---@field WrapX? string\n---@field WrapY? string\n---@field UseOutline? boolean\n---@field OutlineColor? table\n---@field OutlineWidth? number\n---@field W? number\n---@field H? number\n\n---@param id string\n---@param options? Slab.ImageOptions\nSlab.Image = function(id, options)\nend\n\n---@class Slab.BeginListBoxOptions\n---@field W? number\n---@field H? number\n---@field Clear? boolean\n---@field Rounding? number\n---@field StretchW? boolean\n---@field StretchH? boolean\n\n---@param id string\n---@param options? Slab.BeginListBoxOptions\nSlab.BeginListBox = function(id, options)\nend\n\nSlab.EndListBox = function()\nend\n\n---@class Slab.BeginListBoxItemOptions\n---@field Selected? boolean\n\n---@param id string\n---@param options? Slab.BeginListBoxItemOptions\nSlab.BeginListBoxItem = function(id, options)\nend\n\n---@param button number\n---@param isDoubleClick boolean\nSlab.IsListBoxItemClicked = function(button, isDoubleClick)\nend\n\nSlab.EndListBoxItem = function()\nend\n\n---@class Slab.RectangleOptions\n---@field Mode? string\n---@field W? number\n---@field H? number\n---@field Color? table\n---@field Rounding? number | table\n---@field Outline? boolean\n---@field OutlineColor? table\n---@field Segment? number\n\n---@param options? Slab.RectangleOptions\nSlab.Rectangle = function(options)\nend\n\n---@class Slab.CircleOptions\n---@field Mode? string\n---@field Radius? number\n---@field Color? table\n---@field Segment? number\n\n---@param options? Slab.CircleOptions\nSlab.Circle = function(options)\nend\n\n---@class Slab.TriangleOptions\n---@field Mode? string\n---@field Radius? number\n---@field Rotation? number\n---@field Color? table\n\n---@param options? Slab.TriangleOptions\nSlab.Triangle = function(options)\nend\n\n---@class Slab.LineOptions\n---@field Width? number\n---@field Color? table\n\n---@param x2 number\n---@param y2 number\n---@param options? Slab.LineOptions\nSlab.Line = function(x2, y2, options)\nend\n\n---@class Slab.CurveOptions\n---@field Color? table\n---@field Depth? number\n\n---@param point table\n---@param options? Slab.CurveOptions\nSlab.Curve = function(point, options)\nend\n\n---@return number\nSlab.GetCurveControlPointCount = function()\nend\n\n---@return number, number\nSlab.GetCurveControlPoint = function()\nend\n\n---@class Slab.EvaluateCurveOptions\n---@field LocalSpace? boolean\n\n---@param time number\n---@param options? Slab.EvaluateCurveOptions\n---@return number, number\nSlab.EvaluateCurve = function(time, options)\nend\n\n---@param options? Slab.EvaluateCurveOptions\n---@return number, number\nSlab.EvaluateCurveMouse = function(options)\nend\n\n---@class Slab.PolygonOptions\n---@field Color? table\n---@field Mode? string\n\n---@param points table\n---@param options? Slab.PolygonOptions\nSlab.Polygon = function(points, options)\nend\n\n---@param number number\nSlab.SetScrollSpeed = function(number)\nend\n\n---@return number\nSlab.GetScrollSpeed = function()\nend\n\n---@class Slab.SeparatorOptions\n---@field IncudeBorders? boolean\n---@field H? number\n---@field Thickness? number\n\n---@param options? Slab.SeparatorOptions\nSlab.Separator = function(options)\nend\n\n---@param shader love.graphics.Shader\nSlab.PushShader = function(shader)\nend\n\nSlab.PopShader = function()\nend\n\n---@param table table\n---@param options table\n---@param fallback table\nSlab.Properties = function(table, options, fallback)\nend\n\n---@param n? number\nSlab.NewLine = function(n)\nend\n\n---@class Slab.SameLineOptions\n---@field Pad? number\n---@field CenterY? boolean\n\n---@param options? Slab.SameLineOptions\nSlab.SameLine = function(options)\nend\n\n---@class Slab.SetCursorPosOptions\n---@field Absolute? boolean\n\n---@param x number\n---@param y number\n---@param options Slab.SetCursorPosOptions\nSlab.SetCursorPos = function(x, y, options)\nend\n\n---@param width number\nSlab.Indent = function(width)\nend\n\n---@param width number\nSlab.UnIndent = function(width)\nend\n\n---@param button number | 1 | 2 | 3\n---@return boolean\nSlab.IsMouseDown = function(button)\nend\n\n---@param button number | 1 | 2 | 3\n---@return boolean\nSlab.IsMouseClicked = function(button)\nend\n\n---@param button number | 1 | 2 | 3\n---@return boolean\nSlab.IsMouseReleased = function(button)\nend\n\n---@param button number | 1 | 2 | 3\n---@return boolean\nSlab.IsMouseDoubleClicked = function(button)\nend\n\n---@param button number | 1 | 2 | 3\n---@return boolean\nSlab.IsMouseDragging = function(button)\nend\n\n---@return number, number\nSlab.GetMousePosition = function()\nend\n\n---@return number, number\nSlab.GetMousePositionWindow = function()\nend\n\n---@return number, number\nSlab.GetMouseDelta = function()\nend\n\n---@alias CursorCustomType\n---| \"arrow\"\n---| \"sizewe\"\n---| \"sizens\"\n---| \"sizenesw\"\n---| \"ibeam\"\n---| \"hand\"\n\n---@param type CursorCustomType\n---@param image love.graphics.Image\n---@param quad love.graphics.Quad\nSlab.SetCustomMouseCursor = function(type, image, quad)\nend\n\n---@param type CursorCustomType\nSlab.ClearCustomMouseCursor = function(type)\nend\n\n---@return boolean\nSlab.IsControlHovered = function()\nend\n\n---@param button number | 1 | 2 | 3\n---@return boolean\nSlab.IsControlClicked = function(button)\nend\n\n---@return number, number\nSlab.GetControlSize = function()\nend\n\n---@return boolean\nSlab.IsVoidHovered = function()\nend\n\n---@param button number | 1 | 2 | 3\n---@return boolean\nSlab.IsVoidClicked = function(button)\nend\n\n---@param key string\n---@return boolean\nSlab.IsKeyDown = function(key)\nend\n\n---@param key string\n---@return boolean\nSlab.IsKeyPressed = function(key)\nend\n\n---@param key string\n---@return boolean\nSlab.IsKeyReleased = function(key)\nend\n\n---@class Slab.BeginLayoutOptions\n---@field AlignX? \"left\" | \"center\" | \"right\"\n---@field AlignY? \"top\" | \"center\" | \"botton\"\n---@field AlignRowY? \"top\" | \"center\" | \"botton\"\n---@field Ignore? boolean\n---@field ExpandW? boolean\n---@field ExpandH? boolean\n---@field AnchorX? boolean\n---@field AnchorT? boolean\n---@field Columns? number\n\n---@param id string\n---@param options? Slab.BeginLayoutOptions\nSlab.BeginLayout = function(id, options)\nend\n\nSlab.EndLayout = function()\nend\n\n---@param index number\nSlab.SetLayoutColumn = function(index)\nend\n\n---@return number, number\nSlab.GetLayoutSize = function()\nend\n\n---@return number\nSlab.GetCurrentColumnIndex = function()\nend\n\n---@param args table\nSlab.Initialize = function(args)\nend\n\n---@return string\nSlab.GetVersion = function()\nend\n\n---@return string\nSlab.GetLoveVersion = function()\nend\n\n---@param dt number\nSlab.Update = function(dt)\nend\n\nSlab.Draw = function()\nend\n\n---@param isVerbose boolean\nSlab.SetVerbose = function(isVerbose)\nend\n\n---@param name string\n---@param category string\n---@return number\nSlab.BeginStat = function(name, category)\nend\n\n---@param number number\nSlab.EndStat = function(number)\nend\n\n---@param enable boolean\nSlab.EnableStats = function(enable)\nend\n\n---@return boolean\nSlab.IsStatsEnabled = function()\nend\n\n---@return table\nSlab.GetStats = function()\nend\n\n---@param loveStats table\n---@return table\nSlab.CalculateStats = function(loveStats)\nend\n\n---@param path string\nSlab.SetINIStatePath = function(path)\nend\n\n---@return string\nSlab.GetINIStatePath = function()\nend\n\n---@return table\nSlab.GetMassages = function()\nend\n"
  },
  {
    "path": "SlabTest.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Slab = require(SLAB_PATH .. '.Slab')\nlocal SlabDebug = require(SLAB_PATH .. '.SlabDebug')\n\nlocal SlabTest = {}\n\nlocal function DrawOverview()\n\tSlab.Textf(\n\t\t\"Slab is an immediate mode GUI toolkit for the LÖVE 2D framework. This library \" ..\n\t\t\"is designed to allow users to easily add this library to their existing LÖVE 2D projects and \" ..\n\t\t\"quickly create tools to enable them to iterate on their ideas quickly. The user should be able \" ..\n\t\t\"to utilize this library with minimal integration steps and is completely written in Lua and utilizes \" ..\n\t\t\"the LÖVE 2D API. No compiled binaries are required and the user will have access to the source so \" ..\n\t\t\"that they may make adjustments that meet the needs of their own projects and tools. Refer to main.lua \" ..\n\t\t\"and SlabTest.lua for example usage of this library.\\n\\n\" ..\n\t\t\"This window will demonstrate the usage of the Slab library and give an overview of all the supported controls \" ..\n\t\t\"and features.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"The current version of Slab is: \")\n\tSlab.SameLine()\n\tSlab.Text(Slab.GetVersion(), {Color = {0, 1, 0, 1}})\n\n\tSlab.Text(\"The current version of LÖVE is: \")\n\tSlab.SameLine()\n\tSlab.Text(Slab.GetLoveVersion(), {Color = {0, 1, 0, 1}})\n\n\tSlab.Text(\"The current OS is: \")\n\tSlab.SameLine()\n\tSlab.Text(love.system.getOS(), {Color = {0, 1, 0, 1}})\nend\n\nlocal DrawButtons_NumClicked = 0\nlocal DrawButtons_NumClicked_Invisible = 0\nlocal DrawButtons_Enabled = false\nlocal DrawButtons_Hovered = false\n\nlocal function DrawButtons()\n\tSlab.Textf(\"Buttons are simple controls which respond to a user's left mouse click. Buttons will simply return true when they are clicked.\")\n\n\tSlab.NewLine()\n\n\tif Slab.Button(\"Button\") then\n\t\tDrawButtons_NumClicked = DrawButtons_NumClicked + 1\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"You have clicked this button \" .. DrawButtons_NumClicked .. \" time(s).\")\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\"Buttons can be tested for mouse hover with the call to Slab.IsControlHovered right after declaring the button.\")\n\tSlab.Button(DrawButtons_Hovered and \"Hovered\" or \"Not Hovered\", {W = 100})\n\tDrawButtons_Hovered = Slab.IsControlHovered()\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\"Buttons can have a custom width and height.\")\n\tSlab.Button(\"Square\", {W = 75, H = 75})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Buttons can also be invisible so that the designer can implement a custom button but still rely on the \" ..\n\t\t\"button behavior. Below is a an invisible button and a custom rectangle drawn at the same location.\")\n\tlocal X, Y = Slab.GetCursorPos()\n\tSlab.Rectangle({Mode = 'line', W = 50.0, H = 50.0, Color = {1, 1, 1, 1}})\n\tSlab.SetCursorPos(X, Y)\n\n\tif Slab.Button(\"\", {Invisible = true, W = 50.0, H = 50.0}) then\n\t\tDrawButtons_NumClicked_Invisible = DrawButtons_NumClicked_Invisible + 1\n\tend\n\n\tSlab.SameLine({CenterY = true})\n\tSlab.Text(\"Invisible button has been clicked \" .. DrawButtons_NumClicked_Invisible .. \" time(s).\")\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\"Buttons can also be disabled. Click the button below to toggle the status of the neighboring button.\")\n\n\tif Slab.Button(\"Toggle\", { Active = DrawButtons_Enabled }) then\n\t\tDrawButtons_Enabled = not DrawButtons_Enabled\n\tend\n\n\tSlab.SameLine()\n\tSlab.Button(DrawButtons_Enabled and \"Enabled\" or \"Disabled\", {Disabled = not DrawButtons_Enabled})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Buttons can also display images instead of a text label. This can be down through the 'Image' option, which accepts a table \" ..\n\t\t\"where the options are the same as those found in the 'Image' API function.\")\n\tSlab.Button(\"\", {Image = {Path = SLAB_FILE_PATH .. \"/Internal/Resources/Textures/avatar.png\"}})\nend\n\nlocal DrawText_Width = 450.0\nlocal DrawText_Height = 0.0\nlocal DrawText_Alignment = {'left', 'center', 'right', 'justify'}\nlocal DrawText_Alignment_Selected = 'left'\nlocal DrawText_NumClicked = 0\nlocal DrawText_NumClicked_TextOnly = 0\n\nlocal function DrawText()\n\tSlab.Textf(\"Text controls displays text on the current window. Slab currently offers three ways to control the text.\")\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Text(\"The most basic text control is Slab.Text.\")\n\tSlab.Text(\"The color of the text can be controlled with the 'Color' option.\", {Color = {0, 1, 0, 1}})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Text can be formatted using the Slab.Textf API. Formatted text will wrap the text based on the 'W' option. \" ..\n\t\t\"If the 'W' option is not specified, the window's width will be used as the width. Formatted text also has an \" ..\n\t\t\"alignment option. The 'H' option can be used to center the text within a given height.\")\n\n\tSlab.NewLine()\n\tSlab.Text(\"Width\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawText_Width', {Text = tostring(DrawText_Width), NumbersOnly = true, ReturnOnText = false}) then\n\t\tDrawText_Width = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Height\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawText_Height', {Text = tostring(DrawText_Height), NumbersOnly = true, ReturnOnText = false}) then\n\t\tDrawText_Height = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Alignment\")\n\tSlab.SameLine()\n\tif Slab.BeginComboBox('DrawText_Alignment', {Selected = DrawText_Alignment_Selected}) then\n\t\tfor I, V in ipairs(DrawText_Alignment) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawText_Alignment_Selected = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.Textf(\n\t\t\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore \" ..\n\t\t\"et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut \" ..\n\t\t\"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum \" ..\n\t\t\"dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui \" ..\n\t\t\"officia deserunt mollit anim id est laborum.\", {W = DrawText_Width, H = DrawText_Height, Align = DrawText_Alignment_Selected})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Text can also be interacted with using the Slab.TextSelectable function. A background will be \" ..\n\t\t\"rendered when the mouse is hovered over the text and the function will return true when clicked on. \" ..\n\t\t\"The selectable area expands to the width of the window by default. This can be changed to just the text \" ..\n\t\t\"with the 'IsSelectableTextOnly' option.\")\n\n\tSlab.NewLine()\n\tif Slab.TextSelectable(\"This text has been clicked \" .. DrawText_NumClicked .. \" time(s).\") then\n\t\tDrawText_NumClicked = DrawText_NumClicked + 1\n\tend\n\n\tSlab.NewLine()\n\tif Slab.TextSelectable(\"This text has been clicked \" .. DrawText_NumClicked_TextOnly .. \" time(s).\", {IsSelectableTextOnly = true}) then\n\t\tDrawText_NumClicked_TextOnly = DrawText_NumClicked_TextOnly + 1\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Text controls can be configured to contain URL links. When this control is clicked, Slab will open the given URL \" ..\n\t\t\"with the user's default web browser.\")\n\n\tSlab.Text(\"Love 2D\", {URL = \"http://love2d.org\"})\nend\n\nlocal DrawCheckBox_Checked = false\nlocal DrawCheckBox_Checked_NoLabel = false\n\nlocal function DrawCheckBox()\n\tSlab.Textf(\n\t\t\"Check boxes are controls that will display an empty box with an optional label. The function will \" ..\n\t\t\"return true if the user has clicked on the box. The code is then responsible for updating the checked \" ..\n\t\t\"flag to be passed back into the function.\")\n\n\tSlab.NewLine()\n\tif Slab.CheckBox(DrawCheckBox_Checked, \"Check Box\") then\n\t\tDrawCheckBox_Checked = not DrawCheckBox_Checked\n\tend\n\n\tSlab.NewLine()\n\tSlab.Text(\"A check box with no label.\")\n\tif Slab.CheckBox(DrawCheckBox_Checked_NoLabel) then\n\t\tDrawCheckBox_Checked_NoLabel = not DrawCheckBox_Checked_NoLabel\n\tend\n\n\tSlab.NewLine()\n\tSlab.Text(\"A disabled check box.\")\n\tSlab.CheckBox(true, \"Disabled\", {Disabled = true})\nend\n\nlocal DrawRadioButton_Selected = 1\n\nlocal function DrawRadioButton()\n\tSlab.Textf(\"Radio buttons offer the user to select one option from a list of options.\")\n\n\tSlab.NewLine()\n\tfor I = 1, 5, 1 do\n\t\tif Slab.RadioButton(\"Option \" .. I, {Index = I, SelectedIndex = DrawRadioButton_Selected}) then\n\t\t\tDrawRadioButton_Selected = I\n\t\tend\n\tend\nend\n\nlocal DrawMenus_Window_Selected = \"Right click and select an option.\"\nlocal DrawMenus_Control_Selected = \"Right click and select an option from a control.\"\nlocal DrawMenus_CheckBox = false\nlocal DrawMenus_ComboBox = {\"Apple\", \"Banana\", \"Pear\", \"Orange\", \"Lemon\"}\nlocal DrawMenus_ComboBox_Selected = \"Apple\"\n\nlocal function DrawContextMenuItem(Label, Button)\n\tif Slab.BeginContextMenuItem(Button) then\n\t\tfor I = 1, 5, 1 do\n\t\t\tlocal MenuLabel = Label .. \" Option \" .. I\n\t\t\tif Slab.MenuItem(MenuLabel) then\n\t\t\t\tDrawMenus_Control_Selected = MenuLabel\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndContextMenu()\n\tend\nend\n\nlocal function DrawMenus()\n\tSlab.Textf(\n\t\t\"Menus are windows that allow users to make a selection from a list of items. Items can be disabled to prevent \" ..\n\t\t\"any interaction but will still be displayed. Below are descriptions of the various menus and how they can be utilized.\")\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"The main menu bar is rendered at the top of the window with menu items being added \" ..\n\t\t\"from left to right. When a menu item is clicked, a context menu is opened below the \" ..\n\t\t\"selected item. Creating the main menu bar can open anywhere in the code after the \" ..\n\t\t\"Slab.Update call. These functions should not be called within a BeginWindow/EndWindow \" ..\n\t\t\"call.\")\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Context menus are menus which are rendered above all other controls to allow the user to make a selection \" ..\n\t\t\"out of a list of items. These can be opened up through the menu bar, or through a right-click \" ..\n\t\t\"action from the user on a given window or control. Menus and menu items make up the context menu \" ..\n\t\t\"and menus can be nested to allow a tree options to be displayed.\")\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"Controls can have their own context menus. Right-click on each control to open up the menu \" ..\n\t\t\"and select an option.\")\n\n\tSlab.NewLine()\n\tSlab.Text(DrawMenus_Control_Selected)\n\tSlab.NewLine()\n\n\tSlab.Button(\"Button\")\n\tDrawContextMenuItem(\"Button\")\n\n\tSlab.Text(\"Text\")\n\tDrawContextMenuItem(\"Text\")\n\n\tif Slab.CheckBox(DrawMenus_CheckBox, \"Check Box\") then\n\t\tDrawMenus_CheckBox = not DrawMenus_CheckBox\n\tend\n\tDrawContextMenuItem(\"Check Box\")\n\n\tSlab.Input('DrawMenus_Input')\n\tDrawContextMenuItem(\"Input\")\n\n\tif Slab.BeginComboBox('DrawMenus_ComboBox', {Selected = DrawMenus_ComboBox_Selected}) then\n\t\tfor I, V in ipairs(DrawMenus_ComboBox) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawMenus_Window_Selected = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\tDrawContextMenuItem(\"Combo Box\")\n\n\tSlab.NewLine()\n\tSlab.Textf(\n\t\t\"Context menu items are usually opened with the right mouse button. This can be changed for context menus to be a differen \" ..\n\t\t\"mouse button. The button below will open a context menu using the left mouse button.\")\n\n\tSlab.NewLine()\n\tSlab.Button(\"Left Mouse\")\n\tDrawContextMenuItem(\"Left Mouse Button\", 1)\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Right-clicking anywhere within this window will open up a context menu. Note that BeginContextMenuWindow \" ..\n\t\t\"must come after all BeginContextMenuItem calls.\")\n\n\tSlab.NewLine()\n\n\tSlab.Textf(DrawMenus_Window_Selected)\n\n\tif Slab.BeginContextMenuWindow() then\n\t\tif Slab.BeginMenu(\"Window Menu 1\") then\n\t\t\tfor I = 1, 5, 1 do\n\t\t\t\tlocal Enabled = I % 2 ~= 0\n\t\t\t\tif Slab.MenuItem(\"Sub Window Option \" .. I, {Enabled = Enabled}) then\n\t\t\t\t\tDrawMenus_Window_Selected = \"Sub Window Option \" .. I\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tSlab.EndMenu()\n\t\tend\n\n\t\tfor I = 1, 5, 1 do\n\t\t\tif Slab.MenuItem(\"Window Option \" .. I) then\n\t\t\t\tDrawMenus_Window_Selected = \"Window Option \" .. I .. \" selected.\"\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndContextMenu()\n\tend\nend\n\nlocal DrawComboBox_Options = {\"England\", \"France\", \"Germany\", \"USA\", \"Canada\", \"Mexico\", \"Japan\", \"South Korea\", \"China\", \"Russia\", \"India\"}\nlocal DrawComboBox_Selected = \"USA\"\nlocal DrawComboBox_Selected_Width = \"USA\"\n\nlocal function DrawComboBox()\n\tSlab.Textf(\n\t\t\"A combo box allows the user to select a single item from a list and display the selected item \" ..\n\t\t\"in the combo box. The list is only visible when the user is interacting with the control.\")\n\n\tSlab.NewLine()\n\n\tif Slab.BeginComboBox('DrawComboBox_One', {Selected = DrawComboBox_Selected}) then\n\t\tfor I, V in ipairs(DrawComboBox_Options) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawComboBox_Selected = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\"A combo box's width can be modified with the 'W' option.\")\n\n\tSlab.NewLine()\n\n\tlocal W, H = Slab.GetWindowActiveSize()\n\tif Slab.BeginComboBox('DrawComboBox_Two', {Selected = DrawComboBox_Selected_Width, W = W}) then\n\t\tfor I, V in ipairs(DrawComboBox_Options) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawComboBox_Selected_Width = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\nend\n\nlocal DrawInput_Basic = \"Hello World\"\nlocal DrawInput_Basic_Return = \"Hello World\"\nlocal DrawInput_Basic_Numbers = 0\nlocal DrawInput_Basic_Numbers_Clamped = 0.5\nlocal DrawInput_Basic_Numbers_Clamped_Min = 0.0\nlocal DrawInput_Basic_Numbers_Clamped_Max = 1.0\nlocal DrawInput_Basic_Numbers_Clamped_Step = 0.01\nlocal DrawInput_Basic_Numbers_NoDrag = 50\nlocal DrawInput_Basic_Numbers_Slider = 50\nlocal DrawInput_Basic_Numbers_Slider_Handle = 50\nlocal DrawInput_Basic_Numbers_Slider_Min = 0\nlocal DrawInput_Basic_Numbers_Slider_Max = 100\nlocal DrawInput_MultiLine =\n[[\nfunction Foo()\n\tprint(\"Bar\")\nend\n\nThe quick brown fox jumped over the lazy dog.]]\nlocal DrawInput_MultiLine_Width = math.huge\nlocal DrawInput_CursorPos = 0\nlocal DrawInput_CursorColumn = 0\nlocal DrawInput_CursorLine = 0\nlocal DrawInput_Highlight_Text =\n[[\nfunction Hello()\n\tprint(\"World\")\nend]]\nlocal DrawInput_Highlight_Table = {\n\t['function'] = {1, 0, 0, 1},\n\t['end'] = {0, 0, 1, 1}\n}\nlocal DrawInput_Highlight_Table_Modify = nil\n\nlocal function DrawInput()\n\tSlab.Textf(\n\t\t\"The input control allows the user to enter in text into an input box. This control is similar \" ..\n\t\t\"to input boxes found in other applications. These controls are set up to handle UTF8 characters.\")\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"The first example is very simple. An Input control is declared and the resulting text is captured if \" ..\n\t\t\"the function returns true. By default, the function will return true on any text that is entered.\")\n\n\tif Slab.Input('DrawInput_Basic', {Text = DrawInput_Basic}) then\n\t\tDrawInput_Basic = Slab.GetInputText()\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"The return behavior can be modified so that the function will only return true if the Enter/Return \" ..\n\t\t\"key is pressed. If the control loses focus without the Enter/Return key pressed, then the text will \" ..\n\t\t\"revert back to what it was before.\")\n\n\tif Slab.Input('DrawInput_Basic_Return', {Text = DrawInput_Basic_Return, ReturnOnText = false}) then\n\t\tDrawInput_Basic_Return = Slab.GetInputText()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Input controls can be configured to only take numeric values. Input controls that are configured this way \" ..\n\t\t\"will allow the user to click and drag the control to alter the value by default. The user must double-click the \"..\n\t\t\"control to manually enter a valid number.\")\n\n\tif Slab.Input('DrawInput_Basic_Numbers', {Text = tostring(DrawInput_Basic_Numbers), NumbersOnly = true}) then\n\t\tDrawInput_Basic_Numbers = Slab.GetInputNumber()\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"These numeric controls can also have min and/or max values set. Below is an example where the \" ..\n\t\t\"numeric input control is clamped from 0.0 to 1.0. The drag step is also modified to be smaller for more precision.\")\n\n\tSlab.Text(\"Min\")\n\tSlab.SameLine()\n\tlocal DrawInput_Basic_Numbers_Clamped_Min_Options =\n\t{\n\t\tText = tostring(DrawInput_Basic_Numbers_Clamped_Min),\n\t\tMaxNumber = DrawInput_Basic_Numbers_Clamped_Max,\n\t\tStep = DrawInput_Basic_Numbers_Clamped_Step,\n\t\tNumbersOnly = true,\n\t\tW = 50\n\t}\n\tif Slab.Input('DrawInput_Basic_Numbers_Clamped_Min', DrawInput_Basic_Numbers_Clamped_Min_Options) then\n\t\tDrawInput_Basic_Numbers_Clamped_Min = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Max\")\n\tSlab.SameLine()\n\tlocal DrawInput_Basic_Numbers_Clamped_Max_Options =\n\t{\n\t\tText = tostring(DrawInput_Basic_Numbers_Clamped_Max),\n\t\tMinNumber = DrawInput_Basic_Numbers_Clamped_Min,\n\t\tStep = DrawInput_Basic_Numbers_Clamped_Step,\n\t\tNumbersOnly = true,\n\t\tW = 50\n\t}\n\tif Slab.Input('DrawInput_Basic_Numbers_Clamped_Max', DrawInput_Basic_Numbers_Clamped_Max_Options) then\n\t\tDrawInput_Basic_Numbers_Clamped_Max = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Step\")\n\tSlab.SameLine()\n\tlocal DrawInput_Basic_Numbers_Clamped_Step_Options =\n\t{\n\t\tText = tostring(DrawInput_Basic_Numbers_Clamped_Step),\n\t\tMinNumber = 0,\n\t\tStep = 0.01,\n\t\tNumbersOnly = true,\n\t\tW = 50\n\t}\n\tif Slab.Input('DrawInput_Basic_Numbers_Clamped_Step', DrawInput_Basic_Numbers_Clamped_Step_Options) then\n\t\tDrawInput_Basic_Numbers_Clamped_Step = Slab.GetInputNumber()\n\tend\n\n\tlocal DrawInput_Basic_Numbers_Clamped_Options =\n\t{\n\t\tText = tostring(DrawInput_Basic_Numbers_Clamped),\n\t\tNumbersOnly = true,\n\t\tMinNumber = DrawInput_Basic_Numbers_Clamped_Min,\n\t\tMaxNumber = DrawInput_Basic_Numbers_Clamped_Max,\n\t\tStep = DrawInput_Basic_Numbers_Clamped_Step\n\t}\n\tif Slab.Input('DrawInput_Basic_Numbers_Clamped', DrawInput_Basic_Numbers_Clamped_Options) then\n\t\tDrawInput_Basic_Numbers_Clamped = Slab.GetInputNumber()\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"The click and drag functionality of numeric controls can also be disabled. This will make the input control behave like a \" ..\n\t\t\"standard text input control.\")\n\n\tif Slab.Input('DrawInput_Basic_Numbers_NoDrag', {Text = tostring(DrawInput_Basic_Numbers_NoDrag), NumbersOnly = true, NoDrag = true}) then\n\t\tDrawInput_Basic_Numbers_NoDrag = Slab.GetInputNumber()\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"A slider can also be used for these numeric input controls. When configured this way, the value is altered based on where the \" ..\n\t\t\"user clicks and drags inside the control.\")\n\n\tSlab.Text(\"Min\")\n\tSlab.SameLine()\n\tif Slab.InputNumberDrag('DrawInput_Basic_Numbers_Slider_Min', DrawInput_Basic_Numbers_Slider_Min, nil, DrawInput_Basic_Numbers_Slider_Max, nil, {W = 50}) then\n\t\tDrawInput_Basic_Numbers_Slider_Min = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Max\")\n\tSlab.SameLine()\n\tif Slab.InputNumberDrag('DrawInput_Basic_Numbers_Slider_Max', DrawInput_Basic_Numbers_Slider_Max, DrawInput_Basic_Numbers_Slider_Min, nil, nil, {W = 50}) then\n\t\tDrawInput_Basic_Numbers_Slider_Max = Slab.GetInputNumber()\n\tend\n\n\tif Slab.InputNumberSlider('DrawInput_Basic_Numbers_Slider', DrawInput_Basic_Numbers_Slider, DrawInput_Basic_Numbers_Slider_Min, DrawInput_Basic_Numbers_Slider_Max) then\n\t\tDrawInput_Basic_Numbers_Slider = Slab.GetInputNumber()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Text(\"Sliders can also be drawn with a handle\")\n\tif Slab.InputNumberSlider('DrawInput_Basic_Numbers_Slider_Handle', DrawInput_Basic_Numbers_Slider_Handle, DrawInput_Basic_Numbers_Slider_Min, DrawInput_Basic_Numbers_Slider_Max, {DrawSliderAsHandle = true}) then\n\t\tDrawInput_Basic_Numbers_Slider_Handle = Slab.GetInputNumber()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Input controls also allow for multi-line editing using the MultiLine option. The default text wrapping \" ..\n\t\t\"option is set to math.huge, but this can be modified with the MultiLineW option. The example below demonstrates \" ..\n\t\t\"how to set up a multi-line input control and shows how the size of the control can be modified.\")\n\n\tSlab.NewLine()\n\tSlab.Text(\"MultiLineW\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawInput_MultiLine_Width', {Text = tostring(DrawInput_MultiLine_Width), NumbersOnly = true, ReturnOnText = false}) then\n\t\tDrawInput_MultiLine_Width = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Cursor Pos\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawInput_CursorPos', {Text = tostring(DrawInput_CursorPos), NumbersOnly = true, ReturnOnText = false, MinNumber = 0, W = 75}) then\n\t\tDrawInput_CursorPos = Slab.GetInputNumber()\n\t\tSlab.SetInputFocus('DrawInput_MultiLine')\n\t\tSlab.SetInputCursorPos(DrawInput_CursorPos)\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Column\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawInput_CursorColumn', {Text = tostring(DrawInput_CursorColumn), NumbersOnly = true, ReturnOnText = false, MinNumber = 0, W = 75}) then\n\t\tDrawInput_CursorColumn = Slab.GetInputNumber()\n\t\tSlab.SetInputFocus('DrawInput_MultiLine')\n\t\tSlab.SetInputCursorPosLine(DrawInput_CursorColumn, DrawInput_CursorLine)\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Line\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawInput_CursorLine', {Text = tostring(DrawInput_CursorLine), NumbersOnly = true, ReturnOnText = false, MinNumber = 0, W = 75}) then\n\t\tDrawInput_CursorLine = Slab.GetInputNumber()\n\t\tSlab.SetInputFocus('DrawInput_MultiLine')\n\t\tSlab.SetInputCursorPosLine(DrawInput_CursorColumn, DrawInput_CursorLine)\n\tend\n\n\tlocal W, H = Slab.GetWindowActiveSize()\n\n\tif Slab.Input('DrawInput_MultiLine', {Text = DrawInput_MultiLine, MultiLine = true, MultiLineW = DrawInput_MultiLine_Width, W = W, H = 150.0}) then\n\t\tDrawInput_MultiLine = Slab.GetInputText()\n\tend\n\n\tif Slab.IsInputFocused('DrawInput_MultiLine') then\n\t\tDrawInput_CursorPos, DrawInput_CursorColumn, DrawInput_CursorLine = Slab.GetInputCursorPos()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"The input control also offers a way to highlight certain words with a custom color. Below is a list of keywords and the color used to define the word.\")\n\n\tSlab.NewLine()\n\n\tlocal TextW, TextH = Slab.GetTextSize(\"\")\n\n\tfor K, V in pairs(DrawInput_Highlight_Table) do\n\t\tif Slab.Input('DrawInput_Highlight_Table_' .. K, {Text = K, ReturnOnText = false}) then\n\t\t\tDrawInput_Highlight_Table[K] = nil\n\t\t\tK = Slab.GetInputText()\n\t\t\tDrawInput_Highlight_Table[K] = V\n\t\tend\n\n\t\tSlab.SameLine({Pad = 20.0})\n\t\tSlab.Rectangle({W = 50, H = TextH, Color = V})\n\n\t\tif Slab.IsControlClicked() then\n\t\t\tDrawInput_Highlight_Table_Modify = K\n\t\tend\n\n\t\tSlab.SameLine({Pad = 20.0})\n\n\t\tif Slab.Button(\"Delete\", {H = TextH}) then\n\t\t\tDrawInput_Highlight_Table[K] = nil\n\t\tend\n\tend\n\n\tif Slab.Button(\"Add\") then\n\t\tDrawInput_Highlight_Table['new'] = {1, 0, 0, 1}\n\tend\n\n\tif DrawInput_Highlight_Table_Modify ~= nil then\n\t\tlocal Result = Slab.ColorPicker({Color = DrawInput_Highlight_Table[DrawInput_Highlight_Table_Modify]})\n\n\t\tif Result.Button ~= 0 then\n\t\t\tif Result.Button == 1 then\n\t\t\t\tDrawInput_Highlight_Table[DrawInput_Highlight_Table_Modify] = Result.Color\n\t\t\tend\n\n\t\t\tDrawInput_Highlight_Table_Modify = nil\n\t\tend\n\tend\n\n\tSlab.NewLine()\n\n\tif Slab.Input('DrawInput_Highlight', {Text = DrawInput_Highlight_Text, MultiLine = true, Highlight = DrawInput_Highlight_Table, W = W, H = 150.0}) then\n\t\tDrawInput_Highlight_Text = Slab.GetInputText()\n\tend\nend\n\nlocal DrawImage_Path = SLAB_FILE_PATH .. \"/Internal/Resources/Textures/avatar.png\"\nlocal DrawImage_Path_Icons = SLAB_FILE_PATH .. \"/Internal/Resources/Textures/Icons.png\"\nlocal DrawImage_Color = {1, 0, 0, 1}\nlocal DrawImage_Color_Edit = false\nlocal DrawImage_Scale = 1.0\nlocal DrawImage_Scale_X = 1.0\nlocal DrawImage_Scale_Y = 1.0\nlocal DrawImage_Power = false\nlocal DrawImage_Power_Hovered = false\nlocal DrawImage_Power_On = {0, 1, 0, 1}\nlocal DrawImage_Power_Off = {1, 0, 0, 1}\nlocal DrawImage_Icon_X = 0\nlocal DrawImage_Icon_Y = 0\nlocal DrawImage_Icon_Move = false\nlocal DrawImage_UseOutline = true\nlocal DrawImage_OutlineWidth = 1\nlocal DrawImage_OutlineColor = {0, 0, 0, 1}\nlocal DrawImage_OutlineColor_Edit = false\n\nlocal function DrawImage()\n\tSlab.Textf(\n\t\t\"Images can be drawn within windows and react to user interaction. A path to an image can be specified through the options of \" ..\n\t\t\"the Image function. If this is done, Slab will manage the image resource and will use the path as a key to the resource.\")\n\n\tSlab.Image('DrawImage_Basic', {Path = DrawImage_Path})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"An image's color can be modified with the 'Color' option.\")\n\n\tif Slab.Button(\"Change Color\") then\n\t\tDrawImage_Color_Edit = true\n\tend\n\n\tif DrawImage_Color_Edit then\n\t\tlocal Result = Slab.ColorPicker({Color = DrawImage_Color})\n\n\t\tif Result.Button ~= 0 then\n\t\t\tDrawImage_Color_Edit = false\n\n\t\t\tif Result.Button == 1 then\n\t\t\t\tDrawImage_Color = Result.Color\n\t\t\tend\n\t\tend\n\tend\n\n\tSlab.Image('DrawImage_Color', {Path = DrawImage_Path, Color = DrawImage_Color})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"An outline can be applied to the image. The color and width of the outline is customizable.\")\n\n\tSlab.Text(\"Use Outline\")\n\tSlab.SameLine()\n\tif Slab.CheckBox(DrawImage_UseOutline) then\n\t\tDrawImage_UseOutline = not DrawImage_UseOutline\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Width\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawImage_OutlineWidth', {Text = DrawImage_OutlineWidth, NumbersOnly = true, ReturnOnText = false, MinNumber = 1}) then\n\t\tDrawImage_OutlineWidth = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tif Slab.Button(\"Color\") then\n\t\tDrawImage_OutlineColor_Edit = true\n\tend\n\n\tif DrawImage_OutlineColor_Edit then\n\t\tlocal Result = Slab.ColorPicker({Color = DrawImage_OutlineColor})\n\n\t\tif Result.Button ~= 0 then\n\t\t\tDrawImage_OutlineColor_Edit = false\n\n\t\t\tif Result.Button == 1 then\n\t\t\t\tDrawImage_OutlineColor = Result.Color\n\t\t\tend\n\t\tend\n\tend\n\n\tSlab.Image('DrawImage_Outline', {Path = DrawImage_Path, UseOutline = DrawImage_UseOutline, OutlineW = DrawImage_OutlineWidth, OutlineColor = DrawImage_OutlineColor})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"There is an option to modify the scale of an image. The scale can both be affected \" ..\n\t\t\"on the X or Y axis.\")\n\n\tSlab.Text(\"Scale\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawImage_Scale', {Text = tostring(DrawImage_Scale), NumbersOnly = true, ReturnOnText = false, W = 75}) then\n\t\tDrawImage_Scale = Slab.GetInputNumber()\n\t\tDrawImage_Scale_X = DrawImage_Scale\n\t\tDrawImage_Scale_Y = DrawImage_Scale\n\tend\n\n\tSlab.SameLine({Pad = 6.0})\n\tSlab.Text(\"Scale X\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawImage_Scale_X', {Text = tostring(DrawImage_Scale_X), NumbersOnly = true, ReturnOnText = false, W = 75}) then\n\t\tDrawImage_Scale_X = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine({Pad = 6.0})\n\tSlab.Text(\"Scale Y\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawImage_Scale_Y', {Text = tostring(DrawImage_Scale_Y), NumbersOnly = true, ReturnOnText = false, W = 75}) then\n\t\tDrawImage_Scale_Y = Slab.GetInputNumber()\n\tend\n\n\tSlab.Image('DrawImage_Scale', {Path = DrawImage_Path, ScaleX = DrawImage_Scale_X, ScaleY = DrawImage_Scale_Y})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Images can also have interactions through the control API. The left image will change when the mouse is hovered \" ..\n\t\t\"while the right image will change on click.\")\n\n\tSlab.Image('DrawImage_Hover', {Path = DrawImage_Path, Color = DrawImage_Power_Hovered and DrawImage_Power_On or DrawImage_Power_Off})\n\tDrawImage_Power_Hovered = Slab.IsControlHovered()\n\n\tSlab.SameLine({Pad = 12.0})\n\tSlab.Image('DrawImage_Click', {Path = DrawImage_Path, Color = DrawImage_Power and DrawImage_Power_On or DrawImage_Power_Off})\n\tif Slab.IsControlClicked() then\n\t\tDrawImage_Power = not DrawImage_Power\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"A sub region can be defined to draw a section of an image. Move the rectangle around and observe the image on the right.\")\n\n\tlocal X, Y = Slab.GetCursorPos()\n\tlocal AbsX, AbsY = Slab.GetCursorPos({Absolute = true})\n\tSlab.Image('DrawImage_Icons', {Path = DrawImage_Path_Icons})\n\tif Slab.IsControlClicked() then\n\t\tlocal MouseX, MouseY = Slab.GetMousePositionWindow()\n\t\tlocal Left = AbsX + DrawImage_Icon_X\n\t\tlocal Right = Left + 50.0\n\t\tlocal Top = AbsY + DrawImage_Icon_Y\n\t\tlocal Bottom = Top + 50.0\n\t\tif Left <= MouseX and MouseX <= Right and\n\t\t\tTop <= MouseY and MouseY <= Bottom then\n\t\t\tDrawImage_Icon_Move = true\n\t\tend\n\tend\n\n\tif Slab.IsMouseReleased() then\n\t\tDrawImage_Icon_Move = false\n\tend\n\n\tlocal W, H = Slab.GetControlSize()\n\n\tif DrawImage_Icon_Move then\n\t\tlocal DeltaX, DeltaY = Slab.GetMouseDelta()\n\t\tDrawImage_Icon_X = math.max(DrawImage_Icon_X + DeltaX, 0.0)\n\t\tDrawImage_Icon_X = math.min(DrawImage_Icon_X, W - 50.0)\n\n\t\tDrawImage_Icon_Y = math.max(DrawImage_Icon_Y + DeltaY, 0.0)\n\t\tDrawImage_Icon_Y = math.min(DrawImage_Icon_Y, H - 50.0)\n\tend\n\n\tSlab.SetCursorPos(X + DrawImage_Icon_X, Y + DrawImage_Icon_Y)\n\tSlab.Rectangle({Mode = 'line', Color = {0, 0, 0, 1}, W = 50.0, H = 50.0})\n\n\tSlab.SetCursorPos(X + W + 12.0, Y)\n\tSlab.Image('DrawImage_Icons_Region', {\n\t\tPath = DrawImage_Path_Icons,\n\t\tSubX = DrawImage_Icon_X,\n\t\tSubY = DrawImage_Icon_Y,\n\t\tSubW = 50.0,\n\t\tSubH = 50.0\n\t})\nend\n\nlocal DrawCursor_NewLines = 1\nlocal DrawCursor_SameLinePad = 4.0\nlocal DrawCursor_X = nil\nlocal DrawCursor_Y = nil\nlocal DrawCursor_Indent = 14\n\nlocal function DrawCursor()\n\tSlab.Textf(\n\t\t\"Slab offers a way to manage the drawing of controls through the cursor. Whenever a control is used, the cursor is \"..\n\t\t\"automatically advanced based on the size of the control. By default, cursors are advanced vertically downward based \" ..\n\t\t\"on the control's height. However, functions are provided to move the cursor back up to the previous line or create \" ..\n\t\t\"an empty line to advance the cursor downward.\")\n\n\tfor I = 1, DrawCursor_NewLines, 1 do\n\t\tSlab.NewLine()\n\tend\n\n\tSlab.Textf(\n\t\t\"There is a new line between this text and the above description. Modify the number of new lines using the \" ..\n\t\t\"input box below.\")\n\tif Slab.Input('DrawCursor_NewLines', {Text = tostring(DrawCursor_NewLines), NumbersOnly = true, ReturnOnText = false, MinNumber = 0}) then\n\t\tDrawCursor_NewLines = Slab.GetInputNumber()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Using the SameLine function, controls can be layed out on a single line with additional padding. Below are two buttons on \" ..\n\t\t\"the same line with some padding. Use the input field below to modify the padding.\")\n\tSlab.Button(\"One\")\n\tSlab.SameLine({Pad = DrawCursor_SameLinePad})\n\tSlab.Button(\"Two\")\n\tif Slab.Input('DrawCursor_SameLinePad', {Text = tostring(DrawCursor_SameLinePad), NumbersOnly = true, ReturnOnText = false}) then\n\t\tDrawCursor_SameLinePad = Slab.GetInputNumber()\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"The SameLine function can also vertically center the next item based on the previous control. This is useful for labeling \" ..\n\t\t\"items that are much bigger than the text such as images.\")\n\tSlab.Image('DrawCursor_Image', {Path = DrawImage_Path})\n\tSlab.SameLine({CenterY = true})\n\tSlab.Text(\"This text is centered with respect to the previous image.\")\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Slab offers functions to retrieve and set the cursor position. The GetCursorPos function will return the cursor position \" ..\n\t\t\"relative to the current window. An option can be passed to retrieve the absolute position of the cursor with respect \" ..\n\t\t\"to the viewport.\")\n\n\tlocal X, Y = Slab.GetCursorPos()\n\tSlab.Text(\"Cursor X: \" .. X)\n\tSlab.SameLine()\n\tSlab.Text(\"Cursor Y: \" .. Y)\n\n\tlocal AbsX, AbsY = Slab.GetCursorPos({Absolute = true})\n\tSlab.Text(\"Absolute X: \" .. AbsX)\n\tSlab.SameLine()\n\tSlab.Text(\"Absolute Y: \" .. AbsY)\n\n\tif DrawCursor_X == nil then\n\t\tDrawCursor_X, DrawCursor_Y = Slab.GetCursorPos()\n\tend\n\n\tif Slab.Input('DrawCursor_X', {Text = tostring(DrawCursor_X), NumbersOnly = true, ReturnOnText = false}) then\n\t\tDrawCursor_X = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\n\tif Slab.Input('DrawCursor_Y', {Text = tostring(DrawCursor_Y), NumbersOnly = true, ReturnOnText = false}) then\n\t\tDrawCursor_Y = Slab.GetInputNumber()\n\tend\n\n\tSlab.SetCursorPos(DrawCursor_X, DrawCursor_Y + 30.0)\n\tSlab.Text(\"Use the input fields to move this text.\")\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"There are also API functions to indent or unindent the anchored X position of the cursor. The function takes in a \" ..\n\t\t\"number which represents how far to advance/retreat in pixels from the current anchored position. If no number is \" ..\n\t\t\"given, then the default value is used which is defined by the Indent property located in the Style table. Below \" ..\n\t\t\"are examples of how the Indent/Unindent functions can be used and while the example mainly uses Text controls, these \" ..\n\t\t\"functions can be applied to any controls.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Line 1\")\n\tSlab.Text(\"Line 2\")\n\tSlab.Indent()\n\tSlab.Text(\"Indented Line 1\")\n\tSlab.Text(\"Indented Line 2\")\n\tSlab.Indent()\n\tSlab.Text(\"Indented Line 3\")\n\tSlab.Unindent()\n\tSlab.Text(\"Unindented Line 1\")\n\tSlab.Text(\"Unindented Line 2\")\n\tSlab.Unindent()\n\tSlab.Text(\"Unindented Line 3\")\n\n\tSlab.NewLine()\n\tSlab.Indent(DrawCursor_Indent)\n\tSlab.Text(\"Indent:\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawCursor_Indent', {Text = tostring(DrawCursor_Indent), NumbersOnly = true, ReturnOnText = false}) then\n\t\tDrawCursor_Indent = Slab.GetInputNumber()\n\tend\nend\n\nlocal DrawListBox_Basic_Selected = 1\nlocal DrawListBox_Basic_Count = 10\nlocal DrawListBox_Advanced_Selected = 1\n\nlocal function DrawListBox()\n\tSlab.Textf(\n\t\t\"A list box is a scrollable region that contains a list of elements that a user can interact with. The API is flexible \" ..\n\t\t\"so that each element in the list can be rendered in any way desired. Below are a few examples on different ways a list \" ..\n\t\t\"box can be used.\")\n\n\tSlab.NewLine()\n\n\tlocal Clear = false\n\n\tSlab.Text(\"Count\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawListBox_Basic_Count', {Text = tostring(DrawListBox_Basic_Count), NumbersOnly = true, MinNumber = 0, ReturnOnText = false}) then\n\t\tDrawListBox_Basic_Count = Slab.GetInputNumber()\n\t\tClear = true\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.BeginListBox('DrawListBox_Basic', {Clear = Clear})\n\tfor I = 1, DrawListBox_Basic_Count, 1 do\n\t\tSlab.BeginListBoxItem('DrawListBox_Basic_Item_' .. I, {Selected = I == DrawListBox_Basic_Selected})\n\t\tSlab.Text(\"List Box Item \" .. I)\n\t\tif Slab.IsListBoxItemClicked() then\n\t\t\tDrawListBox_Basic_Selected = I\n\t\tend\n\t\tSlab.EndListBoxItem()\n\tend\n\tSlab.EndListBox()\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Each list box can contain more than just text. Below is an example of list items with a triangle and a label.\")\n\n\tSlab.NewLine()\n\n\tSlab.BeginListBox('DrawListBox_Advanced')\n\tlocal Rotation = 0\n\tfor I = 1, 4, 1 do\n\t\tSlab.BeginListBoxItem('DrawListBox_Advanced_Item_' .. I, {Selected = I == DrawListBox_Advanced_Selected})\n\t\tSlab.Triangle({Radius = 24.0, Rotation = Rotation})\n\t\tSlab.SameLine({CenterY = true})\n\t\tSlab.Text(\"Triangle \" .. I)\n\t\tif Slab.IsListBoxItemClicked() then\n\t\t\tDrawListBox_Advanced_Selected = I\n\t\tend\n\t\tSlab.EndListBoxItem()\n\t\tRotation = Rotation + 90\n\tend\n\tSlab.EndListBox()\nend\n\nlocal DrawTree_Icon_Path = SLAB_FILE_PATH .. \"/Internal/Resources/Textures/Folder.png\"\nlocal DrawTree_Opened_Selected = 1\nlocal DrawTree_Tables = nil\n\nlocal function DrawTree()\n\tSlab.Textf(\n\t\t\"Trees allow data to be viewed in a hierarchy. Trees can also contain leaf nodes which have no children.\")\n\n\tSlab.NewLine()\n\n\tif Slab.BeginTree('DrawTree_Root', {Label = \"Root\"}) then\n\t\tif Slab.BeginTree('DrawTree_Child_1', {Label = \"Child 1\"}) then\n\t\t\tSlab.BeginTree('DrawTree_Child_1_Leaf_1', {Label = \"Leaf 1\", IsLeaf = true})\n\t\t\tSlab.EndTree()\n\t\tend\n\n\t\tSlab.BeginTree('DrawTree_Leaf_1', {Label = \"Leaf 2\", IsLeaf = true})\n\n\t\tif Slab.BeginTree('DrawTree_Child_2', {Label = \"Child 2\"}) then\n\t\t\tSlab.BeginTree('DrawTree_Child_2_Leaf_3', {Label = \"Leaf 3\", IsLeaf = true})\n\t\t\tSlab.EndTree()\n\t\tend\n\n\t\tSlab.EndTree()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"The hot zone of a tree item starts at the expander and extends to the width of the window's content. \" ..\n\t\t\"This can be configured to only allow the tree item to be opened/closed with the expander.\")\n\n\tSlab.NewLine()\n\n\tif Slab.BeginTree('DrawTree_Root_NoHighlight', {Label = \"Root\", OpenWithHighlight = false}) then\n\t\tSlab.BeginTree('DrawTree_Leaf', {Label = \"Leaf\", IsLeaf = true})\n\n\t\tif Slab.BeginContextMenuItem() then\n\t\t\tSlab.MenuItem(\"Leaf Option 1\")\n\t\t\tSlab.MenuItem(\"Leaf Option 2\")\n\n\t\t\tSlab.EndContextMenu()\n\t\tend\n\n\t\tSlab.EndTree()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Tree items can have an icon associated with them. A loaded Image object or path to an image can be \" ..\n\t\t\"specified.\")\n\n\tSlab.NewLine()\n\n\tlocal Icon = {Path = DrawImage_Path_Icons, SubX = 0.0, SubY = 0.0, SubW = 50.0, SubH = 50.0}\n\tif Slab.BeginTree('DrawTree_Root_Icon', {Label = \"Folder\", Icon = Icon}) then\n\t\tSlab.BeginTree('DrawTree_Item_1', {Label = \"Item 1\", IsLeaf = true})\n\t\tSlab.BeginTree('DrawTree_Item_2', {Label = \"Item 2\", IsLeaf = true})\n\n\t\tif Slab.BeginTree('DrawTree_Child_1', {Label = \"Folder\", Icon = Icon}) then\n\t\t\tSlab.BeginTree('DrawTree_Item_3', {Label = \"Item 3\", IsLeaf = true})\n\t\t\tSlab.BeginTree('DrawTree_Item_4', {Label = \"Item 4\", IsLeaf = true})\n\n\t\t\tSlab.EndTree()\n\t\tend\n\n\t\tSlab.EndTree()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"A tree item can be specified to be forced open with the IsOpen option as shown in the example below. The example \" ..\n\t\t\"also shows how tree items can have the selection rectangle permanently rendered.\")\n\n\tSlab.NewLine()\n\n\tif Slab.BeginTree('DrawTree_Root_Opened', {Label = \"Root\", IsOpen = true}) then\n\t\tfor I = 1, 5, 1 do\n\t\t\tSlab.BeginTree('DrawTree_Item_' .. I, {Label = \"Item \" .. I, IsLeaf = true, IsSelected = I == DrawTree_Opened_Selected})\n\n\t\t\tif Slab.IsControlClicked() then\n\t\t\t\tDrawTree_Opened_Selected = I\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndTree()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Tree Ids can also be specified as a table. This allows the user to use a transient table to identify a particular tree \" ..\n\t\t\"element. The tree system has been updated so that any Ids that are used as tables will have the key be removed when the \" ..\n\t\t\"referenced table is garbage collected. This gives the user the ability to create thousands of tree elements and have \" ..\n\t\t\"the tree system keep the number of persistent elements to a minimum. The default label used for these elements will be \" ..\n\t\t\"the memory location of the table, so it is highly recommended to set the 'Label' option for the table. These table \" ..\n\t\t\"elements will also be forced to disable saving settings to disk as the referenced key is a table and is transient. \" ..\n\t\t\"As of version 0.7, this feature is only available for tree controls.\")\n\tSlab.NewLine()\n\tSlab.Textf(\n\t\t\"The example below shows 5 tables that have been instanced and have an associated tree element. The right-click context \" ..\n\t\t\"menu for the root allows for additions to this list. The right-click context menu for each item contains the option \" ..\n\t\t\"to remove the individual element from the list and have that table garbage collected. This removal will also remove the \" ..\n\t\t\"associated tree element.\")\n\n\tSlab.NewLine()\n\n\tif DrawTree_Tables == nil then\n\t\tDrawTree_Tables = {}\n\t\tfor I = 1, 5, 1 do\n\t\t\ttable.insert(DrawTree_Tables, {})\n\t\tend\n\tend\n\n\tlocal RemoveIndex = -1\n\tif Slab.BeginTree('Root', {IsOpen = true}) then\n\t\tif Slab.BeginContextMenuItem() then\n\t\t\tif Slab.MenuItem(\"Add\") then\n\t\t\t\ttable.insert(DrawTree_Tables, {})\n\t\t\tend\n\n\t\t\tSlab.EndContextMenu()\n\t\tend\n\n\t\tfor I, V in ipairs(DrawTree_Tables) do\n\t\t\tSlab.BeginTree(V, {IsLeaf = true})\n\n\t\t\tif Slab.BeginContextMenuItem() then\n\t\t\t\tif Slab.MenuItem(\"Remove\") then\n\t\t\t\t\tRemoveIndex = I\n\t\t\t\tend\n\n\t\t\t\tSlab.EndContextMenu()\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndTree()\n\tend\n\n\tif RemoveIndex > 0 then\n\t\ttable.remove(DrawTree_Tables, RemoveIndex)\n\tend\nend\n\nlocal DrawDialog_MessageBox = false\nlocal DrawDialog_MessageBox_Title = \"Message Box\"\nlocal DrawDialog_MessageBox_Message = \"This is a message.\"\nlocal DrawDialog_FileDialog = ''\nlocal DrawDialog_FileDialog_Result = \"\"\n\nlocal function DrawDialog()\n\tSlab.Textf(\n\t\t\"Dialog boxes are windows that rendered on top of everything else. These windows will consume input from all other windows \" ..\n\t\t\"and controls. These are useful for forcing users to interact with a window of importance, such as message boxes and \" ..\n\t\t\"file dialogs.\")\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"By clicking the button below, an example of a simple dialog box will be rendered.\")\n\tif Slab.Button(\"Open Basic Dialog\") then\n\t\tSlab.OpenDialog('DrawDialog_Basic')\n\tend\n\n\tif Slab.BeginDialog('DrawDialog_Basic', {Title = \"Basic Dialog\"}) then\n\t\tSlab.Text(\"This is a basic dialog box.\")\n\n\t\tif Slab.Button(\"Close\") then\n\t\t\tSlab.CloseDialog()\n\t\tend\n\n\t\tSlab.EndDialog()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Slab offers support for common dialog boxes such as message boxes. To display a message box, Slab.MessageBox must be called every \" ..\n\t\t\"frame. The buttons to be drawn must be passed in through the Buttons option. Once the user has made a selection, the button that was \" ..\n\t\t\"clicked is returned and the program can handle the response accordingly.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Title\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawDialog_MessageBox_Title', {Text = DrawDialog_MessageBox_Title}) then\n\t\tDrawDialog_MessageBox_Title = Slab.GetInputText()\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Message\")\n\tif Slab.Input('DrawDialog_MessageBox_Message', {Text = DrawDialog_MessageBox_Message, MultiLine = true, H = 75}) then\n\t\tDrawDialog_MessageBox_Message = Slab.GetInputText()\n\tend\n\n\tSlab.NewLine()\n\n\tif Slab.Button(\"Show Message Box\") then\n\t\tDrawDialog_MessageBox = true\n\tend\n\n\tif DrawDialog_MessageBox then\n\t\tlocal Result = Slab.MessageBox(DrawDialog_MessageBox_Title, DrawDialog_MessageBox_Message, {Buttons = {\"OK\"}})\n\n\t\tif Result ~= \"\" then\n\t\t\tDrawDialog_MessageBox = false\n\t\tend\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Slab offers a file dialog box so that user can select to open or save a file. This behaves similar to file dialogs found on \" ..\n\t\t\"various operating systems. Files can be filtered and a starting directory can be set. There are options for the user to select \" ..\n\t\t\"a single item or multiple items. As with the message box, the FileDialog option must be called every frame and the user response \" ..\n\t\t\"must be handled by the program.\")\n\n\tSlab.NewLine()\n\n\tif Slab.Button(\"Open File\") then\n\t\tDrawDialog_FileDialog = 'openfile'\n\tend\n\n\tSlab.SameLine()\n\n\tif Slab.Button(\"Open Directory\") then\n\t\tDrawDialog_FileDialog = 'opendirectory'\n\tend\n\n\tSlab.SameLine()\n\n\tif Slab.Button(\"Save File\") then\n\t\tDrawDialog_FileDialog = 'savefile'\n\tend\n\n\tif DrawDialog_FileDialog ~= '' then\n\t\tlocal Result = Slab.FileDialog({AllowMultiSelect = false, Type = DrawDialog_FileDialog})\n\n\t\tif Result.Button ~= \"\" then\n\t\t\tDrawDialog_FileDialog = ''\n\n\t\t\tif Result.Button == \"OK\" then\n\t\t\t\tDrawDialog_FileDialog_Result = Result.Files[1]\n\t\t\tend\n\t\tend\n\tend\n\n\tSlab.Textf(\n\t\t\"Selected file: \" .. DrawDialog_FileDialog_Result)\nend\n\nlocal DrawInteraction_MouseClicked_Left = 0\nlocal DrawInteraction_MouseClicked_Right = 0\nlocal DrawInteraction_MouseClicked_Middle = 0\nlocal DrawInteraction_MouseReleased_Left = 0\nlocal DrawInteraction_MouseReleased_Right = 0\nlocal DrawInteraction_MouseReleased_Middle = 0\nlocal DrawInteraction_MouseDoubleClicked_Left = 0\nlocal DrawInteraction_MouseDoubleClicked_Right = 0\nlocal DrawInteraction_MouseDoubleClicked_Middle = 0\nlocal DrawInteraction_MouseVoidClicked_Left = 0\nlocal DrawInteraction_MouseCustomCursors = nil\nlocal DrawInteraction_KeyPressed_A = 0\nlocal DrawInteraction_KeyPressed_S = 0\nlocal DrawInteraction_KeyPressed_D = 0\nlocal DrawInteraction_KeyPressed_F = 0\nlocal DrawInteraction_KeyReleased_A = 0\nlocal DrawInteraction_KeyReleased_S = 0\nlocal DrawInteraction_KeyReleased_D = 0\nlocal DrawInteraction_KeyReleased_F = 0\n\nlocal function DrawInteraction()\n\tSlab.Textf(\n\t\t\"Slab offers functions to query the user's input on a given frame. There are also functions to query for input on the most \" ..\n\t\t\"recently declared control. This can allow the implementation to use custom logic for controls to create custom behaviors.\")\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"Below are functions that query the state of the mouse. The IsMouseDown checks to see if a specific button is down on that \" ..\n\t\t\"frame. The IsMouseClicked will check to see if the state of a button went from up to down on that frame and the IsMouseReleased \" ..\n\t\t\"function checks to see if a button went from down to up on that frame.\")\n\n\tlocal Left = Slab.IsMouseDown(1)\n\tlocal Right = Slab.IsMouseDown(2)\n\tlocal Middle = Slab.IsMouseDown(3)\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Left\")\n\tSlab.SameLine()\n\tSlab.Text(Left and \"Down\" or \"Up\")\n\n\tSlab.Text(\"Right\")\n\tSlab.SameLine()\n\tSlab.Text(Right and \"Down\" or \"Up\")\n\n\tSlab.Text(\"Middle\")\n\tSlab.SameLine()\n\tSlab.Text(Middle and \"Down\" or \"Up\")\n\n\tSlab.NewLine()\n\n\tif Slab.IsMouseClicked(1) then DrawInteraction_MouseClicked_Left = DrawInteraction_MouseClicked_Left + 1 end\n\tif Slab.IsMouseClicked(2) then DrawInteraction_MouseClicked_Right = DrawInteraction_MouseClicked_Right + 1 end\n\tif Slab.IsMouseClicked(3) then DrawInteraction_MouseClicked_Middle = DrawInteraction_MouseClicked_Middle + 1 end\n\n\tif Slab.IsMouseReleased(1) then DrawInteraction_MouseReleased_Left = DrawInteraction_MouseReleased_Left + 1 end\n\tif Slab.IsMouseReleased(2) then DrawInteraction_MouseReleased_Right = DrawInteraction_MouseReleased_Right + 1 end\n\tif Slab.IsMouseReleased(3) then DrawInteraction_MouseReleased_Middle = DrawInteraction_MouseReleased_Middle + 1 end\n\n\tSlab.Text(\"Left Clicked: \" .. DrawInteraction_MouseClicked_Left)\n\tSlab.SameLine()\n\tSlab.Text(\"Released: \" .. DrawInteraction_MouseReleased_Left)\n\n\tSlab.Text(\"Right Clicked: \" .. DrawInteraction_MouseClicked_Right)\n\tSlab.SameLine()\n\tSlab.Text(\"Released: \" .. DrawInteraction_MouseReleased_Right)\n\n\tSlab.Text(\"Middle Clicked: \" .. DrawInteraction_MouseClicked_Middle)\n\tSlab.SameLine()\n\tSlab.Text(\"Released: \" .. DrawInteraction_MouseReleased_Middle)\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"Slab offers functions to detect if the mouse was double-clicked or if a mouse button is being dragged.\")\n\n\tSlab.NewLine()\n\n\tif Slab.IsMouseDoubleClicked(1) then DrawInteraction_MouseDoubleClicked_Left = DrawInteraction_MouseDoubleClicked_Left + 1 end\n\tif Slab.IsMouseDoubleClicked(2) then DrawInteraction_MouseDoubleClicked_Right = DrawInteraction_MouseDoubleClicked_Right + 1 end\n\tif Slab.IsMouseDoubleClicked(3) then DrawInteraction_MouseDoubleClicked_Middle = DrawInteraction_MouseDoubleClicked_Middle + 1 end\n\n\tSlab.Text(\"Left Double Clicked: \" .. DrawInteraction_MouseDoubleClicked_Left)\n\tSlab.Text(\"Right Double Clicked: \" .. DrawInteraction_MouseDoubleClicked_Right)\n\tSlab.Text(\"Middle Double Clicked: \" .. DrawInteraction_MouseDoubleClicked_Middle)\n\n\tSlab.NewLine()\n\n\tlocal LeftDrag = Slab.IsMouseDragging(1)\n\tlocal RightDrag = Slab.IsMouseDragging(2)\n\tlocal MiddleDrag = Slab.IsMouseDragging(3)\n\n\tSlab.Text(\"Left Drag: \" .. tostring(LeftDrag))\n\tSlab.Text(\"Right Drag: \" .. tostring(RightDrag))\n\tSlab.Text(\"Middle Drag: \" .. tostring(MiddleDrag))\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"The mouse position relative to the viewport and relative to the current window can also be queried. Slab also offers retrieving \" ..\n\t\t\"the mouse delta.\")\n\n\tSlab.NewLine()\n\n\tlocal X, Y = Slab.GetMousePosition()\n\tlocal WinX, WinY = Slab.GetMousePositionWindow()\n\tlocal DeltaX, DeltaY = Slab.GetMouseDelta()\n\n\tSlab.Text(\"X: \" .. X .. \" Y: \" .. Y)\n\tSlab.Text(\"Window X: \" .. WinX .. \" Window Y: \" .. WinY)\n\tSlab.Text(\"Delta X: \" .. DeltaX .. \" Delta Y: \" .. DeltaY)\n\n\tSlab.Textf(\n\t\t\"Slab also offers functions to test if the user is interacting with the non-UI layer. The IsVoidHovered and IsVoidClicked \" ..\n\t\t\"behave the same way as IsControlHovered and IsControlClicked except will only return true when it is in a non-UI area.\")\n\n\tSlab.NewLine()\n\n\tif Slab.IsVoidClicked(1) then\n\t\tDrawInteraction_MouseVoidClicked_Left = DrawInteraction_MouseVoidClicked_Left + 1\n\tend\n\n\tlocal IsVoidHovered = Slab.IsVoidHovered()\n\n\tSlab.Text(\"Left Void Clicked: \" .. DrawInteraction_MouseVoidClicked_Left)\n\tSlab.Text(\"Is Void Hovered: \" .. tostring(IsVoidHovered))\n\n\tSlab.NewLine()\n\tSlab.Textf(\n\t\t\"The rendered mouse can also be customized. This is done by overriding what the default system cursor displays. A custom Image \" ..\n\t\t\"can be supplied but must be managed by the developer. Alternatively, 'nil' can be passed to disable rendering any cursor for a \" ..\n\t\t\"given system cursor type. Below is a list of available system cursors that can be overridden. Each custom cursor is associated with \" ..\n\t\t\"a test image for this example.\")\n\n\tif DrawInteraction_MouseCustomCursors == nil then\n\t\tDrawInteraction_MouseCustomCursors = {}\n\t\tlocal Image = love.graphics.newImage(DrawImage_Path_Icons)\n\t\tlocal Corner = love.graphics.newQuad(150, 0, 50, 50, Image:getWidth(), Image:getHeight())\n\t\tlocal Cursor = love.graphics.newQuad(200, 0, 50, 50, Image:getWidth(), Image:getHeight())\n\t\tlocal WestEast = love.graphics.newQuad(50, 50, 50, 50, Image:getWidth(), Image:getHeight())\n\t\tlocal NorthSouth = love.graphics.newQuad(100, 50, 50, 50, Image:getWidth(), Image:getHeight())\n\t\tlocal Hand = love.graphics.newQuad(0, 50, 50, 50, Image:getWidth(), Image:getHeight())\n\t\tlocal IBeam = love.graphics.newQuad(150, 50, 50, 50, Image:getWidth(), Image:getHeight())\n\n\t\tDrawInteraction_MouseCustomCursors['arrow'] = {Image = Image, Quad = Cursor}\n\t\tDrawInteraction_MouseCustomCursors['sizewe'] = {Image = Image, Quad = WestEast}\n\t\tDrawInteraction_MouseCustomCursors['sizens'] = {Image = Image, Quad = NorthSouth}\n\t\tDrawInteraction_MouseCustomCursors['sizenesw'] = {Image = Image, Quad = Corner}\n\t\tDrawInteraction_MouseCustomCursors['sizenwse'] = {Image = Image, Quad = Corner}\n\t\tDrawInteraction_MouseCustomCursors['ibeam'] = {Image = Image, Quad = IBeam}\n\t\tDrawInteraction_MouseCustomCursors['hand'] = {Image = Image, Quad = Hand}\n\tend\n\n\tSlab.NewLine()\n\n\tfor K, V in pairs(DrawInteraction_MouseCustomCursors) do\n\t\tif Slab.CheckBox(V.Enabled, K) then\n\t\t\tV.Enabled = not V.Enabled\n\n\t\t\tif V.Enabled then\n\t\t\t\tSlab.SetCustomMouseCursor(K, V.Image, V.Quad)\n\t\t\telse\n\t\t\t\tSlab.ClearCustomMouseCursor(K)\n\t\t\tend\n\t\tend\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Slab offers functions to check for the state of a specific keyboard key. The key code to use are the ones defined by LÖVE \" ..\n\t\t\"which can be found on the wiki. Below we will check for the key states of the A, S, D, F keys.\")\n\n\tSlab.NewLine()\n\n\tlocal IsDown_A = Slab.IsKeyDown('a')\n\tlocal IsDown_S = Slab.IsKeyDown('s')\n\tlocal IsDown_D = Slab.IsKeyDown('d')\n\tlocal IsDown_F = Slab.IsKeyDown('f')\n\n\tif Slab.IsKeyPressed('a') then DrawInteraction_KeyPressed_A = DrawInteraction_KeyPressed_A + 1 end\n\tif Slab.IsKeyPressed('s') then DrawInteraction_KeyPressed_S = DrawInteraction_KeyPressed_S + 1 end\n\tif Slab.IsKeyPressed('d') then DrawInteraction_KeyPressed_D = DrawInteraction_KeyPressed_D + 1 end\n\tif Slab.IsKeyPressed('f') then DrawInteraction_KeyPressed_F = DrawInteraction_KeyPressed_F + 1 end\n\n\tif Slab.IsKeyReleased('a') then DrawInteraction_KeyReleased_A = DrawInteraction_KeyReleased_A + 1 end\n\tif Slab.IsKeyReleased('s') then DrawInteraction_KeyReleased_S = DrawInteraction_KeyReleased_S + 1 end\n\tif Slab.IsKeyReleased('d') then DrawInteraction_KeyReleased_D = DrawInteraction_KeyReleased_D + 1 end\n\tif Slab.IsKeyReleased('f') then DrawInteraction_KeyReleased_F = DrawInteraction_KeyReleased_F + 1 end\n\n\tSlab.Text(\"A Down: \" .. tostring(IsDown_A))\n\tSlab.Text(\"S Down: \" .. tostring(IsDown_S))\n\tSlab.Text(\"D Down: \" .. tostring(IsDown_D))\n\tSlab.Text(\"F Down: \" .. tostring(IsDown_F))\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"A Pressed: \" .. DrawInteraction_KeyPressed_A)\n\tSlab.Text(\"S Pressed: \" .. DrawInteraction_KeyPressed_S)\n\tSlab.Text(\"D Pressed: \" .. DrawInteraction_KeyPressed_D)\n\tSlab.Text(\"F Pressed: \" .. DrawInteraction_KeyPressed_F)\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"A Released: \" .. DrawInteraction_KeyReleased_A)\n\tSlab.Text(\"S Released: \" .. DrawInteraction_KeyReleased_S)\n\tSlab.Text(\"D Released: \" .. DrawInteraction_KeyReleased_D)\n\tSlab.Text(\"F Released: \" .. DrawInteraction_KeyReleased_F)\nend\n\nlocal DrawShapes_Rectangle_Color = {1, 0, 0, 1}\nlocal DrawShapes_Rectangle_ChangeColor = false\nlocal DrawShapes_Rectangle_Rounding = {0, 0, 2.0, 2.0}\nlocal DrawShapes_Circle_Radius = 32.0\nlocal DrawShapes_Circle_Segments = 24\nlocal DrawShapes_Circle_Mode = 'fill'\nlocal DrawShapes_Triangle_Radius = 32.0\nlocal DrawShapes_Triangle_Rotation = 0\nlocal DrawShapes_Triangle_Mode = 'fill'\nlocal DrawShapes_Modes = {'fill', 'line'}\nlocal DrawShapes_Line_Width = 1.0\nlocal DrawShapes_Curve = {0, 0, 150, 150, 300, 0}\nlocal DrawShapes_ControlPoint_Size = 7.5\nlocal DrawShapes_ControlPoint_Index = 0\nlocal DrawShapes_Polygon = {10, 10, 150, 25, 175, 75, 50, 125}\nlocal DrawShapes_Polygon_Mode = 'fill'\n\nlocal function DrawShapes_Rectangle_Rounding_Input(Corner, Index)\n\tSlab.Text(Corner)\n\tSlab.SameLine()\n\tif Slab.Input('DrawShapes_Rectangle_Rounding_' .. Corner, {Text = tostring(DrawShapes_Rectangle_Rounding[Index]), NumbersOnly = true, MinNumber = 0, ReturnOnText = false}) then\n\t\tDrawShapes_Rectangle_Rounding[Index] = Slab.GetInputNumber()\n\tend\nend\n\nlocal function DrawShapes()\n\tSlab.Textf(\n\t\t\"Slab offers functions to draw basic shapes to the window. These shapes can complement the controls provided by Slab.\")\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"Below is an invisible button combined with a rectangle. Click on the rectangle to change the color.\")\n\n\tlocal X, Y = Slab.GetCursorPos()\n\tSlab.Rectangle({W = 150, H = 25, Color = DrawShapes_Rectangle_Color})\n\tSlab.SetCursorPos(X, Y)\n\tif Slab.Button(\"\", {W = 150, H = 25, Invisible = true}) then\n\t\tDrawShapes_Rectangle_ChangeColor = true\n\tend\n\n\tif DrawShapes_Rectangle_ChangeColor then\n\t\tlocal Result = Slab.ColorPicker({Color = DrawShapes_Rectangle_Color})\n\n\t\tif Result.Button ~= 0 then\n\t\t\tDrawShapes_Rectangle_ChangeColor = false\n\n\t\t\tif Result.Button == 1 then\n\t\t\t\tDrawShapes_Rectangle_Color = Result.Color\n\t\t\tend\n\t\tend\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\n\t\t\"Rectangle corner rounding can be defined in multiple ways. The rounding option can take a single number, which will apply rounding to all corners. The option \" ..\n\t\t\"can also accept a table, with each index affecting a single corner. The order this happens in is top left, top right, bottom right, and bottom left.\")\n\n\tSlab.NewLine()\n\n\tDrawShapes_Rectangle_Rounding_Input('TL', 1)\n\tSlab.SameLine()\n\tDrawShapes_Rectangle_Rounding_Input('TR', 2)\n\tSlab.SameLine()\n\tDrawShapes_Rectangle_Rounding_Input('BR', 3)\n\tSlab.SameLine()\n\tDrawShapes_Rectangle_Rounding_Input('BL', 4)\n\n\tSlab.NewLine()\n\n\tSlab.Rectangle({W = 150.0, H = 75.0, Rounding = DrawShapes_Rectangle_Rounding, Outline = true, Color = {0, 1, 0, 1}})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Circles are drawn by defining a radius. Along with the color the number of segments can be set as well.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Radius\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawShapes_Circle_Radius', {Text = tostring(DrawShapes_Circle_Radius), NumbersOnly = true, MinNumber = 0, ReturnOnText = false}) then\n\t\tDrawShapes_Circle_Radius = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Segments\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawShapes_Circle_Segments', {Text = tostring(DrawShapes_Circle_Segments), NumbersOnly = true, MinNumber = 0, ReturnOnText = false}) then\n\t\tDrawShapes_Circle_Segments = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Mode\")\n\tSlab.SameLine()\n\tif Slab.BeginComboBox('DrawShapes_Circle_Mode', {Selected = DrawShapes_Circle_Mode}) then\n\t\tfor I, V in ipairs(DrawShapes_Modes) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawShapes_Circle_Mode = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.Circle({Radius = DrawShapes_Circle_Radius, Segments = DrawShapes_Circle_Segments, Color = {1, 1, 1, 1}, Mode = DrawShapes_Circle_Mode})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Triangles are drawn by defining a radius, which is the length from the center of the triangle to the 3 points. A rotation in degrees \" ..\n\t\t\"can be specified to rotate the triangle.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Radius\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawShapes_Triangle_Radius', {Text = tostring(DrawShapes_Triangle_Radius), NumbersOnly = true, MinNumber = 0, ReturnOnText = false}) then\n\t\tDrawShapes_Triangle_Radius = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Rotation\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawShapes_Triangle_Rotation', {Text = tostring(DrawShapes_Triangle_Rotation), NumbersOnly = true, MinNumber = 0, ReturnOnText = false}) then\n\t\tDrawShapes_Triangle_Rotation = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Mode\")\n\tSlab.SameLine()\n\tif Slab.BeginComboBox('DrawShapes_Triangle_Mode', {Selected = DrawShapes_Triangle_Mode}) then\n\t\tfor I, V in ipairs(DrawShapes_Modes) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawShapes_Triangle_Mode = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.Triangle({Radius = DrawShapes_Triangle_Radius, Rotation = DrawShapes_Triangle_Rotation, Color = {0, 1, 0, 1}, Mode = DrawShapes_Triangle_Mode})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Lines are defined by two points. The function only takes in a single point which defines the end point while the start point is defined by the current \" ..\n\t\t\"cursor position. Both the line width and color can be defined.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Width\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawShapes_Line_Width', {Text = tostring(DrawShapes_Line_Width), NumbersOnly = true, ReturnOnText = false, MinNumber = 1.0}) then\n\t\tDrawShapes_Line_Width = Slab.GetInputNumber()\n\tend\n\n\tSlab.NewLine()\n\n\tX, Y = Slab.GetCursorPos({Absolute = true})\n\tlocal WinW, WinH = Slab.GetWindowActiveSize()\n\tSlab.Line(X + WinW * 0.5, Y, {Width = DrawShapes_Line_Width, Color = {1, 1, 0, 1}})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Bezier curves can be defined through a set of points and added to a Slab window. The points given must be in local space. Slab will translate the \" ..\n\t\t\"curve to the current cursor position. Along with the ability to draw the curve, Slab offers functions to query information about the curve, such as \" ..\n\t\t\"the number of control points defined, the position of a control point, and the ability to evaluate the position of a curve given a Time value. \" ..\n\t\t\"There is also a function to evaluate the curve with the current X mouse position.\")\n\n\tSlab.NewLine()\n\n\tSlab.Curve(DrawShapes_Curve)\n\tX, Y = Slab.GetCursorPos({Absolute = true})\n\n\tSlab.SameLine({CenterY = true, Pad = 16})\n\tlocal EvalX, EvalY = Slab.EvaluateCurveMouse()\n\tSlab.Text(string.format(\"X: %.2f Y: %.2f\", EvalX, EvalY))\n\n\tEvalX, EvalY = Slab.EvaluateCurveMouse({LocalSpace = false})\n\tSlab.SetCursorPos(EvalX, EvalY, {Absolute = true})\n\tSlab.Circle({Color = {1, 1, 1, 1}, Radius = DrawShapes_ControlPoint_Size * 0.5})\n\n\tlocal HalfSize = DrawShapes_ControlPoint_Size * 0.5\n\tfor I = 1, Slab.GetCurveControlPointCount(), 1 do\n\t\tlocal PX, PY = Slab.GetCurveControlPoint(I, {LocalSpace = false})\n\n\t\tSlab.SetCursorPos(PX - HalfSize, PY - HalfSize, {Absolute = true})\n\t\tSlab.Rectangle({W = DrawShapes_ControlPoint_Size, H = DrawShapes_ControlPoint_Size, Color = {1, 1, 1, 1}})\n\n\t\tif Slab.IsControlClicked() then\n\t\t\tDrawShapes_ControlPoint_Index = I\n\t\tend\n\tend\n\n\tif DrawShapes_ControlPoint_Index > 0 and Slab.IsMouseDragging() then\n\t\tlocal DeltaX, DeltaY = Slab.GetMouseDelta()\n\t\tlocal P2 = DrawShapes_ControlPoint_Index * 2\n\t\tlocal P1 = P2 - 1\n\n\t\tDrawShapes_Curve[P1] = DrawShapes_Curve[P1] + DeltaX\n\t\tDrawShapes_Curve[P2] = DrawShapes_Curve[P2] + DeltaY\n\tend\n\n\tif Slab.IsMouseReleased() then\n\t\tDrawShapes_ControlPoint_Index = 0\n\tend\n\n\tSlab.SetCursorPos(X, Y, {Absolute = true})\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Polygons can be drawn by passing in a list of points into the Polygon function. The points, like the curve, should be defined in local space. Slab will \" ..\n\t\t\"then translate the points to the current cursor position.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Mode\")\n\tSlab.SameLine()\n\tif Slab.BeginComboBox('DrawShapes_Polygon_Mode', {Selected = DrawShapes_Polygon_Mode}) then\n\t\tfor I, V in ipairs(DrawShapes_Modes) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawShapes_Polygon_Mode = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.Polygon(DrawShapes_Polygon, {Color = {0, 0, 1, 1}, Mode = DrawShapes_Polygon_Mode})\nend\n\nlocal DrawWindow_X = 900\nlocal DrawWindow_Y = 100\nlocal DrawWindow_W = 200\nlocal DrawWindow_H = 200\nlocal DrawWindow_Title = \"A\"\nlocal DrawWindow_TitleH = nil\nlocal DrawWindow_TitleAlignmentX = 'center'\nlocal DrawWindow_TitleAlignmentY = 'center'\nlocal DrawWindow_TitleAlignmentX_Options = {'left', 'center', 'right'}\nlocal DrawWindow_TitleAlignmentY_Options = {'top', 'center', 'bottom'}\nlocal DrawWindow_ResetLayout = false\nlocal DrawWindow_ResetSize = false\nlocal DrawWindow_AutoSizeWindow = true\nlocal DrawWindow_AllowResize = true\nlocal DrawWindow_AllowMove = true\nlocal DrawWindow_AllowFocus = true\nlocal DrawWindow_Border = 4.0\nlocal DrawWindow_BgColor = nil\nlocal DrawWindow_BgColor_ChangeColor = false\nlocal DrawWindow_NoOutline = false\nlocal DrawWindow_Constrain = false\nlocal DrawWindow_SizerFilter = {}\nlocal DrawWindow_SizerFiltersOptions = {\n\tN = true,\n\tS = true,\n\tE = true,\n\tW = true,\n\tNW = true,\n\tNE = true,\n\tSW = true,\n\tSE = true,\n}\n\nlocal function DrawWindow_SizerCheckBox(Key)\n\tif Slab.CheckBox(DrawWindow_SizerFiltersOptions[Key], Key) then\n\t\tDrawWindow_SizerFiltersOptions[Key] = not DrawWindow_SizerFiltersOptions[Key]\n\tend\nend\n\nlocal function DrawWindow()\n\t-- Ensure a valid height. This could be due to a first run.\n\tDrawWindow_TitleH = DrawWindow_TitleH or Slab.GetStyle().Font:getHeight()\n\n\tSlab.Textf(\n\t\t\"Windows are the basis for which all controls are rendered on and for all user interactions to occur. This area will contain information on the \" ..\n\t\t\"various options that a window can take and what their expected behaviors will be. The window rendered to the right of this window will be affected \" ..\n\t\t\"by the changes to the various parameters.\")\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"The title of the window can be customized. If no title exists, then the title bar is not rendered and the window can not be moved. There is also an \" ..\n\t\t\"option, AllowMove, to disable movement even with the title bar. The position of the window can be constrained to the viewport through the 'ConstrainPosition' \" ..\n\t\t\"option. The height of the title bar is also adjustable. The default height will be the height of the current font.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Title\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawWindow_Title', {Text = DrawWindow_Title, ReturnOnText = false}) then\n\t\tDrawWindow_Title = Slab.GetInputText()\n\tend\n\n\tif Slab.CheckBox(DrawWindow_AllowMove, \"Allow Move\") then\n\t\tDrawWindow_AllowMove = not DrawWindow_AllowMove\n\tend\n\n\tif Slab.CheckBox(DrawWindow_Constrain, \"Constrain Position To Viewport\") then\n\t\tDrawWindow_Constrain = not DrawWindow_Constrain\n\tend\n\n\tSlab.Text(\"Height\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawWindow_TitleHeight', {Text = DrawWindow_TitleH, ReturnOnText = false}) then\n\t\tDrawWindow_TitleH = Slab.GetInputNumber()\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.Textf(\"The text alignment of the title can also be changed.\")\n\tSlab.Text(\"Horizontal\")\n\tSlab.SameLine()\n\tif Slab.BeginComboBox('DrawWindow_TitleAlignmentX', {Selected = DrawWindow_TitleAlignmentX}) then\n\t\tfor I, V in ipairs(DrawWindow_TitleAlignmentX_Options) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawWindow_TitleAlignmentX = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Vertical\")\n\tSlab.SameLine()\n\tif Slab.BeginComboBox('DrawWindow_TitleAlignmentY', {Selected = DrawWindow_TitleAlignmentY}) then\n\t\tfor I, V in ipairs(DrawWindow_TitleAlignmentY_Options) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawWindow_TitleAlignmentY = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"The default position of the window can be set with the X and Y options. The window can be moved from this position but the parameter values stay the same \" ..\n\t\t\"as the window keeps track of any delta changes from the starting position. The window can be reset to the default position as described later on below.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"X\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawWindow_X', {Text = tostring(DrawWindow_X), NumbersOnly = true, ReturnOnText = false}) then\n\t\tDrawWindow_X = Slab.GetInputNumber()\n\t\tDrawWindow_ResetLayout = true\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Y\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawWindow_Y', {Text = tostring(DrawWindow_Y), NumbersOnly = true, ReturnOnText = false}) then\n\t\tDrawWindow_Y = Slab.GetInputNumber()\n\t\tDrawWindow_ResetLayout = true\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"The size of the window can be specified. However, windows by default are set to auto size with the AutoSizeWindow option, which resizes the window only when \" ..\n\t\t\"controls are added to the window. If this option is disabled, then the W and H parameters will be applied to the window.\\n\" ..\n\t\t\"Similar to the window position, the window's size delta changes are stored by the window. The window's size can be reset to the default with the ResetSize \" ..\n\t\t\"option.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"W\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawWindow_W', {Text = tostring(DrawWindow_W), NumbersOnly = true, ReturnOnText = false, MinNumber = 0}) then\n\t\tDrawWindow_W = Slab.GetInputNumber()\n\t\tDrawWindow_ResetSize = true\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"H\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawWindow_H', {Text = tostring(DrawWindow_H), NumbersOnly = true, ReturnOnText = false, MinNumber = 0}) then\n\t\tDrawWindow_H = Slab.GetInputNumber()\n\t\tDrawWindow_ResetSize = true\n\tend\n\n\tif Slab.CheckBox(DrawWindow_AutoSizeWindow, \"Auto Size Window\") then\n\t\tDrawWindow_AutoSizeWindow = not DrawWindow_AutoSizeWindow\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Windows can be resized onluy if the AutoSizeWindow option is set false. By default, all sides and corners of a window can be resized, but this can be \" ..\n\t\t\"modified by specifying which directions are allowed to be resized. There is also an option to completely disable resizing with the AllowResize option. \" ..\n\t\t\"Below is a list of options that are available.\")\n\n\tSlab.NewLine()\n\n\tif Slab.CheckBox(DrawWindow_AllowResize, \"Allow Resize\") then\n\t\tDrawWindow_AllowResize = not DrawWindow_AllowResize\n\tend\n\n\tDrawWindow_SizerCheckBox('N')\n\tDrawWindow_SizerCheckBox('S')\n\tDrawWindow_SizerCheckBox('E')\n\tDrawWindow_SizerCheckBox('W')\n\tDrawWindow_SizerCheckBox('NW')\n\tDrawWindow_SizerCheckBox('NE')\n\tDrawWindow_SizerCheckBox('SW')\n\tDrawWindow_SizerCheckBox('SE')\n\n\tlocal FalseCount = 0\n\tDrawWindow_SizerFilter = {}\n\tfor K, V in pairs(DrawWindow_SizerFiltersOptions) do\n\t\tif V then\n\t\t\ttable.insert(DrawWindow_SizerFilter, K)\n\t\telse\n\t\t\tFalseCount = FalseCount + 1\n\t\tend\n\tend\n\n\tif FalseCount == 0 then\n\t\tDrawWindow_SizerFilter = {}\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Windows gain focus when the user clicks within the region of the window. When the window gains focus, it is brought to the top of the window stack. \" ..\n\t\t\"Through the AllowFocus option, a window may have this behavior turned off.\")\n\n\tSlab.NewLine()\n\n\tif Slab.CheckBox(DrawWindow_AllowFocus, \"Allow Focus\") then\n\t\tDrawWindow_AllowFocus = not DrawWindow_AllowFocus\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Windows have a border defined which is how much space there is between the edges of the window and the contents of the window.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Border\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawWindow_Border', {Text = tostring(DrawWindow_Border), NumbersOnly = true, ReturnOnText = false, MinNumber = 0}) then\n\t\tDrawWindow_Border = Slab.GetInputNumber()\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"The ResetSize and ResetLayout options for windows will reset any delta changes to a window's position or size. It is recommended to only pass \" ..\n\t\t\"in true for these options on a single frame if resetting the position or size is desired.\")\n\n\tSlab.NewLine()\n\n\tif Slab.Button(\"Reset Layout\") then\n\t\tDrawWindow_ResetLayout = true\n\tend\n\n\tSlab.SameLine()\n\n\tif Slab.Button(\"Reset Size\") then\n\t\tDrawWindow_ResetSize = true\n\tend\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"The background color of the window can be modified. Along with modifying the color, the outline of the window can be set to drawn or hidden.\" ..\n\t\t\"Hiding the outline and setting the background to be transparent will make only the controls be rendered within the window.\")\n\n\tif DrawWindow_BgColor == nil then\n\t\tDrawWindow_BgColor = Slab.GetStyle().WindowBackgroundColor\n\tend\n\n\tif Slab.Button(\"Change Backgound Color\") then\n\t\tDrawWindow_BgColor_ChangeColor = true\n\tend\n\n\tif Slab.CheckBox(DrawWindow_NoOutline, \"No Outline\") then\n\t\tDrawWindow_NoOutline = not DrawWindow_NoOutline\n\tend\n\n\tif DrawWindow_BgColor_ChangeColor then\n\t\tlocal Result = Slab.ColorPicker({Color = DrawWindow_BgColor})\n\n\t\tif Result.Button ~= 0 then\n\t\t\tDrawWindow_BgColor_ChangeColor = false\n\n\t\t\tif Result.Button == 1 then\n\t\t\t\tDrawWindow_BgColor = Result.Color\n\t\t\tend\n\t\tend\n\tend\n\n\tSlab.BeginWindow('DrawWindow_Example', {\n\t\tTitle = DrawWindow_Title,\n\t\tTitleH = DrawWindow_TitleH,\n\t\tTitleAlignX = DrawWindow_TitleAlignmentX,\n\t\tTitleAlignY = DrawWindow_TitleAlignmentY,\n\t\tX = DrawWindow_X,\n\t\tY = DrawWindow_Y,\n\t\tW = DrawWindow_W,\n\t\tH = DrawWindow_H,\n\t\tResetLayout = DrawWindow_ResetLayout,\n\t\tResetSize = DrawWindow_ResetSize,\n\t\tAutoSizeWindow = DrawWindow_AutoSizeWindow,\n\t\tSizerFilter = DrawWindow_SizerFilter,\n\t\tAllowResize = DrawWindow_AllowResize,\n\t\tAllowMove = DrawWindow_AllowMove,\n\t\tAllowFocus = DrawWindow_AllowFocus,\n\t\tBorder = DrawWindow_Border,\n\t\tBgColor = DrawWindow_BgColor,\n\t\tNoOutline = DrawWindow_NoOutline,\n\t\tConstrainPosition = DrawWindow_Constrain\n\t})\n\tSlab.Text(\"Hello World\")\n\tSlab.EndWindow()\n\n\tDrawWindow_ResetLayout = false\n\tDrawWindow_ResetSize = false\nend\n\nlocal DrawTooltip_CheckBox = false\nlocal DrawTooltip_Radio = 1\nlocal DrawTooltip_ComboBox_Items = {\"Button\", \"Check Box\", \"Combo Box\", \"Image\", \"Input\", \"Text\", \"Tree\"}\nlocal DrawTooltip_ComboBox_Selected = \"Button\"\nlocal DrawTooltip_Input = \"This is an input box.\"\n\nlocal function DrawTooltip()\n\tSlab.Textf(\n\t\t\"Slab offers tooltips to be rendered when the user has hovered over the control for a period of time. Not all controls are currently supported, \" ..\n\t\t\"and this window will show examples for tooltips on the supported controls.\")\n\n\tSlab.NewLine()\n\n\tSlab.Button(\"Button\", {Tooltip = \"This is a button.\"})\n\n\tSlab.NewLine()\n\n\tif Slab.CheckBox(DrawTooltip_CheckBox, \"Check Box\", {Tooltip = \"This is a check box.\"}) then\n\t\tDrawTooltip_CheckBox = not DrawTooltip_CheckBox\n\tend\n\n\tSlab.NewLine()\n\n\tfor I = 1, 3, 1 do\n\t\tif Slab.RadioButton(\"Radio \" .. I, {SelectedIndex = DrawTooltip_Radio, Index = I, Tooltip = \"This is radio button \" .. I}) then\n\t\t\tDrawTooltip_Radio = I\n\t\tend\n\tend\n\n\tSlab.NewLine()\n\n\tif Slab.BeginComboBox('DrawTooltip_ComboBox', {Selected = DrawTooltip_ComboBox_Selected, Tooltip = \"This is a combo box.\"}) then\n\t\tfor I, V in ipairs(DrawTooltip_ComboBox_Items) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawTooltip_ComboBox_Selected = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.Image('DrawTooltip_Image', {Path = DrawImage_Path, Tooltip = \"This is an image.\"})\n\n\tSlab.NewLine()\n\n\tif Slab.Input('DrawTooltip_Input', {Text = DrawTooltip_Input, Tooltip = DrawTooltip_Input}) then\n\t\tDrawTooltip_Input = Slab.GetInputText()\n\tend\n\n\tSlab.NewLine()\n\n\tif Slab.BeginTree('DrawTooltip_Tree_Root', {Label = \"Root\", Tooltip = \"This is the root tree item.\"}) then\n\t\tSlab.BeginTree('DrawTooltip_Tree_Child', {Label = \"Child\", Tooltip = \"This is the child tree item.\", IsLeaf = true})\n\t\tSlab.EndTree()\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.Button(\"MultiLine Tooltip\", {Tooltip = \"This is a multi-line tooltip.\\nThis is the second line.\"})\nend\n\nlocal DrawStats_SetPosition = false\nlocal DrawStats_EncodeIterations = 20\nlocal DrawStats_EncodeLength = 500\n\nlocal function DrawStats()\n\tSlab.Textf(\n\t\t\"The Slab API offers functions that track the performance of desired sections of code. With these functions coupled together with the debug \" ..\n\t\t\"performance window, end-users will be able to see bottlenecks located within their code base quickly. To display the performance window, \" ..\n\t\t\"call the SlabDebug.Performance function.\")\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tif not DrawStats_SetPosition then\n\t\tSlabDebug.Performance_SetPosition(800.0, 175.0)\n\t\tDrawStats_SetPosition = true\n\tend\n\n\tSlab.Textf(\n\t\t\"This page has an example of capturing the performance of encoding data. The iterations and length can be changed to show how the performance is \" ..\n\t\t\"impacted when these values change.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Iterations\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawStats_EncodeIterations', {Text = tostring(DrawStats_EncodeIterations), ReturnOnText = false, NumbersOnly = true, MinNumber = 0}) then\n\t\tDrawStats_EncodeIterations = Slab.GetInputNumber()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"Length\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawStats_EncodeLength', {Text = tostring(DrawStats_EncodeLength), ReturnOnText = false, NumbersOnly = true, MinNumber = 0}) then\n\t\tDrawStats_EncodeLength = Slab.GetInputNumber()\n\tend\n\n\tlocal StatHandle = Slab.BeginStat('Encode', 'Slab Test')\n\n\tfor I = 1, DrawStats_EncodeIterations, 1 do\n\t\tlocal LengthStatHandle = Slab.BeginStat('Encode Length', 'Slab Test')\n\n\t\tlocal Data = \"\"\n\t\tfor J = 1, DrawStats_EncodeLength, 1 do\n\t\t\tlocal Byte = love.math.random(255)\n\t\t\tData = Data .. string.char(Byte)\n\t\tend\n\t\tlove.data.encode('string', 'hex', Data)\n\n\t\tSlab.EndStat(LengthStatHandle)\n\tend\n\n\tSlab.EndStat(StatHandle)\n\n\tSlabDebug.Performance()\nend\n\nlocal DrawLayout_AlignX = 'left'\nlocal DrawLayout_AlignY = 'top'\nlocal DrawLayout_AlignRowY = 'top'\nlocal DrawLayout_AlignX_Options = {'left', 'center', 'right'}\nlocal DrawLayout_AlignY_Options = {'top', 'center', 'bottom'}\nlocal DrawLayout_Radio = 1\nlocal DrawLayout_Input = \"Input Control\"\nlocal DrawLayout_ListBox_Selected = 1\nlocal DrawLayout_Columns = 3\n\nlocal function DrawLayout()\n\tSlab.Textf(\n\t\t\"The layout API allows for controls to be grouped together and aligned to a specific position based on the window. \" ..\n\t\t\"These controls can be aligned to the left, the center, or the right part of a window horizontally. They can also \" ..\n\t\t\"be aligned to the top, the center, or the bottom vertically in a window. Multiple controls can be declared on the \" ..\n\t\t\"same line and the API will properly align on the controls on the same line. Below are examples of how this API can \" ..\n\t\t\"be utilized.\")\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"The below example shows how controls can be aligned within a window. Use the below options to dictate where the next \" ..\n\t\t\"set of controls are aligned.\")\n\tSlab.NewLine()\n\n\tSlab.BeginLayout('DrawLayout_Options', {AlignX = 'center'})\n\n\tSlab.Text(\"AlignX\")\n\tSlab.SameLine()\n\tif Slab.BeginComboBox('DrawLayout_AlignX', {Selected = DrawLayout_AlignX}) then\n\t\tfor I, V in ipairs(DrawLayout_AlignX_Options) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawLayout_AlignX = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"AlignY\")\n\tSlab.SameLine()\n\tif Slab.BeginComboBox('DrawLayout_AlignY', {Selected = DrawLayout_AlignY}) then\n\t\tfor I, V in ipairs(DrawLayout_AlignY_Options) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawLayout_AlignY = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.SameLine()\n\tSlab.Text(\"AlignRowY\")\n\tSlab.SameLine()\n\tif Slab.BeginComboBox('DrawLayout_AlignRowY', {Selected = DrawLayout_AlignRowY}) then\n\t\tfor I, V in ipairs(DrawLayout_AlignY_Options) do\n\t\t\tif Slab.TextSelectable(V) then\n\t\t\t\tDrawLayout_AlignRowY = V\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.EndLayout()\n\n\tSlab.NewLine()\n\n\tSlab.BeginLayout('DrawLayout_General', {AlignX = DrawLayout_AlignX, AlignY = DrawLayout_AlignY, AlignRowY = DrawLayout_AlignRowY})\n\n\tSlab.Button(\"Button 1\")\n\tSlab.SameLine()\n\tSlab.Button(\"Button 2\", {W = 150})\n\n\tSlab.Button(\"Button\")\n\tSlab.SameLine()\n\tSlab.Button(\"Button\", {W = 50, H = 50, Tooltip = \"This is a large button.\"})\n\tSlab.SameLine()\n\tSlab.Button(\"Button\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"New Lines are supported too.\")\n\n\tSlab.EndLayout()\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Controls can also be expanded in the width and height. Only controls that can have their size modified through the API \" ..\n\t\t\"will be affected by these options. The controls that will be affected are buttons, combo boxes (only the width), \" ..\n\t\t\"input controls, and list boxes. Non-expandable controls such as text can be mixed in with the controls and the size \" ..\n\t\t\"of the controls will be adjusted accordingly.\")\n\tSlab.NewLine()\n\n\tSlab.BeginLayout('DrawLayout_Expand', {ExpandW = true, ExpandH = true})\n\tSlab.Button(\"OK\")\n\tSlab.SameLine()\n\tSlab.Text(\"Hello\")\n\tSlab.SameLine()\n\tSlab.Input('DrawLayout_ExpandInput')\n\tSlab.SameLine()\n\tif Slab.BeginComboBox('DrawLayout_ExpandComboBox') then\n\t\tSlab.EndComboBox()\n\tend\n\tSlab.SameLine()\n\tSlab.BeginListBox('DrawLayout_ExpandListBox', {H = 0})\n\tSlab.EndListBox()\n\n\tSlab.Button(\"Cancel\")\n\tSlab.EndLayout()\n\n\tSlab.NewLine()\n\tSlab.Separator()\n\n\tSlab.Textf(\n\t\t\"Controls can be layed out in columns. The 'Columns' option is a number that tells the layout how many columns to allocate for \" ..\n\t\t\"positioning the controls. The 'SetLayoutColumn' function sets the current active column and all controls will be placed within \" ..\n\t\t\"the bounds of that column.\")\n\n\tSlab.NewLine()\n\n\tSlab.BeginLayout('DrawLayout_Columns_Options', {AlignX = 'center'})\n\tSlab.Text(\"Columns\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawLayout_Columns_Input', {Text = tostring(DrawLayout_Columns), ReturnOnText = false, MinNumber = 1, NumbersOnly = true}) then\n\t\tDrawLayout_Columns = Slab.GetInputNumber()\n\tend\n\tSlab.EndLayout()\n\n\tSlab.NewLine()\n\n\tSlab.BeginLayout('DrawLayout_Columns', {Columns = DrawLayout_Columns, AlignX = 'center'})\n\tfor I = 1, DrawLayout_Columns, 1 do\n\t\tSlab.SetLayoutColumn(I)\n\t\tSlab.Text(\"Column \" .. I)\n\t\tSlab.Text(\"This is a very long string\")\n\tend\n\tSlab.EndLayout()\nend\n\nlocal DrawFonts_Roboto = nil\nlocal DrawFonts_Roboto_Path = SLAB_FILE_PATH .. \"/Internal/Resources/Fonts/Roboto-Regular.ttf\"\n\nlocal function DrawFonts()\n\tif DrawFonts_Roboto == nil then\n\t\tDrawFonts_Roboto = love.graphics.newFont(DrawFonts_Roboto_Path, 18)\n\tend\n\n\tSlab.Textf(\n\t\t\"Fonts can be pushed to a stack to alter the rendering of any text. All controls will use this pushed font until \" ..\n\t\t\"the font is popped from the stack, using the last pushed font or the default font. Below is an example of font \" ..\n\t\t\"being pushed to the stack to render a single text control and then being popped before the next text control.\")\n\n\tSlab.NewLine()\n\n\tSlab.PushFont(DrawFonts_Roboto)\n\tSlab.Text(\"This text control is using the Roboto font with point size of 18.\")\n\tSlab.PopFont()\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"This text control is using the default font.\")\nend\n\nlocal function DrawScroll()\n\tSlab.Textf(\n\t\t\"The scroll speed can be modified through the SetScrollSpeed API call. There is also an API function to retrieve \" ..\n\t\t\"the current speed.\")\n\n\tSlab.NewLine()\n\n\tSlab.Text(\"Speed\")\n\tSlab.SameLine()\n\tif Slab.Input('DrawScroll_Speed', {Text = tostring(Slab.GetScrollSpeed()), ReturnOnText = false, NumbersOnly = true}) then\n\t\tSlab.SetScrollSpeed(Slab.GetInputNumber())\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.BeginListBox('DrawScroll_List')\n\n\tfor I = 1, 25, 1 do\n\t\tSlab.Text(\"Item \" .. I)\n\tend\n\n\tSlab.EndListBox()\nend\n\nlocal DrawShader_Object = nil\nlocal DrawShader_Time = 0.0\nlocal DrawShader_Source =\n[[extern number time;\nvec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)\n{\n\tvec4 TexColor = Texel(texture, texture_coords);\n    return vec4((1.0+sin(time))/2.0, abs(cos(time)), abs(sin(time)), 1.0) * TexColor;\n}]]\nlocal DrawShader_Highlight =\n{\n\t['vec2'] = {0, 0, 1, 1},\n\t['vec3'] = {0, 0, 1, 1},\n\t['vec4'] = {0, 0, 1, 1},\n\t['mat4'] = {0, 0, 1, 1}\n}\n\nlocal function DrawShader()\n\tif DrawShader_Object == nil then\n\t\tDrawShader_Object = love.graphics.newShader(DrawShader_Source)\n\tend\n\n\tDrawShader_Time = DrawShader_Time + love.timer.getDelta()\n\n\tif DrawShader_Object ~= nil then\n\t\tDrawShader_Object:send(\"time\", DrawShader_Time)\n\tend\n\n\tSlab.Textf(\n\t\t\"Shader effects can be applied to any control through the PushShader/PopShader API calls. Any controls created after \" ..\n\t\t\"a PushShader call will have its effects applied. The next PopShader call will disable the current effect and apply \" ..\n\t\t\"the previous shader on the stack if one is present. The shader object to be pushed must be managed by the user and must be \" ..\n\t\t\"valid when Slab.Draw is called. Below is an example of a shader effect that changes the pixel color over time.\")\n\n\tSlab.NewLine()\n\n\tlocal W, H = Slab.GetWindowActiveSize()\n\tlocal Options =\n\t{\n\t\tText = DrawShader_Source,\n\t\tReturnOnText = false,\n\t\tMultiLine = true,\n\t\tW = W,\n\t\tH = 150,\n\t\tHighlight = DrawShader_Highlight\n\t}\n\tSlab.Input('DrawShader_Source', Options)\n\tif Slab.Button('Compile') then\n\t\tDrawShader_Source = Slab.GetInputText();\n\n\t\tif DrawShader_Object ~= nil then\n\t\t\tDrawShader_Object:release()\n\t\tend\n\n\t\tDrawShader_Object = love.graphics.newShader(DrawShader_Source)\n\tend\n\n\tSlab.NewLine()\n\n\tSlab.PushShader(DrawShader_Object)\n\tSlab.Image('DrawShader_Image', {Path = DrawImage_Path})\n\tSlab.Text(\"Text\")\n\tSlab.Button(\"Button\")\n\tSlab.PopShader()\nend\n\nlocal function DrawMessages()\n\tSlab.Textf(\n\t\t\"Slab has a messaging system that will gather any messages generated by the API and ensure these messages are only \" ..\n\t\t\"displayed a single time in the console. The messages may be generated if the developer is using a deprecated function \" ..\n\t\t\"or deprecated options for a control. The API offers a way to disable this system by passing 'NoMessages' to the args of \" ..\n\t\t\"Slab.Initialize. The API also offers a function to retrieve all gathered messages. Below will display all messages \" ..\n\t\t\"gathered since the start of this application.\")\n\n\tSlab.NewLine()\n\n\tlocal Messages = Slab.GetMessages()\n\tSlab.BeginLayout('DrawMessages_ListBox_Layout', {ExpandW = true, ExpandH = true})\n\tSlab.BeginListBox('DrawMessages_ListBox')\n\n\tfor I, V in ipairs(Messages) do\n\t\tSlab.BeginListBoxItem('DrawMessages_Item_' .. I)\n\t\tSlab.Text(V)\n\t\tSlab.EndListBoxItem()\n\tend\n\n\tSlab.EndListBox()\n\tSlab.EndLayout()\nend\n\nlocal SlabTest_Options = {Title = \"Slab\", AutoSizeWindow = false, W = 800.0, H = 600.0, IsOpen = true}\n\nfunction SlabTest.MainMenuBar()\n\tif Slab.BeginMainMenuBar() then\n\t\tif Slab.BeginMenu(\"File\") then\n\t\t\tif Slab.MenuItemChecked(\"Show Test Window\", SlabTest_Options.IsOpen) then\n\t\t\t\tSlabTest_Options.IsOpen = not SlabTest_Options.IsOpen\n\t\t\tend\n\n\t\t\tif Slab.MenuItem(\"Quit\", { Hint = \"alt+f4\" }) then\n\t\t\t\tlove.event.quit()\n\t\t\tend\n\n\t\t\tSlab.EndMenu()\n\t\tend\n\n\t\tSlabDebug.Menu()\n\n\t\tSlab.EndMainMenuBar()\n\tend\nend\n\nlocal Categories = {\n\t{\"Overview\", DrawOverview},\n\t{\"Window\", DrawWindow},\n\t{\"Buttons\", DrawButtons},\n\t{\"Text\", DrawText},\n\t{\"Check Box\", DrawCheckBox},\n\t{\"Radio Button\", DrawRadioButton},\n\t{\"Menus\", DrawMenus},\n\t{\"Combo Box\", DrawComboBox},\n\t{\"Input\", DrawInput},\n\t{\"Image\", DrawImage},\n\t{\"Cursor\", DrawCursor},\n\t{\"List Box\", DrawListBox},\n\t{\"Tree\", DrawTree},\n\t{\"Dialog\", DrawDialog},\n\t{\"Interaction\", DrawInteraction},\n\t{\"Shapes\", DrawShapes},\n\t{\"Tooltips\", DrawTooltip},\n\t{\"Stats\", DrawStats},\n\t{\"Layout\", DrawLayout},\n\t{\"Fonts\", DrawFonts},\n\t{\"Scroll\", DrawScroll},\n\t{\"Shaders\", DrawShader},\n\t{\"Messages\", DrawMessages}\n}\n\nlocal Selected = nil\n\nfunction SlabTest.Begin()\n\tlocal StatHandle = Slab.BeginStat('Slab Test', 'Slab Test')\n\n\tSlabTest.MainMenuBar()\n\n\tif Selected == nil then\n\t\tSelected = Categories[1]\n\tend\n\n\tSlab.BeginWindow('SlabTest', SlabTest_Options)\n\n\tlocal W, H = Slab.GetWindowActiveSize()\n\n\tif Slab.BeginComboBox('Categories', {Selected = Selected[1], W = W}) then\n\t\tfor I, V in ipairs(Categories) do\n\t\t\tif Slab.TextSelectable(V[1]) then\n\t\t\t\tSelected = Categories[I]\n\t\t\tend\n\t\tend\n\n\t\tSlab.EndComboBox()\n\tend\n\n\tSlab.Separator()\n\n\tif Selected ~= nil and Selected[2] ~= nil then\n\t\tSelected[2]()\n\tend\n\n\tSlab.EndWindow()\n\n\tSlabDebug.Begin()\n\n\tSlab.EndStat(StatHandle)\nend\n\nreturn SlabTest\n"
  },
  {
    "path": "Style.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Config = require(SLAB_PATH .. '.Internal.Core.Config')\nlocal Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')\nlocal FileSystem = require(SLAB_PATH .. '.Internal.Core.FileSystem')\nlocal Utility = require(SLAB_PATH .. '.Internal.Core.Utility')\n\nlocal API = {}\nlocal Styles = {}\nlocal StylePaths = {}\nlocal DefaultStyles = {}\nlocal CurrentStyle = \"\"\nlocal FontStack = {}\n\nlocal Style =\n{\n\tFont = nil,\n\tFontSize = 14,\n\tMenuColor = {0.2, 0.2, 0.2, 1.0},\n\tScrollBarColor = {0.4, 0.4, 0.4, 1.0},\n\tScrollBarHoveredColor = {0.8, 0.8, 0.8, 1.0},\n\tSeparatorColor = {0.5, 0.5, 0.5, 0.7},\n\tWindowBackgroundColor = {0.2, 0.2, 0.2, 1.0},\n\tWindowTitleFocusedColor = {0.26, 0.53, 0.96, 1.0},\n\tWindowCloseBgColor = {0.64, 0.64, 0.64, 1.0},\n\tWindowCloseColor = {0.0, 0.0, 0.0, 1.0},\n\tButtonColor = {0.55, 0.55, 0.55, 1.0},\n\tRadioButtonSelectedColor = {0.2, 0.2, 0.2, 1.0},\n\tButtonHoveredColor = {0.7, 0.7, 0.7, 1.0},\n\tButtonPressedColor = {0.8, 0.8, 0.8, 1.0},\n\tButtonDisabledTextColor = {0.35, 0.35, 0.35, 1.0},\n\tCheckBoxSelectedColor = {0.0, 0.0, 0.0, 1.0},\n\tCheckBoxDisabledColor = {0.35, 0.35, 0.35, 1.0},\n\tTextColor = {0.875, 0.875, 0.875, 1.0},\n\tTextDisabledColor = {0.45, 0.45, 0.45, 1.0},\n\tTextHoverBgColor = {0.5, 0.5, 0.5, 1.0},\n\tTextURLColor = {0.2, 0.2, 1.0, 1.0},\n\tComboBoxColor = {0.4, 0.4, 0.4, 1.0},\n\tComboBoxHoveredColor = {0.55, 0.55, 0.55, 1.0},\n\tComboBoxDropDownColor = {0.4, 0.4, 0.4, 1.0},\n\tComboBoxDropDownHoveredColor = {0.55, 0.55, 0.55, 1.0},\n\tComboBoxArrowColor = {1.0, 1.0, 1.0, 1.0},\n\tInputBgColor = {0.4, 0.4, 0.4, 1.0},\n\tInputEditBgColor = {0.6, 0.6, 0.6, 1.0},\n\tInputSelectColor = {0.14, 0.29, 0.53, 0.4},\n\tInputSliderColor = {0.1, 0.1, 0.1, 1.0},\n\tMultilineTextColor = {0.0, 0.0, 0.0, 1.0},\n\tListBoxBgColor = {0.0, 0.0, 0.0, 0.0},\n\n\tWindowRounding = 2.0,\n\tWindowBorder = 4.0,\n\tWindowTitleH = 0.0,\n\tButtonRounding = 2.0,\n\tCheckBoxRounding = 2.0,\n\tComboBoxRounding = 2.0,\n\tInputBgRounding = 2.0,\n\tScrollBarRounding = 2.0,\n\tIndent = 14.0,\n\tMenuPadH = 0.0,\n\tMenuItemPadH = 0.0,\n\n\tAPI = API\n}\n\nfunction API.Initialize()\n\tlocal StylePath = \"/Internal/Resources/Styles/\"\n\tlocal Path = SLAB_FILE_PATH .. StylePath\n\t-- Use love's filesystem functions to support both packaged and unpackaged builds\n\tlocal Items = love.filesystem.getDirectoryItems(Path)\n\n\tlocal StyleName = nil\n\tfor I, V in ipairs(Items) do\n\t\tif string.find(V, Path, 1, true) == nil then\n\t\t\tV = Path .. V\n\t\tend\n\n\t\tlocal LoadedStyle = API.LoadStyle(V, false, true)\n\n\t\tif LoadedStyle ~= nil then\n\t\t\tlocal Name = FileSystem.GetBaseName(V, true)\n\n\t\t\tif StyleName == nil then\n\t\t\t\tStyleName = Name\n\t\t\tend\n\t\tend\n\tend\n\n\tif not API.SetStyle(\"Dark\") then\n\t\tAPI.SetStyle(StyleName)\n\tend\n\n\tStyle.Font = love.graphics.newFont(Style.FontSize)\n\tAPI.PushFont(Style.Font)\n\tCursor.SetNewLineSize(Style.Font:getHeight())\nend\n\nfunction API.LoadStyle(Path, Set, IsDefault)\n\tlocal Contents, Error = Config.LoadFile(Path, IsDefault)\n\tif Contents ~= nil then\n\t\tlocal Name = FileSystem.GetBaseName(Path, true)\n\t\tStyles[Name] = Contents\n\t\tStylePaths[Name] = Path\n\t\tif IsDefault then\n\t\t\ttable.insert(DefaultStyles, Name)\n\t\tend\n\n\t\tif Set then\n\t\t\tAPI.SetStyle(Name)\n\t\tend\n\telse\n\t\tprint(\"Failed to load style '\" .. Path .. \"'.\\n\" .. Error)\n\tend\n\treturn Contents\nend\n\nfunction API.SetStyle(Name)\n\tif Name == nil then\n\t\treturn false\n\tend\n\n\tlocal Other = Styles[Name]\n\tif Other ~= nil then\n\t\tCurrentStyle = Name\n\t\tfor K, V in pairs(Style) do\n\t\t\tlocal New = Other[K]\n\t\t\tif New ~= nil then\n\t\t\t\tif type(V) == \"table\" then\n\t\t\t\t\tUtility.CopyValues(Style[K], New)\n\t\t\t\telse\n\t\t\t\t\tStyle[K] = New\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn true\n\telse\n\t\tprint(\"Style '\" .. Name .. \"' is not loaded.\")\n\tend\n\n\treturn false\nend\n\nfunction API.GetStyleNames()\n\tlocal Result = {}\n\n\tfor K, V in pairs(Styles) do\n\t\ttable.insert(Result, K)\n\tend\n\n\treturn Result\nend\n\nfunction API.GetCurrentStyleName()\n\treturn CurrentStyle\nend\n\nfunction API.CopyCurrentStyle(Path)\n\tlocal NewStyle = Utility.Copy(Styles[CurrentStyle])\n\tlocal Result, Error = Config.Save(Path, NewStyle)\n\n\tif Result then\n\t\tlocal NewStyleName = FileSystem.GetBaseName(Path, true)\n\t\tStyles[NewStyleName] = NewStyle\n\t\tStylePaths[NewStyleName] = Path\n\t\tAPI.SetStyle(NewStyleName)\n\telse\n\t\tprint(\"Failed to create new style at path '\" .. Path \"'. \" .. Error)\n\tend\nend\n\nfunction API.SaveCurrentStyle()\n\tAPI.StoreCurrentStyle()\n\tlocal Path = StylePaths[CurrentStyle]\n\tlocal Settings = Styles[CurrentStyle]\n\tlocal Result, Error = Config.Save(Path, Settings)\n\tif not Result then\n\t\tprint(\"Failed to save style '\" .. CurrentStyle .. \"'. \" .. Error)\n\tend\nend\n\nfunction API.StoreCurrentStyle()\n\tUtility.CopyValues(Styles[CurrentStyle], Style)\nend\n\nfunction API.IsDefaultStyle(Name)\n\treturn Utility.Contains(DefaultStyles, Name)\nend\n\nfunction API.PushFont(Font)\n\tif Font ~= nil then\n\t\tStyle.Font = Font\n\t\ttable.insert(FontStack, 1, Font)\n\tend\nend\n\nfunction API.PopFont()\n\tif #FontStack > 1 then\n\t\ttable.remove(FontStack, 1)\n\t\tStyle.Font = FontStack[1]\n\tend\nend\n\nreturn Style\n"
  },
  {
    "path": "changelog.txt",
    "content": "v0.9.0\n==========\n[Stats] Added GetStats and CalculateStats. See fixed #91 (@flamendless)\n[Window]: Added minimize/maximize button in title bar to hide/show contents #100 (@flamendless)\n[Slab Logo]: Added Slab logo. Credits to ig@haven.graphicdesigns\n[Window]: Allow horizontal scrolling when there is no vertical scrolling possible #87 (@flamendless)\n[Dock]: Dock now follows Window's option for W and H #92 (@flamendless)\n[Dock]: Allow to programmatically set a window to a dock #92 (@flamendless)\n[Input]: Added NeedDrag option to InputNumberSlider\n[Button]: Added VLines option for multiline button label #81 (@flamendless)\n[Layout]: Added GetCurrentColumnIndex #89 (@flamendless)\n[Properties]: Improved Properties to use ordered table #93 (@flamendless)\n[Mouse]: Read mouse arguments out of Args table #94 (@idbrii)\n\nBUGFIXES\n==========\n[Dialog]: MessageBox expands if there are multiple buttons #99 (@flamendless)\n[Image]: Cache not updating correctly when Image object is passed #98 (@flamendless)\n[ContextMenu]: Context Menu now works with void spaces again #96 (@flamendless)\n[Tree]: Not returning IsOpen flag correctly #90 (@flamendless)\n\nv0.8.0\n==========\n[API]: Make Update and Draw functions private.\n[Android]: Receive feedback and make fixes to errant behavior. Applies to issue #35.\n[Frame]: API calls to setup frames (child regions in Dear ImGui). Can hook into Regions module and expose some functionality from this for this feature. #46.\n[LayoutManager]: Right aligned controls are flush on the edge. Should flush with the window border.\n[Mouse/Keyboard]: Allow multiple 'Update' calls. This will conflict with the async input processing. Might need to keep a cache of the last async update and always apply that to every 'Update' call.\n\nBUGS\n==========\n[Combo Box]: Selectable text not expanding to full width of window.\n[File Dialog]: Excess right border.\n[List Box]: View transform pops while parent window is resizing when scroll is in non-zero position.\n[List Box]: Region size not reset when list items change.\n[List Box]: Window not properly auto-sized.\n[Scroll Bar]: Color flicker when clicking and releasing on scroll bar.\n[Tooltip]: Gains focus when displayed. Focus should not be allowed.\n[Tree]: View transform not correct with content size. Content size may be incorrect.\n[Window]: Clamp window moving to viewport.\n\nTODO\n==========\n[Animation]: Initial animation support.\n[API]: Re-organize functions in categories and update comment.\n[API]: Get/Set cursor padding.\n[API]: Utilitze 'args' parameter for configuring docks, save state.\n[Column]: Investigate moving Column rendering to its own draw channel within an active batch to reduce scissor calls.\n[Column]: Move separator with mouse.\nCombo Box: Easy API with table of possible values.\nContext Menu: Investigate checking hot item and last item of window.\nControls: Convert all window bounds check to use region.\n[Date Time]: Initial implementation for a date/time control.\nDialog: Function to close all dialog boxes.\nDialog: Modal/Non-modal dialogs.\n[Dock]: Flickers when using a custom love.run function with a fixed update interval.\n[Documentation]: Command-line option to just dump all functions listed in API table.\n[Error]: Dialog that shows any error messages.\n[Graph]: Initial line graph implementation.\n[Input]: Drag select word versus character.\n[Input]: Auto-grow control with MulitLine.\n[Input]: Range highlight using start and end tokens.\n[Keyboard]: Option to specify key as scan code.\nLicense: Show license for Kenney.nl assets.\nMain Menu Bar: Assert if called within a window context.\n[Menu]: Radio button like menu items.\nRegion: Clamp position check with owning window's size.\n[Scroll Bar]: Velocity based movement for mouse wheel scrolling.\n[Scroll Bar]: Focus owning window if scroll bar is clicked.\n[Slab]: Move all love.* calls into interfaces for portability with other Lua based frameworks.\nSlabDebug: Show image stats.\n[SlabDebug]: Window management tools.\n[Stats]: Draw time draw command.\n[Stats]: Simple API access with list of stat names to display.\nStyle: Add documentation for each style property.\n[Style]: API documentation.\n[Tab]: Initial implementation of a container holding Window instances with tabs for each.\n[Table]: Initial implementation.\n[Text]: Tooltip support.\n[Text]: Stats for text objects.\n[Text]: Deprecate SelectOnHover option.\n[Tree]: Fix for BeginContextMenuItem for root and children items.\nWindow: Menu support.\nWindow: Ids should be used as keys.\nWindow: Deprecate window bounds values and use region directly.\n[Window]: Move default border size to Style.\n[Window]: Allow arrow key movements of window if option is set.\n[Window]: IsWindowOpen API function.\nWidgets: Track owning window's alpha channel.\nWidgets: Forward Options parameter to sub widgets.\n"
  },
  {
    "path": "conf.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nfunction love.conf(t)\n\tt.window.title = \"Slab\"\n\tt.window.width = 1280\n\tt.window.height = 720\n\tt.console = true\nend"
  },
  {
    "path": "init.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\n-- Global path used in all modules in this library\nSLAB_PATH = ...\n\n---@type Slab\nlocal Slab = require(SLAB_PATH .. '.API')\n\n---@type Slab\nreturn Slab\n"
  },
  {
    "path": "main.lua",
    "content": "--[[\n\nMIT License\n\nCopyright (c) 2019-2021 Love2D Community <love2d.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n--]]\n\nlocal Slab = require 'Slab'\nlocal SlabTest = require 'SlabTest'\n\nlocal dontInterceptEventHandlers = true;\n\nfunction love.load(args)\n\tlove.graphics.setBackgroundColor(0.07, 0.07, 0.07)\n\tSlab.Initialize(args, dontInterceptEventHandlers)\n\tif dontInterceptEventHandlers then setCustomHandlers() end\nend\n\nfunction love.update(dt)\n\tSlab.Update(dt)\n\tSlabTest.Begin()\nend\n\nfunction love.draw()\n\tSlab.Draw()\nend\n\nfunction _quit()\n\tSlab.OnQuit()\nend\n\nfunction _keypressed(key, scancode, isrepeat)\n\tSlab.OnKeyPressed(key, scancode, isrepeat)\nend\n\nfunction _keyreleased(key, scancode)\n\tSlab.OnKeyReleased(key, scancode)\nend\n\nfunction _textinput(text)\n\tSlab.OnTextInput(text)\nend\n\nfunction _wheelmoved(x, y)\n\tSlab.OnWheelMoved(x, y)\nend\n\nfunction _mousemoved(x, y, dx, dy, istouch)\n\tSlab.OnMouseMoved(x, y, dx, dy, istouch)\nend\n\nfunction _mousepressed( x, y, button, istouch, presses)\n\tSlab.OnMousePressed( x, y, button, istouch, presses)\nend\n\nfunction _mousereleased( x, y, button, istouch, presses)\n\tSlab.OnMouseReleased( x, y, button, istouch, presses)\nend\n\nfunction setCustomHandlers()\n\tlove.handlers['quit'] = _quit;\n\tlove.handlers['keypressed'] = _keypressed;\n\tlove.handlers['keyreleased'] = _keyreleased;\n\tlove.handlers['textinput'] = _textinput;\n\tlove.handlers['mousemoved'] = _mousemoved;\n\tlove.handlers['mousepressed'] = _mousepressed;\n\tlove.handlers['mousereleased'] = _mousereleased;\n\tlove.handlers['wheelmoved'] = _wheelmoved;\nend\n"
  }
]